|
|
- // Copyright (c) 2016-17 Walter Bender
- //
- // 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
-
- // This widget makes displays the status of selected parameters and
- // notes as they are being played.
-
-
- function StatusMatrix() {
- const BUTTONDIVWIDTH = 128;
- const BUTTONSIZE = 53;
- const ICONSIZE = 32;
- const OUTERWINDOWWIDTH = 620;
- const INNERWINDOWWIDTH = OUTERWINDOWWIDTH - BUTTONSIZE * 1.5;
-
- docById('statusDiv').style.visibility = 'hidden';
-
- this.init = function(logo) {
- // Initializes the status matrix. First removes the
- // previous matrix and them make another one in DOM (document
- // object model)
- this._logo = logo;
-
- 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 statusDiv = docById('statusDiv');
- statusDiv.style.visibility = 'visible';
- statusDiv.setAttribute('draggable', 'true');
- statusDiv.style.left = '200px';
- statusDiv.style.top = '150px';
-
- // The status buttons
- var statusButtonsDiv = docById('statusButtonsDiv');
- statusButtonsDiv.style.display = 'inline';
- statusButtonsDiv.style.visibility = 'visible';
- statusButtonsDiv.style.width = BUTTONDIVWIDTH;
- statusButtonsDiv.innerHTML = '<table cellpadding="0px" id="statusButtonTable"></table>';
-
- var buttonTable = docById('statusButtonTable');
- var header = buttonTable.createTHead();
- var row = header.insertRow(0);
-
- // For the button callbacks
- var that = this;
-
- var cell = this._addButton(row, 'close-button.svg', ICONSIZE, _('close'));
-
- cell.onclick=function() {
- statusTableDiv.style.visibility = 'hidden';
- statusButtonsDiv.style.visibility = 'hidden';
- statusDiv.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 - statusDiv.getBoundingClientRect().left;
- this._dy = dragCell.getBoundingClientRect().top - statusDiv.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;
- statusDiv.style.left = x + 'px';
- var y = e.clientY - that._dy;
- statusDiv.style.top = y + 'px';
- dragCell.innerHTML = that._dragCellHTML;
- }
- };
-
- statusDiv.ondragover = function(e) {
- e.preventDefault();
- };
-
- statusDiv.ondrop = function(e) {
- if (that._dragging) {
- that._dragging = false;
- var x = e.clientX - that._dx;
- statusDiv.style.left = x + 'px';
- var y = e.clientY - that._dy;
- statusDiv.style.top = y + 'px';
- dragCell.innerHTML = that._dragCellHTML;
- }
- };
-
- statusDiv.onmousedown = function(e) {
- that._dragging = true;
- that._target = e.target;
- };
-
- statusDiv.ondragstart = function(e) {
- if (dragCell.contains(that._target)) {
- e.dataTransfer.setData('text/plain', '');
- } else {
- e.preventDefault();
- }
- };
-
- // The status table
- var statusTableDiv = docById('statusTableDiv');
- statusTableDiv.style.display = 'inline';
- statusTableDiv.style.visibility = 'visible';
- statusTableDiv.style.border = '0px';
-
- // We use an outer div to scroll vertically and an inner div to
- // scroll horizontally.
- statusTableDiv.innerHTML = '<div id="statusOuterDiv"><div id="statusInnerDiv"><table cellpadding="0px" id="statusTable"></table></div></div>';
-
- var n = Math.max(Math.floor((window.innerHeight * 0.5) / 100), 8);
- var outerDiv = docById('statusOuterDiv');
- if (this._logo.turtles.turtleList.length > n) {
- outerDiv.style.height = this._cellScale * MATRIXSOLFEHEIGHT * (n + 2) + 'px';
- var w = Math.max(Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH), BUTTONDIVWIDTH);
- outerDiv.style.width = w + 'px';
- } else {
- if (this._logo.statusFields.length > 4) { // Assume we need a horizontal slider
- outerDiv.style.height = this._cellScale * (MATRIXBUTTONHEIGHT2 + (2 + MATRIXSOLFEHEIGHT) * this._logo.turtles.turtleList.length) + 30 + 'px';
- } else {
- outerDiv.style.height = this._cellScale * (MATRIXBUTTONHEIGHT2 + (2 + MATRIXSOLFEHEIGHT) * this._logo.turtles.turtleList.length) + '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('statusInnerDiv');
- innerDiv.style.width = w + 'px';
- innerDiv.style.marginLeft = (BUTTONSIZE * this._cellScale) + 'px';
-
- // Each row in the status table contains a note label in the
- // first column and a table of buttons in the second column.
- var statusTable = docById('statusTable');
-
- var header = statusTable.createTHead();
- var row = header.insertRow();
-
- var iconSize = Math.floor(this._cellScale * 24);
-
- var cell = row.insertCell();
- cell.style.backgroundColor = MATRIXBUTTONCOLOR;
- cell.className = 'headcol';
- cell.style.height = Math.floor(MATRIXBUTTONHEIGHT * this._cellScale) + 'px';
- cell.style.width = (BUTTONSIZE * this._cellScale) + 'px';
- cell.innerHTML = ' ';
-
- // One column per field
- for (var i = 0; i < this._logo.statusFields.length; i++) {
- var cell = row.insertCell(i + 1);
- cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
-
- switch (this._logo.statusFields[i][1]) {
- case 'plus':
- case 'minus':
- case 'neg':
- case 'divide':
- case 'power':
- case 'multiply':
- case 'sqrt':
- case 'int':
- case 'mod':
- var label = '';
- break;
- case 'namedbox':
- var label = this._logo.blocks.blockList[this._logo.statusFields[i][0]].privateData;
- break;
- default:
- var label = this._logo.blocks.blockList[this._logo.statusFields[i][0]].protoblock.staticLabels[0];
- break;
- }
-
- cell.innerHTML = ' <b>' + label + '</b> '
- cell.style.height = Math.floor(MATRIXBUTTONHEIGHT * this._cellScale) + 'px';
- cell.style.backgroundColor = MATRIXBUTTONCOLOR;
- }
-
- if (_THIS_IS_MUSIC_BLOCKS_) {
- var cell = row.insertCell();
- cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
- cell.innerHTML = ' <b>' + _('note') + '</b> '
- cell.style.height = Math.floor(MATRIXBUTTONHEIGHT * this._cellScale) + 'px';
- cell.style.backgroundColor = MATRIXBUTTONCOLOR;
- }
-
- // One row per voice (turtle)
- var activeTurtles = 0;
- for (var turtle = 0; turtle < this._logo.turtles.turtleList.length; turtle++) {
- if (this._logo.turtles.turtleList[turtle].trash) {
- continue;
- }
-
- var row = header.insertRow();
- var cell = row.insertCell();
- cell.style.backgroundColor = MATRIXLABELCOLOR;
-
- if (_THIS_IS_MUSIC_BLOCKS_) {
- cell.innerHTML = ' <img src="images/mouse.svg" title="' + this._logo.turtles.turtleList[turtle].name + '" alt="' + this._logo.turtles.turtleList[turtle].name + '" height="' + iconSize + '" width="' + iconSize + '"> ';
- } else {
- cell.innerHTML = ' <img src="header-icons/turtle-button.svg" title="' + this._logo.turtles.turtleList[turtle].name + '" alt="' + this._logo.turtles.turtleList[turtle].name + '" height="' + iconSize + '" width="' + iconSize + '"> ';
- }
-
- cell.style.width = (BUTTONSIZE * this._cellScale) + 'px';
- cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 'px';
- cell.className = 'headcol';
-
- if (_THIS_IS_MUSIC_BLOCKS_) {
- // + 1 is for the note column
- for (var i = 0; i < this._logo.statusFields.length + 1; i++) {
- var cell = row.insertCell();
- cell.style.backgroundColor = MATRIXRHYTHMCELLCOLOR;
- cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
- cell.innerHTML = '';
- cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 'px';
- }
- } else {
- for (var i = 0; i < this._logo.statusFields.length; i++) {
- var cell = row.insertCell();
- cell.style.backgroundColor = MATRIXRHYTHMCELLCOLOR;
- cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
- cell.innerHTML = '';
- cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 'px';
- }
- }
-
- activeTurtles += 1;
- }
- };
-
- this.updateAll = function() {
- // Update status of all of the voices in the matrix.
- var table = docById('statusTable');
-
- this._logo.updatingStatusMatrix = true;
-
- var activeTurtles = 0;
- for (var turtle = 0; turtle < this._logo.turtles.turtleList.length; turtle++) {
- if (this._logo.turtles.turtleList[turtle].trash) {
- continue;
- }
-
- for (var i = 0; i < this._logo.statusFields.length; i++) {
- var saveStatus = this._logo.inStatusMatrix;
- this._logo.inStatusMatrix = false;
-
- this._logo.parseArg(this._logo, turtle, this._logo.statusFields[i][0]);
- switch (this._logo.blocks.blockList[this._logo.statusFields[i][0]].name) {
- case 'x':
- case 'y':
- case 'heading':
- var value = this._logo.blocks.blockList[this._logo.statusFields[i][0]].value.toFixed(2);
- break;
- case 'elapsednotes':
- var value = mixedNumber(this._logo.blocks.blockList[this._logo.statusFields[i][0]].value);
- break;
- case 'namedbox':
- var name = this._logo.blocks.blockList[this._logo.statusFields[i][0]].privateData;
- if (name in this._logo.boxes) {
- var value = this._logo.boxes[name];
- } else {
- var value = '';
- }
- break;
- default:
- var value = this._logo.blocks.blockList[this._logo.statusFields[i][0]].value;
- break;
- }
-
- var innerHTML = value;
-
- this._logo.inStatusMatrix = saveStatus;
-
- var cell = table.rows[activeTurtles + 1].cells[i + 1];
- if (cell != null) {
- cell.innerHTML = innerHTML;
- }
- }
-
- if (_THIS_IS_MUSIC_BLOCKS_) {
- var note = '';
- var value = '';
- if (this._logo.noteStatus[turtle] != null) {
- var notes = this._logo.noteStatus[turtle][0];
- for (var j = 0; j < notes.length; j++) {
- note += notes[j];
- if (typeof(notes[j]) === 'number') {
- note += 'Hz ';
- } else {
- note += ' ';
- }
- }
- var value = this._logo.noteStatus[turtle][1];
-
- var obj = rationalToFraction(value);
- note += obj[1] + '/' + obj[0];
- }
-
- var cell = table.rows[activeTurtles + 1].cells[i + 1];
- if (cell != null) {
- cell.innerHTML = note.replace(/#/g, '♯').replace(/b/, '♭');
- }
- }
-
- activeTurtles += 1;
- }
-
- this._logo.updatingStatusMatrix = false;
- };
-
- this._addButton = function(row, icon, iconSize, label) {
- var cell = row.insertCell(-1);
- cell.innerHTML = ' <img src="header-icons/' + icon + '" title="' + label + '" alt="' + label + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle" align-content="center"> ';
- cell.style.width = BUTTONSIZE + 'px';
- cell.style.minWidth = cell.style.width;
- cell.style.maxWidth = cell.style.width;
- cell.style.height = cell.style.width;
- cell.style.minHeight = cell.style.height;
- cell.style.maxHeight = cell.style.height;
- cell.style.backgroundColor = MATRIXBUTTONCOLOR;
-
- cell.onmouseover=function() {
- this.style.backgroundColor = MATRIXBUTTONCOLORHOVER;
- }
-
- cell.onmouseout=function() {
- this.style.backgroundColor = MATRIXBUTTONCOLOR;
- }
-
- return cell;
- };
- };
|