<commit_before>// Copyright (c) 2015 <NAME>
// Copyright (c) 2015-18 <NAME>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the The GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// You should have received a copy of the GNU Affero General Public
// License along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA

const MATRIXGRAPHICS = ['forward', 'back', 'right', 'left', 'setcolor', 'setshade', 'sethue', 'setgrey', 'settranslucency', 'setpensize', 'setheading'];
const MATRIXGRAPHICS2 = ['arc', 'setxy'];
// Deprecated
const MATRIXSYNTHS = ['sine', 'triangle', 'sawtooth', 'square', 'hertz'];


function PitchTimeMatrix () {
    const BUTTONDIVWIDTH = 476;  // 8 buttons 476 = (55 + 4) * 8
    const OUTERWINDOWWIDTH = 728;  // 675;
    const INNERWINDOWWIDTH = 600;
    const BUTTONSIZE = 53;
    const ICONSIZE = 32;

    this.paramsEffects = {
        "doVibrato": false,
        "doDistortion": false,
        "doTremolo": false,
        "doPhaser": false,
        "doChorus": false,
        "vibratoIntensity": 0,
        "vibratoFrequency": 0,
        "distortionAmount": 0,
        "tremoloFrequency": 0,
        "tremoloDepth": 0,
        "rate": 0,
        "octaves": 0,
        "baseFrequency": 0,
        "chorusRate": 0,
        "delayTime": 0,
        "chorusDepth": 0
    };

    // rowLabels can contain either a pitch, a drum, or a grphics command
    this.rowLabels = [];
    // rowArgs can contain an octave or the arg(s) to a graphics command
    this.rowArgs = [];

    this._sorted = false;
    this._notesToPlay = [];
    this._matrixHasTuplets = false;
    this._notesCounter = 0;
    this._noteStored = [];

    // The pitch-block number associated with a row; a rhythm block is
    // associated with a column. We need to keep track of which
    // intersections in the grid are populated.  The blockMap is a
    // list of selected nodes in the matrix that map pitch blocks to
    // rhythm blocks (note that rhythm blocks can span multiple
    // columns).

    // These arrays get created each time the matrix is built.
    this._rowBlocks = [];  // pitch-block number
    this._colBlocks = [];  // [rhythm-block number, note number]

    // This array keeps track of the position of the rows after sorting.
    this._rowMap = [];
    // And offsets due to deleting duplicates.
    this._rowOffset = [];

    // This array is preserved between sessions.
    // We populate the blockMap whenever a note is selected and
    // restore any notes that might be present.

    this._blockMap = [];

    this.clearBlocks = function() {
        this._rowBlocks = [];
        this._colBlocks = [];
        this._rowMap = [];
        this._rowOffset = [];
    };

    this.addRowBlock = function(rowBlock) {
        this._rowMap.push(this._rowBlocks.length);
        this._rowOffset.push(0);
        this._rowBlocks.push(rowBlock);
    };

    this.addColBlock = function(rhythmBlock, n) {
        // Search for previous instance of the same block (from a repeat)
        var startIdx = 0;
        for (var i = 0; i < this._colBlocks.length; i++) {
            var obj = this._colBlocks[i];
            if (obj[0] === rhythmBlock) {
                startIdx += 1;
            }
        }

        for (var i = startIdx; i < n + startIdx; i++) {
            this._colBlocks.push([rhythmBlock, i]);
        }
    };

    this.addNode = function(pitchBlock, rhythmBlock, n) {
        for (var i = 0; i < this._blockMap.length; i++) {
            var obj = this._blockMap[i];
            if (obj[0] === pitchBlock && obj[1][0] === rhythmBlock && obj[1][1] === n) {
                return;  // node is already in the list
            }
        }
        this._blockMap.push([pitchBlock, [rhythmBlock, n]]);
    };

    this.removeNode = function(pitchBlock, rhythmBlock, n) {
        for (var i = 0; i < this._blockMap.length; i++) {
            var obj = this._blockMap[i];
            if (obj[0] === pitchBlock && obj[1][0] === rhythmBlock && obj[1][1] === n) {
                this._blockMap[i] = [-1, [-1, 1, 0]];  // Mark as removed
            }
        }
    };

    this.init = function(logo) {
        // Initializes the matrix. First removes the previous matrix
        // and them make another one in DOM (document object model)
        this._noteStored = [];
        this._sorted = false;
        this._rests = 0;
        this._logo = logo;

        this.playingNow = false;	

        var w = window.innerWidth;
        this._cellScale = w / 1200;
        var iconSize = ICONSIZE * this._cellScale;

        var canvas = docById('myCanvas');

        // Position the widget and make it visible.
        var ptmDiv = docById('ptmDiv');
        ptmDiv.style.visibility = 'visible';
        ptmDiv.setAttribute('draggable', 'true');
        ptmDiv.style.left = '200px';
        ptmDiv.style.top = '150px';

        // The ptm buttons
        var ptmButtonsDiv = docById('ptmButtonsDiv');
        ptmButtonsDiv.style.display = 'inline';
        ptmButtonsDiv.style.visibility = 'visible';
        ptmButtonsDiv.style.width = BUTTONDIVWIDTH;
        ptmButtonsDiv.innerHTML = '<table cellpadding="0px" id="ptmButtonTable"></table>';

        var buttonTable = docById('ptmButtonTable');
        var header = buttonTable.createTHead();
        var row = header.insertRow(0);

        // For the button callbacks
        var that = this;

        console.log('notes ' + this.rowLabels + ' octave ' + this.rowArgs);

        this._notesToPlay = [];
        this._matrixHasTuplets = false;

        // Add the buttons to the top row.
        var that = this;

        var cell = this._addButton(row, 'play-button.svg', ICONSIZE, _('play'));
        cell.onclick=function() {
            that._logo.setTurtleDelay(0);

            that._logo.resetSynth(0);
            that.playAll();
        }

        var cell = this._addButton(row, 'export-chunk.svg', ICONSIZE, _('save'));
        cell.onclick=function() {
            that._save();
        }

        var cell = this._addButton(row, 'erase-button.svg', ICONSIZE, _('clear'));
        cell.onclick=function() {
            that._clear();
        }

        var cell = this._addButton(row, 'export-button.svg', ICONSIZE, _('export'));
        cell.onclick=function() {
            that._export();
        }

        var cell = this._addButton(row, 'sort.svg', ICONSIZE, _('sort'));
        cell.onclick=function() {
            that._sort();
        }

        var cell = this._addButton(row, 'close-button.svg', ICONSIZE, _('close'));
        cell.onclick=function() {
            that._rowOffset = [];
            for (var i = 0; i < that._rowMap.length; i++) {
                that._rowMap[i] = i;
            }
            that._logo.synth.stopSound(0, 'default');
            that._logo.synth.stop();

            ptmTableDiv.style.visibility = 'hidden';
            ptmButtonsDiv.style.visibility = 'hidden';
            ptmDiv.style.visibility = 'hidden';
        }

        // We use this cell as a handle for dragging.
        var dragCell = this._addButton(row, 'grab.svg', ICONSIZE, _('drag'));
        dragCell.style.cursor = 'move';

        this._dx = dragCell.getBoundingClientRect().left - ptmDiv.getBoundingClientRect().left;
        this._dy = dragCell.getBoundingClientRect().top - ptmDiv.getBoundingClientRect().top;
        this._dragging = false;
        this._target = false;
        this._dragCellHTML = dragCell.innerHTML;

        dragCell.onmouseover = function(e) {
            // In order to prevent the dragged item from triggering a
            // browser reload in Firefox, we empty the cell contents
            // before dragging.
            dragCell.innerHTML = '';
        };

        dragCell.onmouseout = function(e) {
            if (!that._dragging) {
                dragCell.innerHTML = that._dragCellHTML;
            }
        };

        canvas.ondragover = function(e) {
            e.preventDefault();
        };

        canvas.ondrop = function(e) {
            if (that._dragging) {
                that._dragging = false;
                var x = e.clientX - that._dx;
                ptmDiv.style.left = x + 'px';
                var y = e.clientY - that._dy;
                ptmDiv.style.top = y + 'px';
                dragCell.innerHTML = that._dragCellHTML;
            }
        };

        ptmDiv.ondragover = function(e) {
            e.preventDefault();
        };

        ptmDiv.ondrop = function(e) {
            if (that._dragging) {
                that._dragging = false;
                var x = e.clientX - that._dx;
                ptmDiv.style.left = x + 'px';
                var y = e.clientY - that._dy;
                ptmDiv.style.top = y + 'px';
                dragCell.innerHTML = that._dragCellHTML;
            }
        };

        ptmDiv.onmousedown = function(e) {
            that._dragging = true;
            that._target = e.target;
        };

        ptmDiv.ondragstart = function(e) {
            if (dragCell.contains(that._target)) {
                e.dataTransfer.setData('text/plain', '');
            } else {
                e.preventDefault();
            }
        };

        // The ptm table
        var ptmTableDiv = docById('ptmTableDiv');
        ptmTableDiv.style.display = 'inline';
        ptmTableDiv.style.visibility = 'visible';
        ptmTableDiv.style.border = '0px';
        ptmTableDiv.innerHTML = '';

        // We use an outer div to scroll vertically and an inner div to
        // scroll horizontally.
        ptmTableDiv.innerHTML = '<div id="ptmOuterDiv"><div id="ptmInnerDiv"><table cellpadding="0px" id="ptmTable"></table></div></div>';

        var n = Math.max(Math.floor((window.innerHeight * 0.5) / 100), 8);
        var outerDiv = docById('ptmOuterDiv');
        if (this.rowLabels.length > n) {
            outerDiv.style.height = this._cellScale * MATRIXSOLFEHEIGHT * (n + 6) + 'px';
            var w = Math.max(Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH), BUTTONDIVWIDTH);
            outerDiv.style.width = w + 'px';
        } else {
            outerDiv.style.height = this._cellScale * MATRIXSOLFEHEIGHT * (this.rowLabels.length + 3) + 'px';
            var w = Math.max(Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH - 20), BUTTONDIVWIDTH);
            outerDiv.style.width = w + 'px';
        }

        var w = Math.max(Math.min(window.innerWidth, this._cellScale * INNERWINDOWWIDTH), BUTTONDIVWIDTH - BUTTONSIZE);
        var innerDiv = docById('ptmInnerDiv');
        innerDiv.style.width = w + 'px';
        innerDiv.style.marginLeft = (BUTTONSIZE * 2 * this._cellScale) + 'px';

        // Each row in the ptm table contains a note label in the
        // first column and a table of buttons in the second column.
        var ptmTable = docById('ptmTable');

        var j = 0;
        for (var i = 0; i < this.rowLabels.length; i++) {
            if (this.rowLabels[i].toLowerCase() === 'rest') {
                this._rests += 1;
                continue;
            }

            var ptmTableRow = ptmTable.insertRow();

            // A cell for the row label graphic
            var cell = ptmTableRow.insertCell();
            cell.style.backgroundColor = MATRIXLABELCOLOR;
            cell.style.fontSize = this._cellScale * 100 + '%';
            cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 1 + 'px';
            cell.style.width = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.minWidth = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.maxWidth = cell.style.minWidth;
            cell.className = 'headcol';  // This cell is fixed horizontally.
            cell.innerHTML = '';

            var drumName = getDrumName(this.rowLabels[i]);

            if (drumName != null) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + getDrumIcon(drumName) + '" title="' + drumName + '" alt="' + drumName + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else if (this.rowLabels[i].slice(0, 4) === 'http') {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + getDrumIcon(this.rowLabels[i]) + '" title="' + this.rowLabels[i] + '" alt="' + this.rowLabels[i] + '" height="' + iconSize / 2 + '" width="' + iconSize / 2 + '" vertical-align="middle"/>&nbsp;&nbsp;';
            } else if (MATRIXSYNTHS.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + 'images/synth2.svg' + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else if (MATRIXGRAPHICS.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + 'images/mouse.svg' + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else if (MATRIXGRAPHICS2.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + 'images/mouse.svg' + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else {
                const BELLSETIDX = {'C': 1, 'D': 2, 'E': 3, 'F': 4, 'G': 5, 'A': 6, 'B': 7, 'do': 1, 're': 2, 'me': 3, 'fa': 4, 'sol': 5, 'la': 6, 'ti': 7};
                // Don't add bellset image with sharps and flats.
                var noteName = this.rowLabels[i];
                if (noteName in BELLSETIDX) {
                    cell.innerHTML = '<img src="' + 'images/8_bellset_key_' + BELLSETIDX[noteName] + '.svg' + '" width="' + cell.style.width + '" vertical-align="middle">';
                }
            }

            // A cell for the row label
            var cell = ptmTableRow.insertCell();
            cell.style.backgroundColor = MATRIXLABELCOLOR;
            cell.style.fontSize = this._cellScale * 100 + '%';
            cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 1 + 'px';
            cell.style.width = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.minWidth = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.maxWidth = cell.style.minWidth;
            cell.className = 'labelcol';  // This cell is fixed horizontally.
            cell.style.left = (BUTTONSIZE * this._cellScale) + 'px';

            if (drumName != null) {
                cell.innerHTML = drumName;
                cell.style.fontSize = Math.floor(this._cellScale * 14) + 'px';
                this._noteStored.push(drumName);
            } else if (this.rowLabels[i].slice(0, 4) === 'http') {
                cell.innerHTML = this.rowLabels[i];
                cell.style.fontSize = Math.floor(this._cellScale * 14) + 'px';
                this._noteStored.push(this.rowLabels[i].replace(/ /g,':'));
            } else if (MATRIXSYNTHS.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = this.rowArgs[i];<commit_msg>only use bellset in register 4<commit_after>// Copyright (c) 2015 <NAME>
// Copyright (c) 2015-18 <NAME>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the The GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// You should have received a copy of the GNU Affero General Public
// License along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA

const MATRIXGRAPHICS = ['forward', 'back', 'right', 'left', 'setcolor', 'setshade', 'sethue', 'setgrey', 'settranslucency', 'setpensize', 'setheading'];
const MATRIXGRAPHICS2 = ['arc', 'setxy'];
// Deprecated
const MATRIXSYNTHS = ['sine', 'triangle', 'sawtooth', 'square', 'hertz'];


function PitchTimeMatrix () {
    const BUTTONDIVWIDTH = 476;  // 8 buttons 476 = (55 + 4) * 8
    const OUTERWINDOWWIDTH = 728;  // 675;
    const INNERWINDOWWIDTH = 600;
    const BUTTONSIZE = 53;
    const ICONSIZE = 32;

    this.paramsEffects = {
        "doVibrato": false,
        "doDistortion": false,
        "doTremolo": false,
        "doPhaser": false,
        "doChorus": false,
        "vibratoIntensity": 0,
        "vibratoFrequency": 0,
        "distortionAmount": 0,
        "tremoloFrequency": 0,
        "tremoloDepth": 0,
        "rate": 0,
        "octaves": 0,
        "baseFrequency": 0,
        "chorusRate": 0,
        "delayTime": 0,
        "chorusDepth": 0
    };

    // rowLabels can contain either a pitch, a drum, or a grphics command
    this.rowLabels = [];
    // rowArgs can contain an octave or the arg(s) to a graphics command
    this.rowArgs = [];

    this._sorted = false;
    this._notesToPlay = [];
    this._matrixHasTuplets = false;
    this._notesCounter = 0;
    this._noteStored = [];

    // The pitch-block number associated with a row; a rhythm block is
    // associated with a column. We need to keep track of which
    // intersections in the grid are populated.  The blockMap is a
    // list of selected nodes in the matrix that map pitch blocks to
    // rhythm blocks (note that rhythm blocks can span multiple
    // columns).

    // These arrays get created each time the matrix is built.
    this._rowBlocks = [];  // pitch-block number
    this._colBlocks = [];  // [rhythm-block number, note number]

    // This array keeps track of the position of the rows after sorting.
    this._rowMap = [];
    // And offsets due to deleting duplicates.
    this._rowOffset = [];

    // This array is preserved between sessions.
    // We populate the blockMap whenever a note is selected and
    // restore any notes that might be present.

    this._blockMap = [];

    this.clearBlocks = function() {
        this._rowBlocks = [];
        this._colBlocks = [];
        this._rowMap = [];
        this._rowOffset = [];
    };

    this.addRowBlock = function(rowBlock) {
        this._rowMap.push(this._rowBlocks.length);
        this._rowOffset.push(0);
        this._rowBlocks.push(rowBlock);
    };

    this.addColBlock = function(rhythmBlock, n) {
        // Search for previous instance of the same block (from a repeat)
        var startIdx = 0;
        for (var i = 0; i < this._colBlocks.length; i++) {
            var obj = this._colBlocks[i];
            if (obj[0] === rhythmBlock) {
                startIdx += 1;
            }
        }

        for (var i = startIdx; i < n + startIdx; i++) {
            this._colBlocks.push([rhythmBlock, i]);
        }
    };

    this.addNode = function(pitchBlock, rhythmBlock, n) {
        for (var i = 0; i < this._blockMap.length; i++) {
            var obj = this._blockMap[i];
            if (obj[0] === pitchBlock && obj[1][0] === rhythmBlock && obj[1][1] === n) {
                return;  // node is already in the list
            }
        }
        this._blockMap.push([pitchBlock, [rhythmBlock, n]]);
    };

    this.removeNode = function(pitchBlock, rhythmBlock, n) {
        for (var i = 0; i < this._blockMap.length; i++) {
            var obj = this._blockMap[i];
            if (obj[0] === pitchBlock && obj[1][0] === rhythmBlock && obj[1][1] === n) {
                this._blockMap[i] = [-1, [-1, 1, 0]];  // Mark as removed
            }
        }
    };

    this.init = function(logo) {
        // Initializes the matrix. First removes the previous matrix
        // and them make another one in DOM (document object model)
        this._noteStored = [];
        this._sorted = false;
        this._rests = 0;
        this._logo = logo;

        this.playingNow = false;	

        var w = window.innerWidth;
        this._cellScale = w / 1200;
        var iconSize = ICONSIZE * this._cellScale;

        var canvas = docById('myCanvas');

        // Position the widget and make it visible.
        var ptmDiv = docById('ptmDiv');
        ptmDiv.style.visibility = 'visible';
        ptmDiv.setAttribute('draggable', 'true');
        ptmDiv.style.left = '200px';
        ptmDiv.style.top = '150px';

        // The ptm buttons
        var ptmButtonsDiv = docById('ptmButtonsDiv');
        ptmButtonsDiv.style.display = 'inline';
        ptmButtonsDiv.style.visibility = 'visible';
        ptmButtonsDiv.style.width = BUTTONDIVWIDTH;
        ptmButtonsDiv.innerHTML = '<table cellpadding="0px" id="ptmButtonTable"></table>';

        var buttonTable = docById('ptmButtonTable');
        var header = buttonTable.createTHead();
        var row = header.insertRow(0);

        // For the button callbacks
        var that = this;

        console.log('notes ' + this.rowLabels + ' octave ' + this.rowArgs);

        this._notesToPlay = [];
        this._matrixHasTuplets = false;

        // Add the buttons to the top row.
        var that = this;

        var cell = this._addButton(row, 'play-button.svg', ICONSIZE, _('play'));
        cell.onclick=function() {
            that._logo.setTurtleDelay(0);

            that._logo.resetSynth(0);
            that.playAll();
        }

        var cell = this._addButton(row, 'export-chunk.svg', ICONSIZE, _('save'));
        cell.onclick=function() {
            that._save();
        }

        var cell = this._addButton(row, 'erase-button.svg', ICONSIZE, _('clear'));
        cell.onclick=function() {
            that._clear();
        }

        var cell = this._addButton(row, 'export-button.svg', ICONSIZE, _('export'));
        cell.onclick=function() {
            that._export();
        }

        var cell = this._addButton(row, 'sort.svg', ICONSIZE, _('sort'));
        cell.onclick=function() {
            that._sort();
        }

        var cell = this._addButton(row, 'close-button.svg', ICONSIZE, _('close'));
        cell.onclick=function() {
            that._rowOffset = [];
            for (var i = 0; i < that._rowMap.length; i++) {
                that._rowMap[i] = i;
            }
            that._logo.synth.stopSound(0, 'default');
            that._logo.synth.stop();

            ptmTableDiv.style.visibility = 'hidden';
            ptmButtonsDiv.style.visibility = 'hidden';
            ptmDiv.style.visibility = 'hidden';
        }

        // We use this cell as a handle for dragging.
        var dragCell = this._addButton(row, 'grab.svg', ICONSIZE, _('drag'));
        dragCell.style.cursor = 'move';

        this._dx = dragCell.getBoundingClientRect().left - ptmDiv.getBoundingClientRect().left;
        this._dy = dragCell.getBoundingClientRect().top - ptmDiv.getBoundingClientRect().top;
        this._dragging = false;
        this._target = false;
        this._dragCellHTML = dragCell.innerHTML;

        dragCell.onmouseover = function(e) {
            // In order to prevent the dragged item from triggering a
            // browser reload in Firefox, we empty the cell contents
            // before dragging.
            dragCell.innerHTML = '';
        };

        dragCell.onmouseout = function(e) {
            if (!that._dragging) {
                dragCell.innerHTML = that._dragCellHTML;
            }
        };

        canvas.ondragover = function(e) {
            e.preventDefault();
        };

        canvas.ondrop = function(e) {
            if (that._dragging) {
                that._dragging = false;
                var x = e.clientX - that._dx;
                ptmDiv.style.left = x + 'px';
                var y = e.clientY - that._dy;
                ptmDiv.style.top = y + 'px';
                dragCell.innerHTML = that._dragCellHTML;
            }
        };

        ptmDiv.ondragover = function(e) {
            e.preventDefault();
        };

        ptmDiv.ondrop = function(e) {
            if (that._dragging) {
                that._dragging = false;
                var x = e.clientX - that._dx;
                ptmDiv.style.left = x + 'px';
                var y = e.clientY - that._dy;
                ptmDiv.style.top = y + 'px';
                dragCell.innerHTML = that._dragCellHTML;
            }
        };

        ptmDiv.onmousedown = function(e) {
            that._dragging = true;
            that._target = e.target;
        };

        ptmDiv.ondragstart = function(e) {
            if (dragCell.contains(that._target)) {
                e.dataTransfer.setData('text/plain', '');
            } else {
                e.preventDefault();
            }
        };

        // The ptm table
        var ptmTableDiv = docById('ptmTableDiv');
        ptmTableDiv.style.display = 'inline';
        ptmTableDiv.style.visibility = 'visible';
        ptmTableDiv.style.border = '0px';
        ptmTableDiv.innerHTML = '';

        // We use an outer div to scroll vertically and an inner div to
        // scroll horizontally.
        ptmTableDiv.innerHTML = '<div id="ptmOuterDiv"><div id="ptmInnerDiv"><table cellpadding="0px" id="ptmTable"></table></div></div>';

        var n = Math.max(Math.floor((window.innerHeight * 0.5) / 100), 8);
        var outerDiv = docById('ptmOuterDiv');
        if (this.rowLabels.length > n) {
            outerDiv.style.height = this._cellScale * MATRIXSOLFEHEIGHT * (n + 6) + 'px';
            var w = Math.max(Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH), BUTTONDIVWIDTH);
            outerDiv.style.width = w + 'px';
        } else {
            outerDiv.style.height = this._cellScale * MATRIXSOLFEHEIGHT * (this.rowLabels.length + 3) + 'px';
            var w = Math.max(Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH - 20), BUTTONDIVWIDTH);
            outerDiv.style.width = w + 'px';
        }

        var w = Math.max(Math.min(window.innerWidth, this._cellScale * INNERWINDOWWIDTH), BUTTONDIVWIDTH - BUTTONSIZE);
        var innerDiv = docById('ptmInnerDiv');
        innerDiv.style.width = w + 'px';
        innerDiv.style.marginLeft = (BUTTONSIZE * 2 * this._cellScale) + 'px';

        // Each row in the ptm table contains a note label in the
        // first column and a table of buttons in the second column.
        var ptmTable = docById('ptmTable');

        var j = 0;
        for (var i = 0; i < this.rowLabels.length; i++) {
            if (this.rowLabels[i].toLowerCase() === 'rest') {
                this._rests += 1;
                continue;
            }

            var ptmTableRow = ptmTable.insertRow();

            // A cell for the row label graphic
            var cell = ptmTableRow.insertCell();
            cell.style.backgroundColor = MATRIXLABELCOLOR;
            cell.style.fontSize = this._cellScale * 100 + '%';
            cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 1 + 'px';
            cell.style.width = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.minWidth = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.maxWidth = cell.style.minWidth;
            cell.className = 'headcol';  // This cell is fixed horizontally.
            cell.innerHTML = '';

            var drumName = getDrumName(this.rowLabels[i]);

            if (drumName != null) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + getDrumIcon(drumName) + '" title="' + drumName + '" alt="' + drumName + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else if (this.rowLabels[i].slice(0, 4) === 'http') {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + getDrumIcon(this.rowLabels[i]) + '" title="' + this.rowLabels[i] + '" alt="' + this.rowLabels[i] + '" height="' + iconSize / 2 + '" width="' + iconSize / 2 + '" vertical-align="middle"/>&nbsp;&nbsp;';
            } else if (MATRIXSYNTHS.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + 'images/synth2.svg' + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else if (MATRIXGRAPHICS.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + 'images/mouse.svg' + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else if (MATRIXGRAPHICS2.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = '&nbsp;&nbsp;<img src="' + 'images/mouse.svg' + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle">&nbsp;&nbsp;';
            } else {
                const BELLSETIDX = {'C': 1, 'D': 2, 'E': 3, 'F': 4, 'G': 5, 'A': 6, 'B': 7, 'do': 1, 're': 2, 'me': 3, 'fa': 4, 'sol': 5, 'la': 6, 'ti': 7};
                // Don't add bellset image with sharps and flats.
                var noteName = this.rowLabels[i];
                if (noteName in BELLSETIDX && this.rowArgs[i] === 4) {
                    cell.innerHTML = '<img src="' + 'images/8_bellset_key_' + BELLSETIDX[noteName] + '.svg' + '" width="' + cell.style.width + '" vertical-align="middle">';
                } else if (noteName === 'C' && this.rowArgs[i] === 5) {
                    cell.innerHTML = '<img src="' + 'images/8_bellset_key_8.svg' + '" width="' + cell.style.width + '" vertical-align="middle">';
                }
            }

            // A cell for the row label
            var cell = ptmTableRow.insertCell();
            cell.style.backgroundColor = MATRIXLABELCOLOR;
            cell.style.fontSize = this._cellScale * 100 + '%';
            cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 1 + 'px';
            cell.style.width = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.minWidth = Math.floor(MATRIXSOLFEWIDTH * this._cellScale) + 'px';
            cell.style.maxWidth = cell.style.minWidth;
            cell.className = 'labelcol';  // This cell is fixed horizontally.
            cell.style.left = (BUTTONSIZE * this._cellScale) + 'px';

            if (drumName != null) {
                cell.innerHTML = drumName;
                cell.style.fontSize = Math.floor(this._cellScale * 14) + 'px';
                this._noteStored.push(drumName);
            } else if (this.rowLabels[i].slice(0, 4) === 'http') {
                cell.innerHTML = this.rowLabels[i];
                cell.style.fontSize = Math.floor(this._cellScale * 14) + 'px';
                this._noteStored.push(this.rowLabels[i].replace(/ /g,':'));
            } else if (MATRIXSYNTHS.indexOf(this.rowLabels[i]) !== -1) {
                cell.innerHTML = this.rowArgs[i];
{'fraction_non_alphanumeric': 0.14141348285304473, 'fraction_numerical': 0.010268613900117355, 'mean_word_length': 2.034022351893977, 'pattern_counts': {'":': 32, '<': 48, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4486114', 'n_tokens_mistral': 9684, 'n_tokens_neox': 9108, 'n_words': 2241}