@@ -250,6 +250,19 @@ class PhraseMaker {
250250 this . notesBlockMap = [ ] ;
251251 this . _blockMapHelper = [ ] ;
252252 this . columnBlocksMap = [ ] ;
253+
254+ /**
255+ * Stores lyrics for each column.
256+ * @type {Array<string> }
257+ * @private
258+ */
259+ this . _lyrics = [ ] ;
260+
261+ /**
262+ * Marks the presence of print block
263+ * @type {boolean }
264+ */
265+ this . lyricsON = false ;
253266 }
254267
255268 /**
@@ -280,7 +293,7 @@ class PhraseMaker {
280293 * @type {NodeListOf<Element> }
281294 */
282295 var windowFrameElements = floatingWindowsDiv . querySelectorAll ( ".windowFrame" ) ;
283-
296+
284297 for ( var i = 0 ; i < windowFrameElements . length ; i ++ ) {
285298
286299 /**
@@ -324,7 +337,7 @@ class PhraseMaker {
324337 * @type {number }
325338 */
326339 var maxHeight = screenHeight * 0.8 ;
327-
340+
328341 if ( totalWidth > screenWidth || totalHeight > screenHeight ) {
329342 windowFrame . style . height = Math . min ( totalHeight , maxHeight ) + "px" ;
330343 windowFrame . style . width = Math . min ( totalWidth , maxWidth ) + "px" ;
@@ -343,7 +356,7 @@ class PhraseMaker {
343356 }
344357 }
345358 }
346-
359+
347360 /**
348361 * Clears block references within the PhraseMaker.
349362 * Resets arrays used to track row and column blocks.
@@ -503,7 +516,6 @@ class PhraseMaker {
503516 this . _stopOrCloseClicked = true ;
504517 this . activity . hideMsgs ( ) ;
505518 docById ( "wheelDivptm" ) . style . display = "none" ;
506-
507519 widgetWindow . destroy ( ) ;
508520 } ;
509521
@@ -625,7 +637,8 @@ class PhraseMaker {
625637 drumName = getDrumName ( this . rowLabels [ i ] ) ;
626638
627639 // Depending on the row, we choose a different background color.
628- if (
640+ if ( this . rowLabels [ i ] === "print" ) break ;
641+ else if (
629642 MATRIXGRAPHICS . indexOf ( this . rowLabels [ i ] ) != - 1 ||
630643 MATRIXGRAPHICS2 . indexOf ( this . rowLabels [ i ] ) != - 1
631644 ) {
@@ -652,7 +665,8 @@ class PhraseMaker {
652665 cell . innerHTML = "" ;
653666 this . _headcols [ i ] = cell ;
654667
655- if ( drumName != null ) {
668+ if ( this . rowLabels [ i ] === "print" ) break ;
669+ else if ( drumName != null ) {
656670 cell . innerHTML =
657671 ' <img src="' +
658672 getDrumIcon ( drumName ) +
@@ -757,7 +771,8 @@ class PhraseMaker {
757771 cell . setAttribute ( "alt" , i ) ;
758772 this . _labelcols [ i ] = cell ;
759773
760- if ( drumName != null ) {
774+ if ( this . rowLabels [ i ] === "print" ) break ;
775+ else if ( drumName != null ) {
761776 cell . innerHTML = _ ( drumName ) ;
762777 cell . style . fontSize = Math . floor ( this . _cellScale * 14 ) + "px" ;
763778 cell . setAttribute ( "alt" , i + "__" + "drumblocks" ) ;
@@ -913,6 +928,82 @@ class PhraseMaker {
913928 j += 1 ;
914929 }
915930
931+ // Add a row for lyrics if they are enabled in the ExtraBlocks.js
932+ if ( this . lyricsON ) {
933+
934+ const lyricsRow = ptmTable . insertRow ( ) ;
935+ lyricsRow . setAttribute ( "id" , "lyricRow" ) ;
936+ lyricsRow . style . position = "sticky" ;
937+
938+ // Label Cell (Fixed like "note value")
939+ cell = lyricsRow . insertCell ( ) ;
940+ cell . setAttribute ( "colspan" , "2" ) ;
941+ cell . className = "headcol" ;
942+ cell . style . position = "sticky" ;
943+ cell . style . left = "1.2px" ;
944+ cell . style . zIndex = "1" ;
945+ cell . style . backgroundColor = platformColor . lyricsLabelBackground || "#FF2B77" ;
946+ cell . style . textAlign = "center" ;
947+ cell . innerHTML = "Lyrics" ;
948+
949+ // Nested Table for Input Fields
950+ tempTable = document . createElement ( "table" ) ;
951+ tempTable . setAttribute ( "cellpadding" , "0px" ) ;
952+ const inputRow = tempTable . insertRow ( ) ;
953+
954+ // Add input cells for lyrics
955+ if ( ! this . _lyrics || this . _lyrics . length === 0 ) {
956+ this . _lyrics = Array ( this . activity . logo . tupletRhythms . length ) . fill ( "" ) ;
957+ } else if ( this . activity . logo . tupletRhythms . length > this . _lyrics . length ) {
958+ const additionalLength = this . activity . logo . tupletRhythms . length - this . _lyrics . length ;
959+ this . _lyrics = this . _lyrics . concat ( Array ( additionalLength ) . fill ( "" ) ) ;
960+ }
961+ for ( let i = 0 ; i < this . activity . logo . tupletRhythms . length ; i ++ ) {
962+ const noteValue = this . activity . logo . tupletRhythms [ i ] [ 2 ] ;
963+ const inputCell = inputRow . insertCell ( ) ;
964+ inputCell . style . height = Math . floor ( MATRIXSOLFEHEIGHT * this . _cellScale ) + 1 + "px" ;
965+ inputCell . style . width = this . _noteWidth ( noteValue ) + "px" ;
966+ inputCell . style . minWidth = inputCell . style . width ;
967+ inputCell . style . maxWidth = inputCell . style . width ;
968+ inputCell . style . backgroundColor = "#FF6EA1" ;
969+ inputCell . style . fontFamily = "sans-serif" ;
970+ inputCell . style . cursor = "default" ;
971+ inputCell . style . borderSpacing = "1px 1px" ;
972+ inputCell . style . borderCollapse = "collapse" ;
973+ inputCell . style . boxSizing = "border-box" ;
974+ inputCell . style . padding = "0" ;
975+ inputCell . style . borderRadius = "6px" ;
976+ inputCell . style . border = "none" ;
977+ inputCell . setAttribute ( "alt" , i + "__" + "graphicsblocks2" ) ;
978+
979+ const lyricsInput = document . createElement ( "input" ) ;
980+ lyricsInput . type = "text" ;
981+ lyricsInput . value = this . _lyrics [ i ] ;
982+
983+ lyricsInput . style . height = inputCell . style . height ;
984+ lyricsInput . style . width = "100%" ;
985+ lyricsInput . style . minWidth = inputCell . style . minWidth ;
986+ lyricsInput . style . maxWidth = inputCell . style . maxWidth ;
987+ lyricsInput . style . fontSize = "inherit" ;
988+ lyricsInput . style . fontFamily = "sans-serif" ;
989+ lyricsInput . style . cursor = "default" ;
990+ lyricsInput . style . boxSizing = "border-box" ;
991+ lyricsInput . style . padding = "0" ;
992+ lyricsInput . style . border = "none" ;
993+ lyricsInput . style . borderRadius = "6px" ;
994+ lyricsInput . style . backgroundColor = "#FF6EA1" ;
995+
996+ inputCell . appendChild ( lyricsInput ) ;
997+ lyricsInput . addEventListener ( "focus" , ( ) => this . activity . isInputON = true ) ;
998+ lyricsInput . addEventListener ( "blur" , ( ) => this . activity . isInputON = false ) ;
999+ lyricsInput . addEventListener ( "input" , ( event ) => {
1000+ this . _lyrics [ i ] = event . target . value ;
1001+ } ) ;
1002+
1003+ } ;
1004+ lyricsRow . insertCell ( ) . appendChild ( tempTable ) ;
1005+ }
1006+
9161007 // An extra row for the note and tuplet values
9171008 ptmTableRow = ptmTable . insertRow ( ) ;
9181009 ptmCell = ptmTableRow . insertCell ( ) ;
@@ -953,6 +1044,7 @@ class PhraseMaker {
9531044 tempTable = document . createElement ( "table" ) ;
9541045 tempTable . setAttribute ( "cellpadding" , "0px" ) ;
9551046 this . _tupletNoteValueRow = tempTable . insertRow ( ) ;
1047+ const tempTable2 = tempTable ;
9561048 this . _tupletValueRow = tempTable . insertRow ( ) ;
9571049 this . _noteValueRow = tempTable . insertRow ( ) ;
9581050 ptmTableRow . insertCell ( ) . append ( tempTable ) ;
@@ -3464,7 +3556,7 @@ class PhraseMaker {
34643556 this . _restartGrid . call ( this ) ;
34653557 }
34663558
3467-
3559+
34683560 /**
34693561 * Deletes a note from the grid and readjusts the notes accordingly.
34703562 * @param {number } noteToDivide - The index of the note to delete.
@@ -4507,6 +4599,10 @@ class PhraseMaker {
45074599 * @param {number } noteCounter - The current note index in the playback sequence.
45084600 */
45094601 __playNote ( time , noteCounter ) {
4602+ // Show lyrics while playing notes.
4603+ if ( this . lyricsON ) {
4604+ this . activity . textMsg ( this . _lyrics [ noteCounter ] ) ;
4605+ }
45104606 // If the widget is closed, stop playing.
45114607 if ( ! this . widgetWindow . isVisible ( ) ) {
45124608 return ;
@@ -4879,7 +4975,16 @@ class PhraseMaker {
48794975 * @private
48804976 */
48814977 _clear ( ) {
4882- // 'Unclick' every entry in the matrix.
4978+ // Reset the `_lyrics` array
4979+ this . _lyrics = Array ( this . _lyrics . length ) . fill ( "" ) ;
4980+ const lyricsRow = document . getElementById ( "lyricRow" ) ;
4981+ if ( lyricsRow ) {
4982+ const inputFields = lyricsRow . querySelectorAll ( "input[type='text']" ) ;
4983+ inputFields . forEach ( ( inputField , index ) => {
4984+ inputField . value = this . _lyrics [ index ] || "" ;
4985+ } ) ;
4986+ }
4987+ // 'Unclick' every entry in the matrix
48834988 let row , cell ;
48844989 for ( let i = 0 ; i < this . rowLabels . length ; i ++ ) {
48854990 row = this . _rows [ i ] ;
@@ -4894,6 +4999,7 @@ class PhraseMaker {
48944999 }
48955000 }
48965001
5002+
48975003 /**
48985004 * Saves the current matrix state as an action stack consisting of note and pitch blocks.
48995005 * @private
@@ -5036,7 +5142,7 @@ class PhraseMaker {
50365142 if ( obj === null ) {
50375143 // add a hertz block
50385144 // The last connection in last pitch block is null.
5039- if ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) {
5145+ if ( ! this . lyricsON && ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) ) {
50405146 lastConnection = null ;
50415147 } else {
50425148 lastConnection = thisBlock + 2 ;
@@ -5061,7 +5167,7 @@ class PhraseMaker {
50615167 } else if ( drumName != null ) {
50625168 // add a playdrum block
50635169 // The last connection in last pitch block is null.
5064- if ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) {
5170+ if ( ! this . lyricsON && ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) ) {
50655171 lastConnection = null ;
50665172 } else {
50675173 lastConnection = thisBlock + 2 ;
@@ -5086,7 +5192,7 @@ class PhraseMaker {
50865192 } else if ( note [ 0 ] [ j ] . slice ( 0 , 4 ) === "http" ) {
50875193 // add a playdrum block with URL
50885194 // The last connection in last pitch block is null.
5089- if ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) {
5195+ if ( ! this . lyricsON && ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) ) {
50905196 lastConnection = null ;
50915197 } else {
50925198 lastConnection = thisBlock + 2 ;
@@ -5111,7 +5217,7 @@ class PhraseMaker {
51115217 } else if ( obj . length > 2 ) {
51125218 // add a 2-arg graphics block
51135219 // The last connection in last pitch block is null.
5114- if ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) {
5220+ if ( ! this . lyricsON && ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) ) {
51155221 lastConnection = null ;
51165222 } else {
51175223 lastConnection = thisBlock + 3 ;
@@ -5143,7 +5249,7 @@ class PhraseMaker {
51435249 } else if ( obj . length > 1 ) {
51445250 // add a 1-arg graphics block
51455251 // The last connection in last pitch block is null.
5146- if ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) {
5252+ if ( ! this . lyricsON && ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) ) {
51475253 lastConnection = null ;
51485254 } else {
51495255 lastConnection = thisBlock + 2 ;
@@ -5168,7 +5274,7 @@ class PhraseMaker {
51685274 } else {
51695275 // add a pitch block
51705276 // The last connection in last pitch block is null.
5171- if ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) {
5277+ if ( ! this . lyricsON && ( note [ 0 ] . length === 1 || j === note [ 0 ] . length - 1 ) ) {
51725278 lastConnection = null ;
51735279 } else {
51745280 lastConnection = thisBlock + 3 ;
@@ -5348,6 +5454,34 @@ class PhraseMaker {
53485454 }
53495455 }
53505456 }
5457+ if ( this . lyricsON ) {
5458+ newStack . push ( [
5459+ thisBlock ,
5460+ "print" ,
5461+ 0 ,
5462+ 0 ,
5463+ [ previousBlock , thisBlock + 1 , null ]
5464+ ] ) ;
5465+ previousBlock = thisBlock ;
5466+ thisBlock += 1 ;
5467+ if ( this . _lyrics [ i ] && this . _lyrics !== "" ) {
5468+ newStack . push ( [
5469+ thisBlock ,
5470+ [ "text" , { value : this . _lyrics [ i ] } ] ,
5471+ 0 ,
5472+ 0 ,
5473+ [ previousBlock ]
5474+ ] ) ;
5475+ } else {
5476+ newStack . push ( [
5477+ thisBlock ,
5478+ [ "text" , { value : "..." } ] ,
5479+ 0 ,
5480+ 0 ,
5481+ [ previousBlock ]
5482+ ] ) ;
5483+ }
5484+ }
53515485 }
53525486 }
53535487
0 commit comments