|
|
- // Copyright (c) 2014-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
-
- // Minimum distance (squared) between two docks required before
- // connecting them.
- const MINIMUMDOCKDISTANCE = 400;
-
- // Special value flags to uniquely identify these media blocks.
- const CAMERAVALUE = '##__CAMERA__##';
- const VIDEOVALUE = '##__VIDEO__##';
-
- // Blocks holds the list of blocks and most of the block-associated
- // methods, since most block manipulations are inter-block.
-
- function Blocks () {
- if (sugarizerCompatibility.isInsideSugarizer()) {
- storage = sugarizerCompatibility.data;
- } else {
- storage = localStorage;
- }
-
- this.canvas = null;
- this.stage = null;
- this.refreshCanvas = null;
- this.trashcan = null;
- this.updateStage = null;
- this.getStageScale = null;
-
- // We keep a list of stacks in the trash.
- this.trashStacks = [];
-
- // We keep a dictionary for the proto blocks,
- this.protoBlockDict = {}
- // and a list of the blocks we create.
- this.blockList = [];
-
- // Track the time with mouse down.
- this.mouseDownTime = 0;
- this.longPressTimeout = null;
-
- // "Copy stack" selects a stack for pasting. Are we selecting?
- this.selectingStack = false;
- // and what did we select?
- this.selectedStack = null;
- // and a copy of the selected stack for pasting.
- this.selectedBlocksObj = null;
-
- // If we somehow have a malformed block database (for example,
- // from importing a corrupted datafile, we need to avoid infinite
- // loops while crawling the block list.
- this._loopCounter = 0;
- this._sizeCounter = 0;
- this._searchCounter = 0;
-
- // We need a reference to the palettes.
- this.palettes = null;
- // Which block, if any, is highlighted?
- this.highlightedBlock = null;
- // Which block, if any, is active?
- this.activeBlock = null;
- // Are the blocks visible?
- this.visible = true;
- // The group of blocks being dragged or moved together
- this.dragGroup = [];
- // The blocks at the tops of stacks
- this.stackList = [];
- // The blocks that need expanding
- this._expandablesList = [];
- // Number of blocks to load
- this._loadCounter = 0;
- // Stacks of blocks that need adjusting as blocks are repositioned
- // due to expanding and contracting or insertion into the flow.
- this._adjustTheseDocks = [];
- // Blocks that need collapsing after load.
- this.blocksToCollapse = [];
- // Arg blocks that need expanding after load.
- this._checkTwoArgBlocks = [];
- // Arg clamp blocks that need expanding after load.
- this._checkArgClampBlocks = [];
- // Clamp blocks that need expanding after load.
- this._clampBlocksToCheck = [];
-
- // We need to keep track of certain classes of blocks that exhibit
- // different types of behavior.
-
- // Blocks with parts that expand, e.g.,
- this._expandableBlocks = [];
- // Blocks that contain child flows of blocks
- this.clampBlocks = [];
- this.doubleExpandable = [];
- this.argClampBlocks = [];
- // Blocks that are used as arguments to other blocks
- this.argBlocks = [];
- // Blocks that return values
- this.valueBlocks = [];
- // Two-arg blocks with two arguments (expandable).
- this.twoArgBlocks = [];
- // Blocks that don't run when clicked.
- this.noRunBlocks = [];
-
- this._homeButtonContainers = [];
- this.blockScale = DEFAULTBLOCKSCALE;
-
- // We need to know if we are processing a copy or save stack command.
- this.inLongPress = false;
-
- // We stage deletion of prototype action blocks on the palette so
- // as to avoid palette refresh race conditions.
- this.deleteActionTimeout = 0;
-
- this.setCanvas = function (canvas) {
- this.canvas = canvas;
- return this;
- };
-
- this.setStage = function (stage) {
- this.stage = stage;
- return this;
- };
-
- this.setRefreshCanvas = function (refreshCanvas) {
- this.refreshCanvas = refreshCanvas;
- return this;
- };
-
- this.setTrashcan = function (trashcan) {
- this.trashcan = trashcan;
- return this;
- };
-
- this.setUpdateStage = function (updateStage) {
- this.updateStage = updateStage;
- return this;
- };
-
- this.setGetStageScale = function (getStageScale) {
- this.getStageScale = getStageScale;
- return this;
- };
-
- // Change the scale of the blocks (and the protoblocks on the palette).
- this.setBlockScale = function (scale) {
- console.log('New block scale is ' + scale);
- this.blockScale = scale;
-
- // Regenerate all of the artwork at the new scale.
- for (var blk = 0; blk < this.blockList.length; blk++) {
- // if (!this.blockList[blk].trash) {
- this.blockList[blk].resize(scale);
- // }
- }
-
- this.findStacks();
- for (var stack = 0; stack < this.stackList.length; stack++) {
- // console.log('Adjust Docks: ' + this.blockList[this.stackList[stack]].name);
- this.adjustDocks(this.stackList[stack], true);
- }
-
- // We reset the protoblock scale on the palettes, but don't
- // modify the palettes themselves.
- for (var palette in this.palettes.dict) {
- for (var blk = 0; blk < this.palettes.dict[palette].protoList.length; blk++) {
- this.palettes.dict[palette].protoList[blk].scale = scale;
- }
- }
- };
-
- // We need access to the msg block...
- this.setMsgText = function (msgText) {
- this.msgText = msgText;
- };
-
- // and the Error msg function.
- this.setErrorMsg = function (errorMsg) {
- this.errorMsg = errorMsg;
- return this;
- };
-
- // We need access to the macro dictionary because we add to it.
- this.setMacroDictionary = function (obj) {
- this.macroDict = obj;
- return this;
- };
-
- // We need access to the turtles list because we associate a
- // turtle with each start block.
- this.setTurtles = function (turtles) {
- this.turtles = turtles;
- return this;
- };
-
- // We need to access the "pseudo-Logo interpreter" when we click
- // on blocks.
- this.setLogo = function (logo) {
- this.logo = logo;
- return this;
- };
-
- // The scale of the graphics is determined by screen size.
- this.setScale = function (scale) {
- this.blockScale = scale;
- return this;
- };
-
- // Toggle state of collapsible blocks.
- this.toggleCollapsibles = function () {
- for (var blk in this.blockList) {
- var myBlock = this.blockList[blk];
- if (COLLAPSABLES.indexOf(myBlock.name) !== -1 && !myBlock.trash) {
- myBlock.collapseToggle();
- }
- }
- };
-
- // We need access to the go-home buttons and boundary.
- this.setHomeContainers = function (containers, boundary) {
- this._homeButtonContainers = containers;
- this.boundary = boundary;
- return this;
- };
-
- // set up copy/paste, dismiss, and copy-stack buttons
- this.makeCopyPasteButtons = function (makeButton, updatePasteButton) {
- var that = this;
- this.updatePasteButton = updatePasteButton;
-
- this.dismissButton = makeButton('cancel-button', '', 0, 0, 55, 0, this.stage);
- this.dismissButton.visible = false;
-
- this.saveStackButton = makeButton('save-blocks-button', _('Save stack'), 0, 0, 55, 0, this.stage);
- this.saveStackButton.visible = false;
-
- this.dismissButton.on('click', function (event) {
- that.saveStackButton.visible = false;
- that.dismissButton.visible = false;
- that.inLongPress = false;
- that.refreshCanvas();
- });
-
- this.saveStackButton.on('click', function (event) {
- // Only invoked from action blocks.
- var topBlock = that.findTopBlock(that.activeBlock);
- that.inLongPress = false;
- that.selectedStack = topBlock;
- that.saveStackButton.visible = false;
- that.dismissButton.visible = false;
- that.saveStack();
- that.refreshCanvas();
- });
- };
-
- // Walk through all of the proto blocks in order to make lists of
- // any blocks that need special treatment.
- this.findBlockTypes = function () {
- for (var proto in this.protoBlockDict) {
- if (this.protoBlockDict[proto].expandable) {
- this._expandableBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'clamp') {
- this.clampBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'argclamp') {
- this.argClampBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'argflowclamp') {
- this.clampBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'argclamparg') {
- this.argClampBlocks.push(this.protoBlockDict[proto].name);
- this.argBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'twoarg') {
- this.twoArgBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'arg') {
- this.argBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'value') {
- this.argBlocks.push(this.protoBlockDict[proto].name);
- this.valueBlocks.push(this.protoBlockDict[proto].name);
- }
- if (this.protoBlockDict[proto].style === 'doubleclamp') {
- this.doubleExpandable.push(this.protoBlockDict[proto].name);
- }
- }
- };
-
- this._actionBlock = function (name) {
- return ['do', 'doArg', 'calc', 'calcArg'].indexOf(name) !== -1;
- };
-
- this._namedActionBlock = function (name) {
- return ['nameddo', 'nameddoArg', 'namedcalc', 'namedcalcArg'].indexOf(name) !== -1;
- };
-
- // Adjust the docking postions of all blocks in the current drag
- // group.
- this._adjustBlockPositions = function () {
- if (this.dragGroup.length < 2) {
- return;
- }
-
- // console.log('Adjust Docks: ' + this.blockList[this.dragGroup[0]].name);
- this.adjustDocks(this.dragGroup[0], true)
- };
-
- // Adjust the size of the clamp in an expandable block when blocks
- // are inserted into (or removed from) the child flow. This is a
- // common operation for start and action blocks, but also for
- // repeat, forever, if, etc.
- this._adjustExpandableClampBlock = function () {
- if (this._clampBlocksToCheck.length === 0) {
- return;
- }
-
- var obj = this._clampBlocksToCheck.pop();
- var blk = obj[0];
- var clamp = obj[1];
-
- var myBlock = this.blockList[blk];
-
- if (myBlock.isArgFlowClampBlock()) {
- // Make sure myBlock is a clamp block.
- } else if (myBlock.isArgBlock() || myBlock.isTwoArgBlock()) {
- return;
- } else if (myBlock.isArgClamp()) {
- // We handle ArgClamp blocks elsewhere.
- this._adjustArgClampBlock([blk]);
- return;
- }
-
- function clampAdjuster(blocks, blk, myBlock, clamp) {
- // First we need to count up the number of (and size of) the
- // blocks inside the clamp; The child flow is usually the
- // second-to-last argument.
-
- if (myBlock.isArgFlowClampBlock()) {
- var c = 1; // 0: outie; and 1: child flow
- } else if (clamp === 0) {
- var c = myBlock.connections.length - 2;
- } else { // e.g., Bottom clamp in if-then-else
- var c = myBlock.connections.length - 3;
- }
-
- blocks._sizeCounter = 0;
- var childFlowSize = 1;
- if (c > 0 && myBlock.connections[c] != null) {
- childFlowSize = Math.max(blocks._getStackSize(myBlock.connections[c]), 1);
- }
-
- // Adjust the clamp size to match the size of the child
- // flow.
- var plusMinus = childFlowSize - myBlock.clampCount[clamp];
- if (plusMinus !== 0) {
- if (!(childFlowSize === 0 && myBlock.clampCount[clamp] === 1)) {
- myBlock.updateSlots(clamp, plusMinus);
- }
- }
-
- // Recurse through the list.
- setTimeout(function () {
- if (blocks._clampBlocksToCheck.length > 0) {
- blocks._adjustExpandableClampBlock();
- }
- }, 250);
- };
-
- clampAdjuster(this, blk, myBlock, clamp);
- };
-
- // Returns the block size.
- this._getBlockSize = function (blk) {
- var myBlock = this.blockList[blk];
- return myBlock.size;
- };
-
- // Adjust the slot sizes of arg clamps.
- this._adjustArgClampBlock = function (argBlocksToCheck) {
- if (argBlocksToCheck.length === 0) {
- return;
- }
-
- var blk = argBlocksToCheck.pop();
- var myBlock = this.blockList[blk];
-
- // Which connection do we start with?
- if (['doArg', 'calcArg'].indexOf(myBlock.name) !== -1) {
- var ci = 2;
- } else {
- var ci = 1;
- }
-
- // Get the current slot list.
- var slotList = myBlock.argClampSlots;
-
- var update = false;
- // Determine the size of each argument.
- for (var i = 0; i < slotList.length; i++) {
- var c = myBlock.connections[ci + i];
- var size = 1; // Minimum size
- if (c != null) {
- size = Math.max(this._getBlockSize(c), 1);
- }
- if (slotList[i] !== size) {
- slotList[i] = size;
- update = true;
- }
- }
- if (update) {
- myBlock.updateArgSlots(slotList);
- }
- };
-
- // We also adjust the size of twoarg blocks. It is similar to how
- // we adjust clamps, but enough different that it is in its own
- // function.
- this._adjustExpandableTwoArgBlock = function (argBlocksToCheck) {
- if (argBlocksToCheck.length === 0) {
- return;
- }
-
- var blk = argBlocksToCheck.pop();
- var myBlock = this.blockList[blk];
-
- // Determine the size of the first argument.
- var c = myBlock.connections[1];
- var firstArgumentSize = 1; // Minimum size
- if (c != null) {
- firstArgumentSize = Math.max(this._getBlockSize(c), 1);
- }
-
- // Expand/contract block by plusMinus.
- var plusMinus = firstArgumentSize - myBlock.clampCount[0];
- if (plusMinus !== 0) {
- if (!(firstArgumentSize === 0)) {
- myBlock.updateSlots(0, plusMinus);
- }
- }
- };
-
- this._addRemoveVspaceBlock = function (blk) {
- var myBlock = this.blockList[blk];
-
- var c = myBlock.connections[myBlock.connections.length - 2];
- var secondArgumentSize = 1;
- if (c != null) {
- var secondArgumentSize = Math.max(this._getBlockSize(c), 1);
- }
-
- var that = this;
-
- var vSpaceCount = howManyVSpaceBlocksBelow(blk);
- if (secondArgumentSize < vSpaceCount + 1) {
- // Remove a vspace block
- var n = Math.abs(secondArgumentSize - vSpaceCount - 1);
- for (var i = 0; i < n; i++) {
- var lastConnection = myBlock.connections.length - 1;
- var vspaceBlock = this.blockList[myBlock.connections[lastConnection]];
- var nextBlockIndex = vspaceBlock.connections[1];
- myBlock.connections[lastConnection] = nextBlockIndex;
- if (nextBlockIndex != null) {
- this.blockList[nextBlockIndex].connections[0] = blk;
- }
- vspaceBlock.connections = [null, null];
- vspaceBlock.trash = true;
- vspaceBlock.hide();
- }
- } else if (secondArgumentSize > vSpaceCount + 1) {
- // Add vspace blocks
- var n = secondArgumentSize - vSpaceCount - 1;
- var nextBlock = last(myBlock.connections);
- var thisBlock = myBlock;
- var newPos = this.blockList.length;
-
- var that = this;
-
- function vspaceAdjuster(args) { // nextBlock, vspace, i, n
- var thisBlock = args[0];
- var nextBlock = args[1];
- var vspace = args[2];
- var i = args[3];
- var n = args[4];
- var vspaceBlock = that.blockList[vspace];
- var lastDock = last(thisBlock.docks);
- var dx = lastDock[0] - vspaceBlock.docks[0][0];
- var dy = lastDock[1] - vspaceBlock.docks[0][1];
- vspaceBlock.container.x = thisBlock.container.x + dx;
- vspaceBlock.container.y = thisBlock.container.y + dy;
- vspaceBlock.connections[0] = that.blockList.indexOf(thisBlock);
- vspaceBlock.connections[1] = nextBlock;
- thisBlock.connections[thisBlock.connections.length - 1] = vspace;
- if (nextBlock) {
- that.blockList[nextBlock].connections[0] = vspace;
- }
- if (i + 1 < n) {
- var newPos = that.blockList.length;
- thisBlock = last(that.blockList);
- nextBlock = last(thisBlock.connections);
- that._makeNewBlockWithConnections('vspace', newPos, [null, null], vspaceAdjuster, [thisBlock, nextBlock, newPos, i + 1, n]);
- }
- };
-
- this._makeNewBlockWithConnections('vspace', newPos, [null, null], vspaceAdjuster, [thisBlock, nextBlock, newPos, 0, n]);
- };
-
- function howManyVSpaceBlocksBelow(blk) {
- // Need to know how many vspace blocks are below the block
- // we're checking against.
- var nextBlock = last(that.blockList[blk].connections);
- if (nextBlock && that.blockList[nextBlock].name === 'vspace') {
- return 1 + howManyVSpaceBlocksBelow(nextBlock);
- // Recurse until it isn't a vspace
- }
- return 0;
- };
- };
-
- this._getStackSize = function (blk) {
- // How many block units in this stack?
- var size = 0;
- this._sizeCounter += 1;
- if (this._sizeCounter > this.blockList.length * 2) {
- console.log('Infinite loop encountered detecting size of expandable block? ' + blk);
- return size;
- }
-
- if (blk == null) {
- return size;
- }
-
- var myBlock = this.blockList[blk];
- if (myBlock == null) {
- console.log('Something very broken in _getStackSize.');
- }
-
- if (myBlock.isClampBlock()) {
- var c = myBlock.connections.length - 2;
- var csize = 0;
- if (c > 0) {
- var cblk = myBlock.connections[c];
- if (cblk != null) {
- csize = this._getStackSize(cblk);
- }
- if (csize === 0) {
- size = 1; // minimum of 1 slot in clamp
- } else {
- size = csize;
- }
- }
-
- if (myBlock.isDoubleClampBlock()) {
- var c = myBlock.connections.length - 3;
- var csize = 0;
- if (c > 0) {
- var cblk = myBlock.connections[c];
- if (cblk != null) {
- csize = this._getStackSize(cblk);
- }
-
- if (csize === 0) {
- size += 1; // minimum of 1 slot in clamp
- } else {
- size += csize;
- }
- }
- }
-
- // add top and bottom of clamp
- size += myBlock.size;
- } else {
- size = myBlock.size;
- }
-
- // check on any connected block
- if (!myBlock.isValueBlock()) {
- var cblk = last(myBlock.connections);
- if (cblk != null) {
- size += this._getStackSize(cblk);
- }
- }
- return size;
- };
-
- this.adjustDocks = function (blk, resetLoopCounter) {
- // Give a block, adjust the dock positions
- // of all of the blocks connected to it
-
- var myBlock = this.blockList[blk];
-
- // For when we come in from makeBlock
- if (resetLoopCounter != null) {
- this._loopCounter = 0;
- }
-
- // These checks are to test for malformed data. All blocks
- // should have connections.
- if (myBlock == null) {
- console.log('Saw a null block: ' + blk);
- return;
- }
-
- if (myBlock.connections == null) {
- console.log('Saw a block with null connections: ' + blk);
- return;
- }
-
- if (myBlock.connections.length === 0) {
- console.log('Saw a block with [] connections: ' + blk);
- return;
- }
-
- // Value blocks only have one dock.
- if (myBlock.docks.length === 1) {
- return;
- }
-
- this._loopCounter += 1;
- if (this._loopCounter > this.blockList.length * 2) {
- console.log('Infinite loop encountered while adjusting docks: ' + blk + ' ' + this.blockList);
- return;
- }
-
- // Walk through each connection except the parent block; the
- // exception being the parent block of boolean 2arg blocks,
- // since the dock[0] position can change.
- if (myBlock.isTwoArgBooleanBlock()) {
- var start = 0;
- } else {
- var start = 1;
- }
- for (var c = start; c < myBlock.connections.length; c++) {
- // Get the dock position for this connection.
- var bdock = myBlock.docks[c];
-
- // Find the connecting block.
- var cblk = myBlock.connections[c];
- // Nothing connected here so continue to the next connection.
- if (cblk == null) {
- continue;
- }
-
- // Another database integrety check.
- if (this.blockList[cblk] == null) {
- console.log('This is not good: we encountered a null block: ' + cblk);
- continue;
- }
-
- // Find the dock position in the connected block.
- var foundMatch = false;
- for (var b = 0; b < this.blockList[cblk].connections.length; b++) {
- if (this.blockList[cblk].connections[b] === blk) {
- foundMatch = true;
- break
- }
- }
-
- // Yet another database integrety check.
- if (!foundMatch) {
- console.log('Did not find match for ' + myBlock.name + ' (' + blk + ') and ' + this.blockList[cblk].name + ' (' + cblk + ')');
- console.log(myBlock.connections);
- console.log(this.blockList[cblk].connections);
- break;
- }
-
- var cdock = this.blockList[cblk].docks[b];
-
- if (c > 0) {
- // Move the connected block...
- var dx = bdock[0] - cdock[0];
- var dy = bdock[1] - cdock[1];
- if (myBlock.container == null) {
- console.log('Does this ever happen any more?')
- } else {
- var nx = myBlock.container.x + dx;
- var ny = myBlock.container.y + dy;
- }
- this._moveBlock(cblk, nx, ny);
- } else {
- // or it's parent.
- var dx = cdock[0] - bdock[0];
- var dy = cdock[1] - bdock[1];
- var nx = this.blockList[cblk].container.x + dx;
- var ny = this.blockList[cblk].container.y + dy;
- this._moveBlock(blk, nx, ny);
- }
-
- if (c > 0) {
- // Recurse on connected blocks.
- this.adjustDocks(cblk, true);
- }
- }
- };
-
- this.addDefaultBlock = function (parentblk, oldBlock, skipOldBlock) {
- // Add an action name whenever the user removes the name from
- // an action block. Add a box name whenever the user removes
- // the name from a storein block. Add a Silence block
- // whenever the user removes all the blocks from a Note block.
- if (parentblk == null) {
- return;
- }
-
- if (this.blockList[parentblk].name === 'action') {
- var cblk = this.blockList[parentblk].connections[1];
- if (cblk == null) {
- var that = this;
- postProcess = function (args) {
- var parentblk = args[0];
- var oldBlock = args[1];
-
- var blk = that.blockList.length - 1;
- that.blockList[parentblk].connections[1] = blk;
- that.blockList[blk].value = that.findUniqueActionName(_('action'));
- var label = that.blockList[blk].value;
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
- that.blockList[blk].text.text = label;
- that.blockList[blk].container.updateCache();
-
- if (that.blockList[blk].value !== that.blockList[oldBlock].value) {
-
- that.newNameddoBlock(that.blockList[blk].value, that.actionHasReturn(parentblk), that.actionHasArgs(parentblk));
- var blockPalette = that.palettes.dict['action'];
- for (var b = 0; b < blockPalette.protoList.length; b++) {
- var protoblock = blockPalette.protoList[b];
- if (protoblock.name === 'nameddo' && protoblock.defaults[0] === that.blockList[oldBlock].value) {
- setTimeout(function () {
- blockPalette.remove(protoblock, that.blockList[oldBlock].value);
- delete that.protoBlockDict['myDo_' + that.blockList[oldBlock].value];
- that.palettes.hide();
- that.palettes.updatePalettes('action');
- that.palettes.show();
- }, 500);
-
- break;
- }
- }
-
- that.renameNameddos(that.blockList[oldBlock].value, that.blockList[blk].value);
- if (skipOldBlock) {
- that.renameDos(that.blockList[oldBlock].value, that.blockList[blk].value, oldBlock);
- } else {
- that.renameDos(that.blockList[oldBlock].value, that.blockList[blk].value);
- }
- }
-
- that.adjustDocks(parentblk, true);
- };
-
- this._makeNewBlockWithConnections('text', 0, [parentblk], postProcess, [parentblk, oldBlock], false);
- }
- } else if (this.blockList[parentblk].name === 'storein') {
- var cblk = this.blockList[parentblk].connections[1];
- if (cblk == null) {
- var that = this;
- postProcess = function (args) {
- var parentblk = args[0];
- var oldBlock = args[1];
-
- var blk = that.blockList.length - 1;
- that.blockList[parentblk].connections[1] = blk;
- that.blockList[blk].value = _('box');
- var label = that.blockList[blk].value;
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
- that.blockList[blk].text.text = label;
- that.blockList[blk].container.updateCache();
-
- that.adjustDocks(parentblk, true);
- };
-
- this._makeNewBlockWithConnections('text', 0, [parentblk], postProcess, [parentblk, oldBlock], false);
- }
- } else if (['newnote', 'osctime'].indexOf(this.blockList[parentblk].name) !== -1) {
- var cblk = this.blockList[parentblk].connections[2];
- if (cblk == null) {
- var blkname = 'vspace';
- var newVspaceBlock = this.makeBlock(blkname, '__NOARG__');
- this.blockList[parentblk].connections[2] = newVspaceBlock;
- this.blockList[newVspaceBlock].connections[0] = parentblk;
- var blkname = 'rest2';
- var newSilenceBlock = this.makeBlock(blkname, '__NOARG__');
- this.blockList[newSilenceBlock].connections[0] = newVspaceBlock;
- this.blockList[newSilenceBlock].connections[1] = null;
- this.blockList[newVspaceBlock].connections[1] = newSilenceBlock;
- } else if (this.blockList[cblk].name === 'vspace' && this.blockList[cblk].connections[1] == null) {
- var blkname = 'rest2';
- var newSilenceBlock = this.makeBlock(blkname, '__NOARG__');
- this.blockList[newSilenceBlock].connections[0] = cblk;
- this.blockList[newSilenceBlock].connections[1] = null;
- this.blockList[cblk].connections[1] = newSilenceBlock;
- }
- }
- };
-
- this.deleteNextDefault = function (thisBlock) {
- // Remove the Silence block from a Note block if another block
- // is inserted above the silence block.
- var thisBlockobj = this.blockList[thisBlock];
- for (var i = 1; i < thisBlockobj.connections.length; i++) {
- if (thisBlockobj.connections[i] && this.blockList[thisBlockobj.connections[i]].name === 'rest2') {
- var silenceBlock = thisBlockobj.connections[i];
- var silenceBlockobj = this.blockList[silenceBlock];
- silenceBlockobj.hide();
- silenceBlockobj.trash = true;
- this.blockList[thisBlock].connections[i] = silenceBlockobj.connections[1];
- break;
- }
- }
- };
-
- this.deletePreviousDefault = function (thisBlock) {
- // Remove the Silence block from a Note block if another block
- // is inserted just after the Silence block.
- var thisBlockobj = this.blockList[thisBlock];
- if (thisBlockobj && this.blockList[thisBlockobj.connections[0]] && this.blockList[thisBlockobj.connections[0]].name === 'rest2') {
- var silenceBlock = thisBlockobj.connections[0];
- var silenceBlockobj = this.blockList[silenceBlock];
- silenceBlockobj.hide();
- silenceBlockobj.trash = true;
-
- for (var i = 0; i < this.blockList[silenceBlockobj.connections[0]].connections.length; i++) {
- if (this.blockList[silenceBlockobj.connections[0]].connections[i] === silenceBlock) {
- this.blockList[silenceBlockobj.connections[0]].connections[i] = thisBlock;
- break;
- }
- }
-
- thisBlockobj.connections[0] = silenceBlockobj.connections[0];
- }
-
- return thisBlockobj.connections[0];
- };
-
- this.blockMoved = function (thisBlock) {
- // When a block is moved, we have lots of things to check:
- // (0) Is it inside of a expandable block?
- // Is it an arg inside an arg clamp?
- // (1) Is it an arg block connected to a two-arg block?
- // (2) Disconnect its connection[0];
- // (3) Look for a new connection;
- // Is it potentially an arg inside an arg clamp?
- // (4) Is it an arg block connected to a 2-arg block?
- // (5) Is it a pitch block being inserted or removed from
- // a Note clamp? In which case, we may have to remove
- // or add a silence block.
- // (6) Is it the name of an action block? In which case we
- // need to check to see if we need to rename it.
- // (7) Is it the name of a storein block? In which case we
- // need to check to see if we need to add a palette entry.
- // (8) And we need to recheck if it inside of a expandable block.
-
- // Find any containing expandable blocks.
- this._clampBlocksToCheck = [];
- if (thisBlock == null) {
- console.log('blockMoved called with null block.');
- return;
- }
-
- var blk = this._insideExpandableBlock(thisBlock);
- var expandableLoopCounter = 0;
-
- var parentblk = null;
- if (blk != null) {
- parentblk = blk;
- }
-
- var actionCheck = false;
-
- while (blk != null) {
- expandableLoopCounter += 1;
- if (expandableLoopCounter > 2 * this.blockList.length) {
- console.log('Infinite loop encountered checking for expandables?');
- break;
- }
-
- this._clampBlocksToCheck.push([blk, 0]);
- blk = this._insideExpandableBlock(blk);
- }
-
- this._checkTwoArgBlocks = [];
- var checkArgBlocks = [];
- var myBlock = this.blockList[thisBlock];
- if (myBlock == null) {
- console.log('null block found in blockMoved method: ' + thisBlock);
- return;
- }
-
- var c = myBlock.connections[0];
- if (c != null) {
- var cBlock = this.blockList[c];
- }
-
- // If it is an arg block, where is it coming from?
- if (myBlock.isArgBlock() && c != null) {
- // We care about twoarg (2arg) blocks with
- // connections to the first arg;
- if (this.blockList[c].isTwoArgBlock() || this.blockList[c].isArgClamp()) {
- if (cBlock.connections[1] === thisBlock) {
- this._checkTwoArgBlocks.push(c);
- }
- } else if (this.blockList[c].isArgBlock() && this.blockList[c].isExpandableBlock() || this.blockList[c].isArgClamp()) {
- if (cBlock.connections[1] === thisBlock) {
- this._checkTwoArgBlocks.push(c);
- }
- }
- }
-
- // Disconnect from connection[0] (both sides of the connection).
- if (c != null) {
- // Disconnect both ends of the connection.
- for (var i = 1; i < cBlock.connections.length; i++) {
- if (cBlock.connections[i] === thisBlock) {
- cBlock.connections[i] = null;
- break;
- }
- }
- myBlock.connections[0] = null;
- this.raiseStackToTop(thisBlock);
- }
-
- // Look for a new connection.
- var x1 = myBlock.container.x + myBlock.docks[0][0];
- var y1 = myBlock.container.y + myBlock.docks[0][1];
-
- // Find the nearest dock; if it is close enough, make the
- // connection.
- var newBlock = null;
- var newConnection = null;
- var min = (MINIMUMDOCKDISTANCE/DEFAULTBLOCKSCALE) * this.blockScale;
- var blkType = myBlock.docks[0][2];
-
- // Is the added block above the silence block or below?
- var insertAfterDefault = true;
-
- for (var b = 0; b < this.blockList.length; b++) {
- // Don't connect to yourself.
- if (b === thisBlock) {
- continue;
- }
-
- // Don't connect to a collapsed block.
- if (this.blockList[b].collapsed) {
- continue;
- }
-
- // Don't connect to a block in the trash.
- if (this.blockList[b].trash) {
- continue;
- }
-
- for (var i = 1; i < this.blockList[b].connections.length; i++) {
- // When converting from Python projects to JS format,
- // sometimes extra null connections are added. We need
- // to ignore them.
- if (i === this.blockList[b].docks.length) {
- break;
- }
-
- if ((i === this.blockList[b].connections.length - 1) && (this.blockList[b].connections[i] != null) && (this.blockList[this.blockList[b].connections[i]].isNoHitBlock())) {
- // Don't break the connection between a block and
- // a hidden block below it.
- continue;
- } else if ((['backward', 'status'].indexOf(this.blockList[b].name) !== -1) && (i === 1) && (this.blockList[b].connections[1] != null) && (this.blockList[this.blockList[b].connections[1]].isNoHitBlock())) {
- // Don't break the connection betweem a backward
- // block and a hidden block attached to its clamp.
- continue;
- } else if (this.blockList[b].name === 'action' && (i === 2) && (this.blockList[b].connections[2] != null) && (this.blockList[this.blockList[b].connections[2]].isNoHitBlock())) {
- // Don't break the connection betweem an action
- // block and a hidden block attached to its clamp.
- continue;
- }
-
- // Look for available connections.
- if (this._testConnectionType(blkType, this.blockList[b].docks[i][2])) {
- var x2 = this.blockList[b].container.x + this.blockList[b].docks[i][0];
- var y2 = this.blockList[b].container.y + this.blockList[b].docks[i][1];
- var dist = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
- if (dist < min) {
- newBlock = b;
- newConnection = i;
- min = dist;
- }
- } else {
- // TODO: bounce away from illegal connection?
- // only if the distance was small
- // console.log('cannot not connect these two block types');
- }
- }
- }
-
- if (newBlock != null) {
- // We found a match.
- myBlock.connections[0] = newBlock;
- var connection = this.blockList[newBlock].connections[newConnection];
- if (connection == null) {
- if (this.blockList[newBlock].isArgClamp()) {
- // If it is an arg clamp, we may have to adjust
- // the slot size.
- if ((this.blockList[newBlock].name === 'doArg' || this.blockList[newBlock].name === 'calcArg') && newConnection === 1) {
- } else if (['doArg', 'nameddoArg'].indexOf(this.blockList[newBlock].name) !== -1 && newConnection === this.blockList[newBlock].connections.length - 1) {
- } else {
- // Get the size of the block we are inserting
- // adding.
- var size = this._getBlockSize(thisBlock);
- // console.log('inserting block of size ' + size + ' to arg clamp ' + this.blockList[newBlock].name);
- // Get the current slot list.
- var slotList = this.blockList[newBlock].argClampSlots;
-
- // Which slot is this block in?
- if (['doArg', 'calcArg'].indexOf(this.blockList[newBlock].name) !== -1) {
- var si = newConnection - 2;
- } else {
- var si = newConnection - 1;
- }
- if (slotList[si] !== size) {
- slotList[si] = size;
- this.blockList[newBlock].updateArgSlots(slotList);
- }
- }
- }
- } else {
- // Three scenarios in which we may be overriding an
- // existing connection:
- // (1) if it is an argClamp, add a new slot below the
- // current block;
- // (2) if it is an arg block, replace it; or
- // (3) if it is a flow block, insert it into the flow.
- // A few corner cases: Whenever we connect (or disconnect)
- // from an action block (c[1] arg), we need to ensure we have
- // a unique action name; Whenever we connect to a newnote
- // block (c[2] flow), we need to ensure we have either a silence
- // block or a pitch block. And if we are connecting to a
- // storein block, we need to ensure that there is a palette
- // entry for the new namedbox.
- insertAfterDefault = false;
- if (this.blockList[newBlock].isArgClamp()) {
- if ((this.blockList[newBlock].name === 'doArg' || this.blockList[newBlock].name === 'calcArg') && newConnection === 1) {
- // If it is the action name then treat it like
- // a standard replacement.
- this.blockList[connection].connections[0] = null;
- this.findDragGroup(connection);
- for (var c = 0; c < this.dragGroup.length; c++) {
- this.moveBlockRelative(this.dragGroup[c], 40, 40);
- }
- } else if (['doArg', 'nameddoArg'].indexOf(this.blockList[newBlock].name) !== -1 && newConnection === this.blockList[newBlock].connections.length - 1) {
- // If it is the bottom of the flow, insert as
- // usual.
- var bottom = this.findBottomBlock(thisBlock);
- this.blockList[connection].connections[0] = bottom;
- this.blockList[bottom].connections[this.blockList[bottom].connections.length - 1] = connection;
- } else {
- // Move the block in the current slot down one
- // slot (cascading and creating a new slot if
- // necessary).
-
- // Get the size of the block we are inserting adding.
- var size = this._getBlockSize(thisBlock);
-
- // Get the current slot list.
- var slotList = this.blockList[newBlock].argClampSlots;
- // Which slot is this block in?
- var ci = this.blockList[newBlock].connections.indexOf(connection);
- if (['doArg', 'calcArg'].indexOf(this.blockList[newBlock].name) !== -1) {
- var si = ci - 2;
- } else {
- var si = ci - 1;
- }
-
- var emptySlot = null;
- var emptyConnection = null;
- // Is there an empty slot below?
- for (var emptySlot = si; emptySlot < slotList.length; emptySlot++) {
- if (this.blockList[newBlock].connections[ci + emptySlot - si] == null) {
- emptyConnection = ci + emptySlot - si;
- break;
- }
- }
-
- if (emptyConnection == null) {
- slotList.push(1);
- this._newLocalArgBlock(slotList.length);
- emptyConnection = ci + emptySlot - si;
- this.blockList[newBlock].connections.push(null);
-
- // Slide everything down one slot.
- for (var i = slotList.length - 1; i > si + 1; i--) {
- slotList[i] = slotList[i - 1];
- }
- for (var i = this.blockList[newBlock].connections.length - 1; i > ci + 1; i--) {
- this.blockList[newBlock].connections[i] = this.blockList[newBlock].connections[i - 1];
- }
- }
- // The new block is added below the current
- // connection...
- newConnection += 1;
- // Set its slot size too.
- slotList[si + 1] = size;
-
- this.blockList[newBlock].updateArgSlots(slotList);
- }
- } else if (myBlock.isArgBlock()) {
- this.blockList[connection].connections[0] = null;
- this.findDragGroup(connection);
- for (var c = 0; c < this.dragGroup.length; c++) {
- this.moveBlockRelative(this.dragGroup[c], 40, 40);
- }
-
- // We need to rename the action stack.
- if (this.blockList[newBlock].name === 'action') {
- actionCheck = true;
-
- if (myBlock.value !== this.blockList[connection].value) {
- // Temporarily disconnect to ensure we don't
- // find myBlock when looking for a unique name.
- var c = myBlock.connections[0];
- myBlock.connections[0] = null;
- var name = this.findUniqueActionName(myBlock.value);
- myBlock.connections[0] = c;
-
- if (name !== myBlock.value) {
- myBlock.value = name;
- var label = name;
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
- myBlock.text.text = label;
- myBlock.container.updateCache();
- }
-
- var that = this;
- setTimeout(function () {
- // A previously disconnected name may have left
- // an entry in the palette we need to remove.
- var name = that.blockList[connection].value;
- if (that.protoBlockDict['myDo_' + name] != undefined) {
- delete that.protoBlockDict['myDo_' + name];
- that.palettes.dict['action'].hideMenu(true);
- }
-
- that.newNameddoBlock(myBlock.value, that.actionHasReturn(newBlock), that.actionHasArgs(newBlock));
- var blockPalette = that.palettes.dict['action'];
- for (var b = 0; b < blockPalette.protoList.length; b++) {
- var protoblock = blockPalette.protoList[b];
- if (protoblock.name === 'nameddo' && protoblock.staticLabels[0] === that.blockList[connection].value) {
- setTimeout(function () {
- blockPalette.remove(protoblock, that.blockList[connection].value);
- delete that.protoBlockDict['myDo_' + that.blockList[connection].value];
- that.palettes.hide();
- that.palettes.updatePalettes('action');
- that.palettes.show();
- }, 500);
-
- break;
- }
- }
-
- that.renameNameddos(that.blockList[connection].value, myBlock.value);
- that.renameDos(that.blockList[connection].value, myBlock.value);
- }, 750);
- }
- } else if (this.blockList[newBlock].name === 'storein') {
- // We may need to add new storein and namedo
- // blocks to the palette.
- if (myBlock.value !== 'box') {
- this.newStoreinBlock(myBlock.value);
- this.newNamedboxBlock(myBlock.value);
- var that = this;
- setTimeout(function () {
- that.palettes.hide();
- that.palettes.updatePalettes('boxes');
- that.palettes.show();
- }, 500);
- }
- }
- } else if (!this.blockList[thisBlock].isArgFlowClampBlock()) {
- var bottom = this.findBottomBlock(thisBlock);
- this.blockList[connection].connections[0] = bottom;
- this.blockList[bottom].connections[this.blockList[bottom].connections.length - 1] = connection;
- }
- }
-
- this.blockList[newBlock].connections[newConnection] = thisBlock;
-
- // Remove the silence block (if it is present) after
- // adding a new block inside of a note block.
- if (this._insideNoteBlock(thisBlock) != null) {
- // If blocks are inserted above the silence block.
- if (insertAfterDefault) {
- newBlock = this.deletePreviousDefault(thisBlock);
- } else {
- this.deleteNextDefault(bottom);
- }
- }
-
- // If we attached a name to an action block, check to see
- // if we need to rename it.
- if (this.blockList[newBlock].name === 'action' && !actionCheck) {
- // Is there already another action block with this name?
- for (var b = 0; b < this.blockList.length; b++) {
- if (b === newBlock) continue;
- if (this.blockList[b].name === 'action') {
- if (this.blockList[b].connections[1] != null) {
- if (this.blockList[this.blockList[b].connections[1]].value === this.blockList[thisBlock].value) {
- this.blockList[thisBlock].value = this.findUniqueActionName(this.blockList[thisBlock].value);
- var label = this.blockList[thisBlock].value;
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
- this.blockList[thisBlock].text.text = label;
- this.blockList[thisBlock].container.updateCache();
- this.newNameddoBlock(this.blockList[thisBlock].value, this.actionHasReturn(b), this.actionHasArgs(b));
- this.setActionProtoVisiblity(false);
- }
- }
- }
- }
- }
-
- // console.log('Adjust Docks: ' + this.blockList[newBlock].name);
- this.adjustDocks(newBlock, true);
- // TODO: some graphical feedback re new connection?
- }
-
- // If it is an arg block, where is it coming from?
- // FIXME: improve mechanism for testing block types.
- if ((myBlock.isArgBlock() || myBlock.name === 'calcArg' || myBlock.name === 'namedcalcArg') && newBlock != null) {
- // We care about twoarg blocks with connections to the
- // first arg;
- if (this.blockList[newBlock].isTwoArgBlock()) {
- if (this.blockList[newBlock].connections[1] === thisBlock) {
- if (this._checkTwoArgBlocks.indexOf(newBlock) === -1) {
- this._checkTwoArgBlocks.push(newBlock);
- }
- }
- } else if (this.blockList[newBlock].isArgBlock() && this.blockList[newBlock].isExpandableBlock()) {
- if (this.blockList[newBlock].connections[1] === thisBlock) {
- if (this._checkTwoArgBlocks.indexOf(newBlock) === -1) {
- this._checkTwoArgBlocks.push(newBlock);
- }
- }
- }
- // We also care about the second-to-last connection to an
- // arg block.
- var n = this.blockList[newBlock].connections.length;
- if (this.blockList[newBlock].connections[n - 2] === thisBlock) {
- // Only flow blocks, but not ArgClamps
- if (!this.blockList[newBlock].isArgClamp() && this.blockList[newBlock].docks[n - 1][2] === 'in') {
- checkArgBlocks.push(newBlock);
- }
- }
- }
-
- this.addDefaultBlock(parentblk, thisBlock, actionCheck);
-
- // Put block adjustments inside a slight delay to make the
- // addition/substraction of vspace and changes of block shape
- // appear less abrupt (and it can be a little racy).
- var that = this;
- setTimeout(function () {
- // If we changed the contents of a arg block, we may need a vspace.
- if (checkArgBlocks.length > 0) {
- for (var i = 0; i < checkArgBlocks.length; i++) {
- that._addRemoveVspaceBlock(checkArgBlocks[i]);
- }
- }
-
- // If we changed the contents of a two-arg block, we need to
- // adjust it.
- if (that._checkTwoArgBlocks.length > 0) {
- that._adjustExpandableTwoArgBlock(that._checkTwoArgBlocks);
- }
-
- // First, adjust the docks for any blocks that may have
- // had a vspace added.
- for (var i = 0; i < checkArgBlocks.length; i++) {
- // console.log('Adjust Docks: ' + this.blockList[checkArgBlocks[i]].name);
- that.adjustDocks(checkArgBlocks[i], true);
- }
-
- // Next, recheck if the connection is inside of a
- // expandable block.
- var blk = that._insideExpandableBlock(thisBlock);
- var expandableLoopCounter = 0;
- while (blk != null) {
- // Extra check for malformed data.
- expandableLoopCounter += 1;
- if (expandableLoopCounter > 2 * that.blockList.length) {
- console.log('Infinite loop checking for expandables?');
- console.log(that.blockList);
- break;
- }
-
- if (that.blockList[blk].name === 'ifthenelse') {
- that._clampBlocksToCheck.push([blk, 0]);
- that._clampBlocksToCheck.push([blk, 1]);
- } else {
- that._clampBlocksToCheck.push([blk, 0]);
- }
-
- blk = that._insideExpandableBlock(blk);
- }
-
- that._adjustExpandableClampBlock();
- that.refreshCanvas();
- }, 250);
- };
-
- this._testConnectionType = function (type1, type2) {
- // Can these two blocks dock?
- if (type1 === 'in' && type2 === 'out') {
- return true;
- }
- if (type1 === 'out' && type2 === 'in') {
- return true;
- }
- if (type1 === 'numberin' && ['numberout', 'anyout'].indexOf(type2) !== -1) {
- return true;
- }
- if (['numberout', 'anyout'].indexOf(type1) !== -1 && type2 === 'numberin') {
- return true;
- }
- if (type1 === 'textin' && ['textout', 'anyout'].indexOf(type2) !== -1) {
- return true;
- }
- if (['textout', 'anyout'].indexOf(type1) !== -1 && type2 === 'textin') {
- return true;
- }
- if (type1 === 'booleanout' && type2 === 'booleanin') {
- return true;
- }
- if (type1 === 'booleanin' && type2 === 'booleanout') {
- return true;
- }
- if (type1 === 'mediain' && type2 === 'mediaout') {
- return true;
- }
- if (type1 === 'mediaout' && type2 === 'mediain') {
- return true;
- }
- if (type1 === 'mediain' && type2 === 'textout') {
- return true;
- }
- if (type2 === 'mediain' && type1 === 'textout') {
- return true;
- }
- if (type1 === 'filein' && type2 === 'fileout') {
- return true;
- }
- if (type1 === 'fileout' && type2 === 'filein') {
- return true;
- }
- if (type1 === 'solfegein' && ['anyout', 'solfegeout', 'textout', 'noteout', 'numberout'].indexOf(type2) !== -1) {
- return true;
- }
- if (type2 === 'solfegein' && ['anyout', 'solfegeout', 'textout', 'noteout', 'numberout'].indexOf(type1) !== -1) {
- return true;
- }
- if (type1 === 'notein' && ['solfegeout', 'textout', 'noteout'].indexOf(type2) !== -1) {
- return true;
- }
- if (type2 === 'notein' && ['solfegeout', 'textout', 'noteout'].indexOf(type1) !== -1) {
- return true;
- }
- if (type1 === 'anyin' && ['textout', 'mediaout', 'numberout', 'anyout', 'fileout', 'solfegeout', 'noteout'].indexOf(type2) !== -1) {
- return true;
- }
- if (type2 === 'anyin' && ['textout', 'mediaout', 'numberout', 'anyout', 'fileout', 'solfegeout', 'noteout'].indexOf(type1) !== -1) {
- return true;
- }
- return false;
- };
-
- this.updateBlockPositions = function () {
- // Create the block image if it doesn't yet exist.
- for (var blk = 0; blk < this.blockList.length; blk++) {
- this._moveBlock(blk, this.blockList[blk].container.x, this.blockList[blk].container.y);
- }
- };
-
- this.bringToTop = function () {
- // Move all the blocks to the top layer of the stage
- this._adjustTheseStacks = [];
-
- for (var blk in this.blockList) {
- var myBlock = this.blockList[blk];
- /*
- this.stage.removeChild(myBlock.container);
- this.stage.addChild(myBlock.container);
- if (myBlock.collapseContainer != null) {
- this.stage.removeChild(myBlock.collapseContainer);
- this.stage.addChild(myBlock.collapseContainer);
- }
- */
- if (myBlock.connections[0] == null) {
- this._adjustTheseStacks.push(blk);
- }
- }
- for (var blk = 0; blk < this._adjustTheseStacks.length; blk++) {
- // console.log('Adjust Stack: ' + this.blockList[this._adjustTheseStacks[blk]].name);
- this.raiseStackToTop(this._adjustTheseStacks[blk]);
- }
-
- this.refreshCanvas();
- };
-
- this.checkBounds = function () {
- var onScreen = true;
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (this.blockList[blk].connections[0] == null) {
- if (this.blockList[blk].offScreen(this.boundary)) {
- this._homeButtonContainers[0].visible = true;
- this._homeButtonContainers[1].visible = false;
- this.boundary.show();
- onScreen = false;
- break;
- }
- }
- }
- if (onScreen) {
- this._homeButtonContainers[0].visible = false;
- this._homeButtonContainers[1].visible = true;
- this.boundary.hide();
- }
- };
-
- this._moveBlock = function (blk, x, y) {
- // Move a block (and its label) to x, y.
- var myBlock = this.blockList[blk];
- if (myBlock.container != null) {
- myBlock.container.x = x;
- myBlock.container.y = y;
-
- if (myBlock.collapseContainer != null) {
- myBlock.collapseContainer.x = x + COLLAPSEBUTTONXOFF * (this.blockList[blk].protoblock.scale / 2);
- myBlock.collapseContainer.y = y + COLLAPSEBUTTONYOFF * (this.blockList[blk].protoblock.scale / 2);
- }
-
- this.checkBounds();
- } else {
- console.log('No container yet for block ' + myBlock.name);
- }
- };
-
- this.moveBlockRelative = function (blk, dx, dy) {
- // Move a block (and its label) by dx, dy.
- if (this.inLongPress) {
- this.saveStackButton.visible = false;
- this.dismissButton.visible = false;
- this.inLongPress = false;
- }
-
- var myBlock = this.blockList[blk];
- if (myBlock.container != null) {
- myBlock.container.x += dx;
- myBlock.container.y += dy;
-
- if (myBlock.collapseContainer != null) {
- myBlock.collapseContainer.x += dx;
- myBlock.collapseContainer.y += dy;
- }
-
- this.checkBounds();
- } else {
- console.log('No container yet for block ' + myBlock.name);
- }
- };
-
- this.moveStackRelative = function (blk, dx, dy) {
- this.findDragGroup(blk)
- if (this.dragGroup.length > 0) {
- for (var b = 0; b < this.dragGroup.length; b++) {
- this.moveBlockRelative(this.dragGroup[b], dx, dy);
- }
- }
- };
-
- this.updateBlockText = function (blk) {
- // When we create new blocks, we may not have assigned the
- // value yet.
- var myBlock = this.blockList[blk];
- var maxLength = 8;
- if (myBlock.text == null) {
- return;
- }
-
- if (myBlock.name === 'loadFile') {
- try {
- var label = myBlock.value[0].toString();
- } catch (e) {
- var label = _('open file');
- }
- maxLength = 10;
- } else if (myBlock.name === 'solfege') {
- var obj = splitSolfege(myBlock.value);
- var label = i18nSolfege(obj[0]);
- var attr = obj[1];
-
- if (attr !== '♮') {
- label += attr;
- }
- } else if (myBlock.name === 'eastindiansolfege') {
- var obj = splitSolfege(myBlock.value);
- var label = WESTERN2EISOLFEGENAMES[obj[0]];
- var attr = obj[1];
-
- if (attr !== '♮') {
- label += attr;
- }
- } else {
- var label = myBlock.value.toString();
- }
-
- if (label.length > maxLength) {
- label = label.substr(0, maxLength - 1) + '...';
- }
-
- myBlock.text.text = label;
-
- // Make sure text is on top.
- var z = myBlock.container.getNumChildren() - 1;
- myBlock.container.setChildIndex(myBlock.text, z);
-
- if (myBlock.loadComplete) {
- myBlock.container.updateCache();
- } else {
- console.log('Load not yet complete for (' + blk + ') ' + myBlock.name);
- }
- };
-
- this.findTopBlock = function (blk) {
- // Find the top block in a stack.
- if (blk == null) {
- return null;
- }
-
- var myBlock = this.blockList[blk];
- if (myBlock.connections == null) {
- return blk;
- }
-
- if (myBlock.connections.length === 0) {
- return blk;
- }
-
- // Test for corrupted-connection scenario.
- if (myBlock.connections.length > 1 && myBlock.connections[0] != null && myBlock.connections[0] === last(myBlock.connections)) {
- console.log('WARNING: CORRUPTED BLOCK DATA. Block ' + myBlock.name + ' (' + blk + ') is connected to the same block ' + this.blockList[myBlock.connections[0]].name + ' (' + myBlock.connections[0] + ') twice.');
- return blk;
- }
-
- var topBlockLoop = 0;
- while (myBlock.connections[0] != null) {
- topBlockLoop += 1;
- if (topBlockLoop > 2 * this.blockList.length) {
- // Could happen if the block data is malformed.
- console.log('infinite loop finding topBlock?');
- console.log(this.blockList.indexOf(myBlock) + ' ' + myBlock.name);
- break;
- }
- blk = myBlock.connections[0];
- myBlock = this.blockList[blk];
- }
- return blk;
- };
-
- this.sameGeneration = function (firstBlk, childBlk) {
- if (firstBlk == null || childBlk == null) {
- return false;
- }
-
- if (firstBlk === childBlk) {
- return true;
- }
-
- var myBlock = this.blockList[firstBlk];
- if (myBlock.connections == null) {
- return false;
- }
-
- if (myBlock.connections.length === 0) {
- return false;
- }
-
- var bottomBlockLoop = 0;
- while (last(myBlock.connections) != null) {
- bottomBlockLoop += 1;
- if (bottomBlockLoop > 2 * this.blockList.length) {
- // Could happen if the block data is malformed.
- console.log('infinite loop finding bottomBlock?');
- break;
- }
- blk = last(myBlock.connections);
- myBlock = this.blockList[blk];
- if (blk === childBlk) {
- return true;
- }
- }
- return false;
- }
-
- this.findBottomBlock = function (blk) {
- // Find the bottom block in a stack.
- if (blk == null) {
- return null;
- }
-
- var myBlock = this.blockList[blk];
- if (myBlock.connections == null) {
- return blk;
- }
-
- if (myBlock.connections.length === 0) {
- return blk;
- }
-
- var bottomBlockLoop = 0;
- while (last(myBlock.connections) != null) {
- bottomBlockLoop += 1;
- if (bottomBlockLoop > 2 * this.blockList.length) {
- // Could happen if the block data is malformed.
- console.log('infinite loop finding bottomBlock?');
- break;
- }
- blk = last(myBlock.connections);
- myBlock = this.blockList[blk];
- }
- return blk;
- };
-
- this.findStacks = function () {
- // Find any blocks with null in the first connection.
- this.stackList = [];
- for (var i = 0; i < this.blockList.length; i++) {
- if (this.blockList[i].connections[0] == null) {
- this.stackList.push(i)
- }
- }
- };
-
- this._findClamps = function () {
- // Find any clamp blocks.
- this._expandablesList = [];
- this.findStacks(); // We start by finding the stacks
- for (var i = 0; i < this.stackList.length; i++) {
- this._searchCounter = 0;
- this._searchForExpandables(this.stackList[i]);
- }
-
- this._searchForArgFlow();
- };
-
- this._findTwoArgs = function () {
- // Find any expandable arg blocks.
- this._expandablesList = [];
- for (var i = 0; i < this.blockList.length; i++) {
- if (this.blockList[i].isArgBlock() && this.blockList[i].isExpandableBlock()) {
- this._expandablesList.push(i);
- } else if (this.blockList[i].isTwoArgBlock()) {
- this._expandablesList.push(i);
- }
- }
- };
-
- this._searchForArgFlow = function () {
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (this.blockList[blk].isArgFlowClampBlock()) {
- this._expandablesList.push(blk);
- }
- }
- };
-
- this._searchForExpandables = function (blk) {
- // Find the expandable blocks below blk in a stack.
- while (blk != null && this.blockList[blk] != null && !this.blockList[blk].isValueBlock()) {
- // More checks for malformed or corrupted block data.
- this._searchCounter += 1;
- if (this._searchCounter > 2 * this.blockList.length) {
- console.log('infinite loop searching for Expandables? ' + this._searchCounter);
- console.log(blk + ' ' + this.blockList[blk].name);
- break;
- }
-
- if (this.blockList[blk].isClampBlock()) {
- this._expandablesList.push(blk);
- var c = this.blockList[blk].connections.length - 2;
- this._searchForExpandables(this.blockList[blk].connections[c]);
- if (this.blockList[blk].name === 'ifthenelse') {
- // search top clamp too
- var c = 2;
- this._searchForExpandables(this.blockList[blk].connections[c]);
- }
- } else if (this.blockList[blk].isArgClamp()) {
- this._expandablesList.push(blk);
- }
-
- if (this.blockList[blk].connections.length > 1) {
- blk = last(this.blockList[blk].connections);
- } else {
- // A value block only connects back to its parent, so
- // end the search here.
- blk = null;
- }
- }
- };
-
- this._expandTwoArgs = function () {
- // Expand expandable 2-arg blocks as needed.
- this._findTwoArgs();
- this._adjustExpandableTwoArgBlock(this._expandablesList);
- this.refreshCanvas();
- };
-
- this._expandClamps = function () {
- // Expand expandable clamp blocks as needed.
- this._findClamps();
- this._clampBlocksToCheck = [];
- for (var i = 0; i < this._expandablesList.length; i++) {
- if (this.blockList[this._expandablesList[i]].name === 'ifthenelse') {
- this._clampBlocksToCheck.push([this._expandablesList[i], 0]);
- this._clampBlocksToCheck.push([this._expandablesList[i], 1]);
- } else {
- this._clampBlocksToCheck.push([this._expandablesList[i], 0]);
- }
- }
-
- this._adjustExpandableClampBlock();
- this.refreshCanvas();
- };
-
- this.changeDisabledStatus = function (name, flag) {
- // Some blocks, e.g., sensor blocks for Butia, change their
- // appearance depending upon if they have been enabled or
- // disabled.
- for (var blk in this.blockList) {
- var myBlock = this.blockList[blk];
- if (myBlock.name === name) {
- myBlock.protoblock.disabled = flag;
- myBlock.regenerateArtwork(false);
- }
- }
- };
-
- this.unhighlightAll = function () {
- for (var blk in this.blockList) {
- this.unhighlight(blk);
- }
- };
-
- this.unhighlight = function (blk) {
- if (!this.visible) {
- return;
- }
- if (blk != null) {
- var thisBlock = blk;
- } else {
- var thisBlock = this.highlightedBlock;
- }
- if (thisBlock != null) {
-
- this.blockList[thisBlock].unhighlight();
- }
- if (this.highlightedBlock = thisBlock) {
- this.highlightedBlock = null;
- }
- };
-
- this.highlight = function (blk, unhighlight) {
- if (!this.visible) {
- return;
- }
- if (blk != null) {
- if (unhighlight) {
- this.unhighlight(null);
- }
- this.blockList[blk].highlight();
- this.highlightedBlock = blk;
- }
- };
-
- this.hide = function () {
- for (var blk in this.blockList) {
- this.blockList[blk].hide();
- }
- this.visible = false;
- };
-
- this.show = function () {
- for (var blk in this.blockList) {
- this.blockList[blk].show();
- }
- this.visible = true;
- };
-
- this._makeNewBlockWithConnections = function (name, blockOffset, connections, postProcess, postProcessArg, collapsed) {
- if (typeof(collapsed) === 'undefined') {
- collapsed = false
- }
- myBlock = this.makeNewBlock(name, postProcess, postProcessArg);
- if (myBlock == null) {
- console.log('could not make block ' + name);
- return;
- }
-
- // myBlock.collapsed = !collapsed;
- for (var c = 0; c < connections.length; c++) {
- if (c === myBlock.docks.length) {
- break;
- }
- if (connections[c] == null) {
- myBlock.connections.push(null);
- } else {
- myBlock.connections.push(connections[c] + blockOffset);
- }
- }
- };
-
- this.makeNewBlock = function (name, postProcess, postProcessArg) {
- // Create a new block
- if (!name in this.protoBlockDict) {
- // Should never happen: nop blocks should be substituted
- console.log('makeNewBlock: no prototype for ' + name);
- return null;
- }
-
- if (this.protoBlockDict[name] == null) {
- // Should never happen
- console.log('makeNewBlock: no prototype for ' + name);
- return null;
- }
-
- // Deprecated
- // If we drag in a synth block, we need to load the synth.
- if (['sine', 'sawtooth', 'triangle', 'square'].indexOf(name) !== -1) {
- if (_THIS_IS_MUSIC_BLOCKS_) {
- this.logo.synth.loadSynth(name);
- }
- }
-
- if (['namedbox', 'nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg'].indexOf(name) !== -1) {
- this.blockList.push(new Block(this.protoBlockDict[name], this, postProcessArg[1]));
- } else if (name === 'namedarg') {
- this.blockList.push(new Block(this.protoBlockDict[name], this, 'arg ' + postProcessArg[1]));
- } else {
- this.blockList.push(new Block(this.protoBlockDict[name], this));
- }
-
- if (last(this.blockList) == null) {
- // Should never happen
- console.log('failed to make protoblock for ' + name);
- return null;
- }
-
- // We copy the dock because expandable blocks modify it.
- var myBlock = last(this.blockList);
- myBlock.copySize();
-
- // We may need to do some postProcessing to the block
- myBlock.postProcess = postProcess;
- myBlock.postProcessArg = postProcessArg;
-
- // We need a container for the block graphics.
- myBlock.container = new createjs.Container();
- this.stage.addChild(myBlock.container);
- myBlock.container.snapToPixelEnabled = true;
- myBlock.container.x = 0;
- myBlock.container.y = 0;
-
- // and we need to load the images into the container.
- myBlock.imageLoad();
- return myBlock;
- };
-
- this.makeBlock = function (name, arg) {
- // Make a new block from a proto block.
- // Called from palettes.
-
- if (name === 'text') {
- console.log('makeBlock ' + name + ' ' + arg);
- }
- var postProcess = null;
- var postProcessArg = null;
- var that = this;
- var thisBlock = this.blockList.length;
- if (name === 'start') {
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = that.turtles.turtleList.length;
- that.turtles.addTurtle(that.blockList[thisBlock]);
- };
-
- postProcessArg = thisBlock;
- } else if (name === 'drum') {
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = that.turtles.turtleList.length;
- that.turtles.addDrum(that.blockList[thisBlock]);
- };
-
- postProcessArg = thisBlock;
- } else if (name === 'text') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value;
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, _('text')];
- } else if (name === 'solfege') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value;
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, 'sol'];
- } else if (name === 'eastindiansolfege') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = WESTERN2EISOLFEGENAMES[value];
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, 'sol'];
- } else if (name === 'notename') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value;
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, 'G'];
- } else if (name === 'drumname') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value;
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, 'kick'];
- } else if (name === 'voicename') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value;
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, 'sine'];
- } else if (name === 'modename') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value;
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, 'Major'];
- } else if (name === 'number') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = Number(args[1]);
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value.toString();
- that.blockList[thisBlock].container.updateCache();
- };
-
- postProcessArg = [thisBlock, NUMBERBLOCKDEFAULT];
- } else if (name === 'media') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- if (value == null) {
- that.blockList[thisBlock].image = 'images/load-media.svg';
- } else {
- that.blockList[thisBlock].image = null;
- }
- };
-
- postProcessArg = [thisBlock, null];
- } else if (name === 'camera') {
- postProcess = function (args) {
- console.log('post process camera ' + args[1]);
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = CAMERAVALUE;
- if (value == null) {
- that.blockList[thisBlock].image = 'images/camera.svg';
- } else {
- that.blockList[thisBlock].image = null;
- }
- };
-
- postProcessArg = [thisBlock, null];
- } else if (name === 'video') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = VIDEOVALUE;
- if (value == null) {
- that.blockList[thisBlock].image = 'images/video.svg';
- } else {
- that.blockList[thisBlock].image = null;
- }
- };
-
- postProcessArg = [thisBlock, null];
- } else if (name === 'loadFile') {
- postProcess = function (args) {
- that.updateBlockText(args[0]);
- };
-
- postProcessArg = [thisBlock, null];
- } else if (['namedbox', 'nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg', 'namedarg'].indexOf(name) !== -1) {
- postProcess = function (args) {
- that.blockList[thisBlock].value = null;
- that.blockList[thisBlock].privateData = args[1];
- };
-
- postProcessArg = [thisBlock, arg];
- }
-
- var protoFound = false;
- for (var proto in that.protoBlockDict) {
- if (that.protoBlockDict[proto].name === name) {
- if (arg === '__NOARG__') {
- that.makeNewBlock(proto, postProcess, postProcessArg);
- protoFound = true;
- break;
- } else if (that.protoBlockDict[proto].defaults[0] === arg) {
- that.makeNewBlock(proto, postProcess, postProcessArg);
- protoFound = true;
- break;
- } else if (['namedbox', 'nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg', 'namedarg'].indexOf(name) !== -1) {
- if (that.protoBlockDict[proto].defaults[0] === undefined) {
- that.makeNewBlock(proto, postProcess, postProcessArg);
- protoFound = true;
- break;
- }
- }
- }
- }
-
- if (!protoFound) {
- console.log(name + ' not found!!');
- }
-
- var blk = this.blockList.length - 1;
- var myBlock = this.blockList[blk];
- for (var i = 0; i < myBlock.docks.length; i++) {
- myBlock.connections.push(null);
- }
-
- // Attach default args if any
- var cblk = blk + 1;
- for (var i = 0; i < myBlock.protoblock.defaults.length; i++) {
- var value = myBlock.protoblock.defaults[i];
-
- if (myBlock.name === 'action') {
- // Make sure we don't make two actions with the same name.
- value = this.findUniqueActionName(_('action'));
- if (value !== _('action')) {
- // TODO: are there return or arg blocks?
- this.newNameddoBlock(value, false, false);
- this.palettes.hide();
- this.palettes.updatePalettes('action');
- this.palettes.show();
- }
- }
-
- var that = this;
- var thisBlock = this.blockList.length;
- if (myBlock.docks.length > i && myBlock.docks[i + 1][2] === 'anyin') {
- if (value == null) {
- console.log('cannot set default value');
- } else if (typeof(value) === 'string') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- var label = value.toString();
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
- that.blockList[thisBlock].text.text = label;
- that.blockList[thisBlock].container.updateCache();
- };
-
- this.makeNewBlock('text', postProcess, [thisBlock, value]);
- } else {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = Number(args[1]);
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value.toString();
- };
-
- this.makeNewBlock('number', postProcess, [thisBlock, value]);
- }
- } else if (myBlock.docks[i + 1][2] === 'textin') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- var label = value.toString();
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
- that.blockList[thisBlock].text.text = label;
- };
-
- this.makeNewBlock('text', postProcess, [thisBlock, value]);
- } else if (myBlock.docks[i + 1][2] === 'solfegein') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- var label = value.toString();
- that.blockList[thisBlock].text.text = label;
- };
-
- this.makeNewBlock('solfege', postProcess, [thisBlock, value]);
- } else if (myBlock.docks[i + 1][2] === 'notein') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- var label = value.toString();
- that.blockList[thisBlock].text.text = label;
- };
-
- this.makeNewBlock('notename', postProcess, [thisBlock, value]);
- } else if (myBlock.docks[i + 1][2] === 'mediain') {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- if (value != null) {
- // loadThumbnail(that, thisBlock, null);
- }
- };
-
- this.makeNewBlock('media', postProcess, [thisBlock, value]);
- } else if (myBlock.docks[i + 1][2] === 'filein') {
- postProcess = function (blk) {
- that.updateBlockText(blk);
- }
- this.makeNewBlock('loadFile', postProcess, thisBlock);
- } else {
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.blockList[thisBlock].text.text = value.toString();
- };
-
- this.makeNewBlock('number', postProcess, [thisBlock, value]);
- }
-
- var myConnectionBlock = this.blockList[cblk + i];
- myConnectionBlock.connections = [blk];
- myConnectionBlock.value = value;
- myBlock.connections[i + 1] = cblk + i;
- }
-
- // Generate and position the block bitmaps and labels
- this.updateBlockPositions();
- // console.log('Adjust Docks: ' + this.blockList[blk].name);
- this.adjustDocks(blk, true);
- this.refreshCanvas();
-
- return blk;
- };
-
- this.findDragGroup = function (blk) {
- // Generate a drag group from blocks connected to blk
- this.dragLoopCounter = 0;
- this.dragGroup = [];
- this._calculateDragGroup(blk);
- };
-
- this._calculateDragGroup = function (blk) {
- // Give a block, find all the blocks connected to it
- this.dragLoopCounter += 1;
- if (this.dragLoopCount > this.blockList.length) {
- console.log('maximum loop counter exceeded in calculateDragGroup... this is bad. ' + blk);
- return;
- }
-
- if (blk == null) {
- console.log('null block passed to calculateDragGroup');
- return;
- }
-
- var myBlock = this.blockList[blk];
- // If this happens, something is really broken.
- if (myBlock == null) {
- console.log('null block encountered... this is bad. ' + blk);
- return;
- }
-
- // As before, does these ever happen?
- if (myBlock.connections == null) {
- this.dragGroup = [blk];
- return;
- }
-
- // Some malformed blocks might have no connections.
- if (myBlock.connections.length === 0) {
- this.dragGroup = [blk];
- return;
- }
-
- this.dragGroup.push(blk);
-
- for (var c = 1; c < myBlock.connections.length; c++) {
- var cblk = myBlock.connections[c];
- if (cblk != null) {
- // Recurse
- this._calculateDragGroup(cblk);
- }
- }
- };
-
- this.setActionProtoVisiblity = function (state) {
- // By default, the nameddo protoblock is hidden.
- var actionsPalette = this.palettes.dict['action'];
- var stateChanged = false;
- for (var blockId = 0; blockId < actionsPalette.protoList.length; blockId++) {
- var block = actionsPalette.protoList[blockId];
- if ('nameddo' === block.name && block.defaults.length === 0) {
- if (block.hidden === state) {
- block.hidden = !state;
- stateChanged = true;
- }
- }
- }
-
- // Force an update if the name has changed.
- if (stateChanged) {
- this.palettes.hide();
- this.palettes.updatePalettes('action');
- this.palettes.show();
- }
- };
-
- this.findUniqueActionName = function (name) {
- // If we have a stack named 'action', make the protoblock visible.
- if (name === _('action')) {
- this.setActionProtoVisiblity(true);
- }
-
- // Make sure we don't make two actions with the same name.
- var actionNames = [];
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if ((this.blockList[blk].name === 'text' || this.blockList[blk].name === 'string') && !this.blockList[blk].trash) {
- var c = this.blockList[blk].connections[0];
- if (c != null && this.blockList[c].name === 'action' && !this.blockList[c].trash) {
- actionNames.push(this.blockList[blk].value);
- }
- }
- }
-
- var i = 1;
- var value = name;
- while (actionNames.indexOf(value) !== -1) {
- value = name + i.toString();
- i += 1;
- }
- return value;
- };
-
- this._findDrumURLs = function () {
- // Make sure we initialize any drum with a URL name.
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (this.blockList[blk].name === 'text' || this.blockList[blk].name === 'string') {
- var c = this.blockList[blk].connections[0];
- if (c != null && ['playdrum', 'setdrum', 'setvoice'].indexOf(this.blockList[c].name) !== -1) {
- if (this.blockList[blk].value.slice(0, 4) === 'http') {
- if (_THIS_IS_MUSIC_BLOCKS_) {
- this.logo.synth.loadSynth(this.blockList[blk].value);
- }
- }
- }
- }
- }
- };
-
- this.renameBoxes = function (oldName, newName) {
- if (oldName === newName) {
- return;
- }
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (this.blockList[blk].name === 'text') {
- var c = this.blockList[blk].connections[0];
- if (c != null && this.blockList[c].name === 'box') {
- if (this.blockList[blk].value === oldName) {
- this.blockList[blk].value = newName;
- this.blockList[blk].text.text = newName;
- try {
- this.blockList[blk].container.updateCache();
- } catch (e) {
- console.log(e);
- }
- }
- }
- }
- }
- };
-
- this.renameNamedboxes = function (oldName, newName) {
- if (oldName === newName) {
- return;
- }
-
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (this.blockList[blk].name === 'namedbox') {
- if (this.blockList[blk].privateData === oldName) {
- this.blockList[blk].privateData = newName;
- this.blockList[blk].overrideName = newName;
- this.blockList[blk].regenerateArtwork();
- // Update label...
- try {
- this.blockList[blk].container.updateCache();
- } catch (e) {
- console.log(e);
- }
- }
- }
- }
-
- // Update the palette
- var blockPalette = this.palettes.dict['boxes'];
- var nameChanged = false;
- for (var blockId = 0; blockId < blockPalette.protoList.length; blockId++) {
- var block = blockPalette.protoList[blockId];
- if (block.name === 'namedbox' && block.defaults[0] !== _('box') && block.defaults[0] === oldName) {
- // console.log('renaming ' + block.defaults[0] + ' to ' + newName);
- block.defaults[0] = newName;
- nameChanged = true;
- }
- }
-
- // Force an update if the name has changed.
- if (nameChanged) {
- this.palettes.hide();
- this.palettes.updatePalettes('boxes');
- this.palettes.show();
- }
- };
-
- this.renameDos = function (oldName, newName, skipBlock) {
- if (oldName === newName) {
- return;
- }
-
- // Update the blocks, do->oldName should be do->newName
- // Named dos are modified in a separate function below.
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (blk === skipBlock) {
- continue;
- }
-
- var myBlock = this.blockList[blk];
- var blkParent = this.blockList[myBlock.connections[0]];
- if (blkParent == null) {
- continue;
- }
-
- if (['do', 'calc', 'doArg', 'calcArg', 'action'].indexOf(blkParent.name) === -1) {
- continue;
- }
-
- var blockValue = myBlock.value;
- if (blockValue === oldName) {
- myBlock.value = newName;
- var label = myBlock.value;
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
- myBlock.text.text = label;
- myBlock.container.updateCache();
- }
- }
- };
-
- this.renameNameddos = function (oldName, newName) {
- if (oldName === newName) {
- return;
- }
-
- // Update the blocks, do->oldName should be do->newName
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (['nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg'].indexOf(this.blockList[blk].name) !== -1) {
- if (this.blockList[blk].privateData === oldName) {
- this.blockList[blk].privateData = newName;
- var label = newName;
- if (label.length > 8) {
- label = label.substr(0, 7) + '...';
- }
-
- this.blockList[blk].overrideName = label;
- // console.log('regenerating artwork for ' + this.blockList[blk].name + ' block[' + blk + ']: ' + oldName + ' -> ' + label);
- this.blockList[blk].regenerateArtwork();
- }
- }
- }
-
- // Update the palette
- var actionsPalette = this.palettes.dict['action'];
- var nameChanged = false;
- for (var blockId = 0; blockId < actionsPalette.protoList.length; blockId++) {
- var block = actionsPalette.protoList[blockId];
- if (['nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg'].indexOf(block.name) !== -1 /* && block.defaults[0] !== _('action') */ && block.defaults[0] === oldName) {
- // console.log('renaming ' + block.name + ': ' + block.defaults[0] + ' to ' + newName);
- block.defaults[0] = newName;
- nameChanged = true;
- }
- }
-
- // Force an update if the name has changed.
- if (nameChanged) {
- this.palettes.hide();
- this.palettes.updatePalettes('action');
- this.palettes.show();
- }
- };
-
- this.newStoreinBlock = function (name) {
- if (name == null) {
- console.log('null name passed to newStoreinBlock');
- return;
- } else if (name == undefined) {
- console.log('undefined name passed to newStoreinBlock');
- return;
- } else if ('myStorein_' + name in this.protoBlockDict) {
- // console.log(name + ' already in palette');
- return;
- }
-
- // console.log('new storein block ' + name);
- var myStoreinBlock = new ProtoBlock('storein');
- this.protoBlockDict['myStorein_' + name] = myStoreinBlock;
- myStoreinBlock.palette = this.palettes.dict['boxes'];
- myStoreinBlock.defaults.push(name);
- myStoreinBlock.defaults.push(NUMBERBLOCKDEFAULT);
- myStoreinBlock.staticLabels.push(_('store in'), _('name'), _('value'));
- myStoreinBlock.adjustWidthToLabel();
- myStoreinBlock.twoArgBlock();
- myStoreinBlock.dockTypes[1] = 'anyin';
- myStoreinBlock.dockTypes[2] = 'anyin';
- if (name === 'box') {
- return;
- }
-
- // Add the new block to the top of the palette.
- myStoreinBlock.palette.add(myStoreinBlock, true);
- };
-
- this.newNamedboxBlock = function (name) {
- if (name == null) {
- console.log('null name passed to newNamedboxBlock');
- return;
- } else if (name == undefined) {
- console.log('undefined name passed to newNamedboxBlock');
- return;
- } else if ('myBox_' + name in this.protoBlockDict) {
- return;
- }
-
- var myBoxBlock = new ProtoBlock('namedbox');
- this.protoBlockDict['myBox_' + name] = myBoxBlock;
- myBoxBlock.palette = this.palettes.dict['boxes'];
- myBoxBlock.defaults.push(name);
- myBoxBlock.staticLabels.push(name);
- myBoxBlock.parameterBlock();
- if (name === 'box') {
- return;
- }
-
- // Add the new block to the top of the palette.
- myBoxBlock.palette.add(myBoxBlock, true);
- };
-
- this._newLocalArgBlock = function (name) {
- // name === 1, 2, 3, ...
- var blkname = 'arg_' + name;
- if ('myArg_' + name in this.protoBlockDict) {
- return;
- }
-
- if (blkname in this.protoBlockDict) {
- return;
- }
-
- var myNamedArgBlock = new ProtoBlock('namedarg');
- this.protoBlockDict['myArg_' + blkname] = myNamedArgBlock;
- myNamedArgBlock.palette = this.palettes.dict['action'];
- myNamedArgBlock.defaults.push(name);
- myNamedArgBlock.staticLabels.push('arg ' + name);
- myNamedArgBlock.parameterBlock();
-
- if (blkname === 'arg_1') {
- return;
- }
-
- myNamedArgBlock.palette.add(myNamedArgBlock, true);
-
- // Force regeneration of palette after adding new block.
- // Add delay to avoid race condition.
- var that = this;
- setTimeout(function () {
- that.palettes.hide();
- that.palettes.updatePalettes('action');
- that.palettes.show();
- }, 500);
- };
-
- this._removeNamedoEntries = function (name) {
- // Delete any old palette entries.
- // console.log('DELETE: removing old palette entries for ' + name);
- if (this.protoBlockDict['myDo_' + name]) {
- // console.log('deleting myDo_' + name + ' ' + this.protoBlockDict['myDo_' + name].name);
- this.protoBlockDict['myDo_' + name].hide = true;
- delete this.protoBlockDict['myDo_' + name];
- } else if (this.protoBlockDict['myCalc_' + name]) {
- // console.log('deleting myCalc_' + name + ' ' + this.protoBlockDict['myCalc_' + name].name);
- this.protoBlockDict['myCalc_' + name].hide = true;
- delete this.protoBlockDict['myCalc_' + name];
- } else if (this.protoBlockDict['myDoArg_' + name]) {
- // console.log('deleting myDoArg_' + name + ' ' + this.protoBlockDict['myDoArg_' + name].name);
- this.protoBlockDict['myDoArg_' + name].hide = true;
- delete this.protoBlockDict['myDoArg_' + name];
- } else if (this.protoBlockDict['myCalcArg_' + name]) {
- // console.log('deleting myCalcArg_' + name + ' ' + this.protoBlockDict['myCalcArg_' + name].name);
- this.protoBlockDict['myCalcArg_' + name].hide = true;
- delete this.protoBlockDict['myCalcArg_' + name];
- }
- };
-
- this.newNameddoBlock = function (name, hasReturn, hasArgs) {
- // Depending upon the form of the associated action block, we
- // want to add a named do, a named calc, a named do w/args, or
- // a named calc w/args.
- if (name === _('action')) {
- // 'action' already has its associated palette entries.
- return false;
- }
-
- if (hasReturn && hasArgs) {
- this.newNamedcalcArgBlock(name);
- return true;
- } else if (!hasReturn && hasArgs) {
- this.newNameddoArgBlock(name);
- return true;
- } else if (hasReturn && !hasArgs) {
- this.newNamedcalcBlock(name);
- return true;
- } else if (this.protoBlockDict['myDo_' + name] === undefined) {
- var myDoBlock = new ProtoBlock('nameddo');
- this.protoBlockDict['myDo_' + name] = myDoBlock;
- myDoBlock.palette = this.palettes.dict['action'];
- myDoBlock.defaults.push(name);
- myDoBlock.staticLabels.push(name);
- myDoBlock.zeroArgBlock();
- myDoBlock.palette.add(myDoBlock, true);
- this.palettes.updatePalettes();
- return true;
- }
-
- return false;
- };
-
- this.newNamedcalcBlock = function (name) {
- if (this.protoBlockDict['myCalc_' + name] === undefined) {
- // console.log('creating myCalc_' + name);
- var myCalcBlock = new ProtoBlock('namedcalc');
- this.protoBlockDict['myCalc_' + name] = myCalcBlock;
- myCalcBlock.palette = this.palettes.dict['action'];
- myCalcBlock.defaults.push(name);
- myCalcBlock.staticLabels.push(name);
- myCalcBlock.zeroArgBlock();
- // Add the new block to the top of the palette.
- myCalcBlock.palette.add(myCalcBlock, true);
- // } else {
- // console.log('myCalc_' + name + ' already exists.');
- }
- };
-
- this.newNameddoArgBlock = function (name) {
- if (this.protoBlockDict['myDoArg_' + name] === undefined) {
- // console.log('creating myDoArg_' + name);
- var myDoArgBlock = new ProtoBlock('nameddoArg');
- this.protoBlockDict['myDoArg_' + name] = myDoArgBlock;
- myDoArgBlock.palette = this.palettes.dict['action'];
- myDoArgBlock.defaults.push(name);
- myDoArgBlock.staticLabels.push(name);
- myDoArgBlock.zeroArgBlock();
- // Add the new block to the top of the palette.
- myDoArgBlock.palette.add(myDoArgBlock, true);
- // } else {
- // console.log('myDoArg_' + name + ' already exists.');
- }
- };
-
- this.newNamedcalcArgBlock = function (name) {
- if (this.protoBlockDict['myCalcArg_' + name] === undefined) {
- // console.log('creating myCalcArg_' + name);
- var myCalcArgBlock = new ProtoBlock('namedcalcArg');
- this.protoBlockDict['myCalcArg_' + name] = myCalcArgBlock;
- myCalcArgBlock.palette = this.palettes.dict['action'];
- myCalcArgBlock.defaults.push(name);
- myCalcArgBlock.staticLabels.push(name);
- myCalcArgBlock.zeroArgBlock();
- // Add the new block to the top of the palette.
- myCalcArgBlock.palette.add(myCalcArgBlock, true);
- // } else {
- // console.log('myCalcArg_' + name + ' already exists.');
- }
- };
-
- this._insideArgClamp = function (blk) {
- // Returns a containing arg clamp block or null
- if (this.blockList[blk] == null) {
- // race condition?
- console.log('null block in blockList? ' + blk);
- return null;
- } else if (this.blockList[blk].connections[0] == null) {
- return null;
- } else {
- var cblk = this.blockList[blk].connections[0];
- if (this.blockList[cblk].isArgClamp()) {
- return cblk;
- } else {
- return null;
- }
- }
- };
-
- this._insideExpandableBlock = function (blk) {
- // Returns a containing expandable block or null
- if (this.blockList[blk] == null) {
- // race condition?
- console.log('null block in blockList? ' + blk);
- return null;
- } else if (this.blockList[blk].connections[0] == null) {
- return null;
- } else {
- var cblk = this.blockList[blk].connections[0];
- if (this.blockList[cblk].isExpandableBlock()) {
- // If it is the last connection, keep searching.
- if (this.blockList[cblk].isArgFlowClampBlock()) {
- return cblk;
- } else if (blk === last(this.blockList[cblk].connections)) {
- return this._insideExpandableBlock(cblk);
- } else {
- return cblk;
- }
- } else {
- return this._insideExpandableBlock(cblk);
- }
- }
- };
-
- this._insideNoteBlock = function (blk) {
- // Returns a containing note block or null
- if (this.blockList[blk] == null) {
- console.log('null block in blockList? ' + blk);
- return null;
- } else if (this.blockList[blk].connections[0] == null) {
- return null;
- } else {
- var cblk = this.blockList[blk].connections[0];
- if (this.blockList[cblk].isExpandableBlock()) {
- // If it is the last connection, keep searching.
- if (blk === last(this.blockList[cblk].connections)) {
- return this._insideNoteBlock(cblk);
- } else if (blk === this.blockList[cblk].connections[1]) {
- // Connection 1 of a note block is not inside the clamp.
- return null;
- } else {
- if (['newnote', 'osctime'].indexOf(this.blockList[cblk].name) !== -1) {
- return cblk;
- } else {
- return null;
- }
- }
- } else {
- return this._insideNoteBlock(cblk);
- }
- }
- };
-
- this.triggerLongPress = function (myBlock) {
- this.timeOut == null;
- this.inLongPress = true;
- var z = this.stage.getNumChildren() - 1;
-
- // Auto-select stack for copying -- no need to actually click on
- // the copy button.
- var topBlock = this.findTopBlock(this.activeBlock);
- this.selectedStack = topBlock;
-
- // Copy the selectedStack.
- this.selectedBlocksObj = JSON.parse(JSON.stringify(this._copyBlocksToObj()));
-
- this.updatePasteButton();
-
- if (myBlock.name === 'action') {
- this.dismissButton.visible = true;
- this.dismissButton.x = myBlock.container.x - 27;
- this.dismissButton.y = myBlock.container.y - 27;
- this.stage.setChildIndex(this.dismissButton, z - 1);
- this.saveStackButton.visible = true;
- this.saveStackButton.x = myBlock.container.x + 27;
- this.saveStackButton.y = myBlock.container.y - 27;
- this.stage.setChildIndex(this.saveStackButton, z - 2);
- }
-
- this.refreshCanvas();
- };
-
- this.pasteStack = function () {
- // Copy a stack of blocks by creating a blockObjs and passing
- // it to this.load.
- if (this.selectedStack == null) {
- return;
- }
-
- // First, hide the palettes as they will need updating.
- for (var name in this.palettes.dict) {
- this.palettes.dict[name].hideMenu(true);
- }
-
- // var blockObjs = this._copyBlocksToObj();
- // this.loadNewBlocks(blockObjs);
- // console.log(this.selectedBlocksObj);
- this.loadNewBlocks(this.selectedBlocksObj);
- };
-
- this.saveStack = function () {
- // Save a stack of blocks to local storage and the 'myblocks'
- // palette by creating a blockObjs and ...
- if (this.selectedStack == null) {
- return;
- }
-
- this.palettes.hide();
-
- var blockObjs = this._copyBlocksToObj();
- // The first block is an action block. Its first connection is
- // the block containing its label.
- var nameBlk = blockObjs[0][4][1];
-
- if (nameBlk == null) {
- console.log('action not named... skipping');
- } else {
- console.log(blockObjs[nameBlk][1][1]);
- if (typeof(blockObjs[nameBlk][1][1]) === 'string') {
- var name = blockObjs[nameBlk][1][1];
- } else if (typeof(blockObjs[nameBlk][1][1]) === 'number') {
- var name = blockObjs[nameBlk][1][1].toString();
- } else {
- var name = blockObjs[nameBlk][1][1]['value'];
- }
- storage.macros = prepareMacroExports(name, blockObjs, this.macroDict);
- if (sugarizerCompatibility.isInsideSugarizer()) {
- sugarizerCompatibility.saveLocally(function () {
- this.addToMyPalette(name, blockObjs);
- // this.palettes.updatePalettes('myblocks');
- });
- } else {
- this.addToMyPalette(name, blockObjs);
- // this.palettes.updatePalettes('myblocks');
- }
- }
- };
-
- this._copyBlocksToObj = function () {
- var blockObjs = [];
- var blockMap = {};
-
- this.findDragGroup(this.selectedStack);
- for (var b = 0; b < this.dragGroup.length; b++) {
- myBlock = this.blockList[this.dragGroup[b]];
- if (b === 0) {
- x = 75 - this.stage.x;
- y = 75 - this.stage.y;
- } else {
- x = 0;
- y = 0;
- }
-
- if (myBlock.isValueBlock()) {
- switch (myBlock.name) {
- case 'media':
- blockItem = [b, [myBlock.name, null], x, y, []];
- break;
- default:
- blockItem = [b, [myBlock.name, myBlock.value], x, y, []];
- break;
- }
- } else if (['namedbox', 'nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg', 'namedarg'].indexOf(myBlock.name) !== -1) {
- blockItem = [b, [myBlock.name, {'value': myBlock.privateData}], x, y, []];
- } else {
- blockItem = [b, myBlock.name, x, y, []];
- }
-
- blockMap[this.dragGroup[b]] = b;
- blockObjs.push(blockItem);
- }
- for (var b = 0; b < this.dragGroup.length; b++) {
- myBlock = this.blockList[this.dragGroup[b]];
- for (var c = 0; c < myBlock.connections.length; c++) {
- if (myBlock.connections[c] == null) {
- blockObjs[b][4].push(null);
- } else {
- blockObjs[b][4].push(blockMap[myBlock.connections[c]]);
- }
- }
- }
-
- return blockObjs;
- };
-
- this.addToMyPalette = function (name, obj) {
- // On the palette we store the macro as a basic block.
- var myBlock = new ProtoBlock('macro_' + name);
- var blkName = 'macro_' + name;
- this.protoBlockDict[blkName] = myBlock;
- if (!('myblocks' in this.palettes.dict)) {
- this.palettes.add('myblocks');
- }
-
- myBlock.palette = this.palettes.dict['myblocks'];
- myBlock.zeroArgBlock();
- myBlock.staticLabels.push(_(name));
- this.protoBlockDict[blkName].palette.add(this.protoBlockDict[blkName]);
- };
-
- this.loadNewBlocks = function (blockObjs) {
- // Check for blocks connected to themselves,
- // and for action blocks not connected to text blocks.
- for (var b = 0; b < blockObjs.length; b++) {
- var blkData = blockObjs[b];
- for (var c in blkData[4]) {
- if (blkData[4][c] === blkData[0]) {
- console.log('Circular connection in block data: ' + blkData);
- console.log('Punting loading of new blocks!');
- console.log(blockObjs);
- return;
- }
- }
- }
-
- // We'll need a list of existing storein and action names.
- var currentActionNames = [];
- var currentStoreinNames = [];
- for (var b = 0; b < this.blockList.length; b++) {
- if (this.blockList[b].trash) {
- continue;
- }
-
- if (this.blockList[b].name === 'action') {
- if (this.blockList[b].connections[1] != null) {
- console.log(this.blockList[this.blockList[b].connections[1]].value);
- currentActionNames.push(this.blockList[this.blockList[b].connections[1]].value);
- }
- } else if (this.blockList[b].name === 'storein') {
- if (this.blockList[b].connections[1] != null) {
- currentStoreinNames.push(this.blockList[this.blockList[b].connections[1]].value);
- }
- }
- }
-
- // We need to track two-arg blocks in case they need expanding.
- this._checkTwoArgBlocks = [];
-
- // And arg clamp blocks in case they need expanding.
- this._checkArgClampBlocks = [];
-
- // Don't make duplicate action names.
- // Add a palette entry for any new storein blocks.
- var stringNames = [];
- var stringValues = {}; // label: [blocks with that label]
- var actionNames = {}; // action block: label block
- var storeinNames = {}; // storein block: label block
- var doNames = {}; // do block: label block, nameddo block value
-
- // action and start blocks that need to be collapsed.
- this.blocksToCollapse = [];
-
- // Scan for any new action and storein blocks to identify
- // duplicates. We also need to track start and action blocks
- // that may need to be collapsed.
- for (var b = 0; b < blockObjs.length; b++) {
- var blkData = blockObjs[b];
- // blkData[1] could be a string or an object.
- if (typeof(blkData[1]) === 'string') {
- var name = blkData[1];
- } else {
- var name = blkData[1][0];
- }
-
- if (!(name in this.protoBlockDict)) {
- switch (name) {
- case 'hat':
- name = 'action';
- break;
- case 'string':
- name = 'text';
- break;
- default:
- console.log('skipping ' + name);
- continue;
- break;
- }
- }
-
- if (['arg', 'twoarg'].indexOf(this.protoBlockDict[name].style) !== -1) {
- if (this.protoBlockDict[name].expandable) {
- this._checkTwoArgBlocks.push(this.blockList.length + b);
- }
- }
-
- // FIXME: Use tests in block.js
- if (['clamp', 'argclamp', 'argclamparg', 'doubleclamp', 'argflowclamp'].indexOf(this.protoBlockDict[name].style) !== -1) {
- this._checkArgClampBlocks.push(this.blockList.length + b);
- }
-
- switch (name) {
- case 'text':
- var key = blkData[1][1];
- if (stringValues[key] === undefined) {
- stringValues[key] = [];
- }
- stringValues[key].push(b);
- break;
- case 'action':
- case 'hat':
- if (blkData[4][1] != null) {
- actionNames[b] = blkData[4][1];
- }
- break;
- case 'storein':
- if (blkData[4][1] != null) {
- storeinNames[b] = blkData[4][1];
- }
- break;
- case 'nameddo':
- case 'namedcalc':
- case 'nameddoArg':
- case 'namedcalcArg':
- doNames[b] = blkData[1][1]['value'];
- break;
- case 'do':
- case 'stack':
- if (blkData[4][1] != null) {
- doNames[b] = blkData[4][1];
- }
- break;
- default:
- break;
- }
-
- switch (name) {
- case 'action':
- case 'pitchdrummatrix':
- case 'rhythmruler':
- case 'pitchstaircase':
- case 'tempo':
- case 'pitchslider':
- case 'matrix':
- case 'drum':
- case 'status':
- case 'start':
- if (typeof(blkData[1]) === 'object' && blkData[1].length > 1 && typeof(blkData[1][1]) === 'object' && 'collapsed' in blkData[1][1]) {
- if (blkData[1][1]['collapsed']) {
- this.blocksToCollapse.push(this.blockList.length + b);
- }
- }
- break;
- default:
- break;
- }
- }
-
- var updatePalettes = false;
- // Make sure new storein names have palette entries.
- for (var b in storeinNames) {
- var blkData = blockObjs[storeinNames[b]];
- if (currentStoreinNames.indexOf(blkData[1][1]) === -1) {
- if (typeof(blkData[1][1]) === 'string') {
- var name = blkData[1][1];
- } else {
- var name = blkData[1][1]['value'];
- }
- // console.log('Adding new palette entries for store-in ' + name);
- this.newStoreinBlock(name);
- this.newNamedboxBlock(name);
- updatePalettes = true;
- }
- }
-
- // Make sure action names are unique.
- for (var b in actionNames) {
- // Is there a proto do block with this name? If so, find a
- // new name.
- // Name = the value of the connected label.
- var blkData = blockObjs[actionNames[b]];
- if (typeof(blkData[1][1]) === 'string') {
- var name = blkData[1][1];
- } else {
- var name = blkData[1][1]['value'];
- }
-
- // If we have a stack named 'action', make the protoblock visible.
- if (name === _('action')) {
- this.setActionProtoVisiblity(true);
- }
- var oldName = name;
- var i = 1;
- while (currentActionNames.indexOf(name) !== -1) {
- name = oldName + i.toString();
- i += 1;
- // Should never happen... but just in case.
- if (i > this.blockList.length) {
- console.log('Could not generate unique action name.');
- break;
- }
- }
-
- if (oldName !== name) {
- // Change the name of the action...
- console.log('action ' + oldName + ' is being renamed ' + name);
- blkData[1][1] = {'value': name};
- }
-
- // and any do blocks
- for (var d in doNames) {
- var thisBlkData = blockObjs[d];
- if (typeof(thisBlkData[1]) === 'string') {
- var blkName = thisBlkData[1];
- } else {
- var blkName = thisBlkData[1][0];
- }
- if (['nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg'].indexOf(blkName) !== -1) {
- if (thisBlkData[1][1]['value'] === oldName) {
- thisBlkData[1][1] = {'value': name};
- }
- } else {
- var doBlkData = blockObjs[doNames[d]];
- if (typeof(doBlkData[1][1]) === 'string') {
- if (doBlkData[1][1] === oldName) {
- // console.log('renaming ' + oldName + ' to ' + name);
- doBlkData[1][1] = name;
- }
- } else {
- if (doBlkData[1][1]['value'] === oldName) {
- // console.log('renaming ' + oldName + ' to ' + name);
- doBlkData[1][1] = {'value': name};
- }
- }
- }
- }
- }
-
- if (updatePalettes) {
- this.palettes.hide();
- this.palettes.updatePalettes('action');
- this.palettes.show();
- }
-
-
- // This section of the code attempts to repair imported
- // code. For example, it adds missing hidden blocks and
- // convert old-style notes to new-style notes.
- blockObjsLength = blockObjs.length;
- var extraBlocksLength = 0;
-
- for (var b = 0; b < blockObjsLength; b++) {
- if (typeof(blockObjs[b][1]) === 'object') {
- var name = blockObjs[b][1][0];
- } else {
- var name = blockObjs[b][1];
- }
-
- switch (name) {
- case 'articulation':
- case 'augmented':
- case 'backward':
- case 'crescendo':
- case 'diminished':
- case 'dividebeatfactor':
- case 'drift':
- case 'duplicatenotes':
- case 'invert1':
- case 'fill':
- case 'flat':
- case 'hollowline':
- case 'major':
- case 'minor':
- case 'multiplybeatfactor':
- case 'note':
- case 'newnote':
- case 'newslur':
- case 'newstaccato':
- case 'newswing':
- case 'newswing2':
- case 'osctime':
- case 'perfect':
- case 'pluck':
- case 'rhythmicdot':
- case 'setbpm':
- case 'setnotevolume2':
- case 'settransposition':
- case 'setvoice':
- case 'sharp':
- case 'skipnotes':
- case 'slur':
- case 'staccato':
- case 'swing':
- case 'tie':
- case 'tuplet2':
- case 'vibrato':
- var len = blockObjs[b][4].length;
- if (last(blockObjs[b][4]) == null) {
- // If there is no next block, add a hidden block;
- console.log('last connection of ' + name + ' is null: adding hidden block');
- blockObjs[b][4][len - 1] = blockObjsLength + extraBlocksLength;
- blockObjs.push([blockObjsLength + extraBlocksLength, 'hidden', 0, 0, [b, null]]);
- extraBlocksLength += 1;
- } else {
- var nextBlock = blockObjs[b][4][len - 1];
-
- if (typeof(blockObjs[nextBlock][1]) === 'object') {
- var nextName = blockObjs[nextBlock][1][0];
- } else {
- var nextName = blockObjs[nextBlock][1];
- }
-
- if (nextName !== 'hidden') {
- console.log('last connection of ' + name + ' is ' + nextName + ': adding hidden block');
- // If the next block is not a hidden block, add one.
- blockObjs[b][4][len - 1] = blockObjsLength + extraBlocksLength;
- blockObjs[nextBlock][4][0] = blockObjsLength + extraBlocksLength;
- blockObjs.push([blockObjsLength + extraBlocksLength, 'hidden', 0, 0, [b, nextBlock]]);
- extraBlocksLength += 1;
- }
- }
-
- if (['note', 'slur', 'staccato', 'swing'].indexOf(name) !== -1) {
- // We need to convert to newnote style:
- // (1) add a vspace to the start of the clamp of a note block.
- console.log('note: ' + b);
- var clampBlock = blockObjs[b][4][2];
- blockObjs[b][4][2] = blockObjsLength + extraBlocksLength;
- if (clampBlock == null) {
- blockObjs.push([blockObjsLength + extraBlocksLength, 'vspace', 0, 0, [b, null]]);
- } else {
- blockObjs[clampBlock][4][0] = blockObjsLength + extraBlocksLength;
- blockObjs.push([blockObjsLength + extraBlocksLength, 'vspace', 0, 0, [b, clampBlock]]);
- }
-
- extraBlocksLength += 1;
-
- // (2) switch the first connection to divide 1 / arg.
- var argBlock = blockObjs[b][4][1];
- blockObjs[b][4][1] = blockObjsLength + extraBlocksLength;
- if (argBlock == null) {
- blockObjs.push([blockObjsLength + extraBlocksLength, 'divide', 0, 0, [b, blockObjsLength + extraBlocksLength + 1, blockObjsLength + extraBlocksLength + 2]]);
- blockObjs.push([blockObjsLength + extraBlocksLength + 1, ['number', {'value': 1}], 0, 0, [blockObjsLength + extraBlocksLength]]);
- blockObjs.push([blockObjsLength + extraBlocksLength + 2, ['number', {'value': 1}], 0, 0, [blockObjsLength + extraBlocksLength]]);
- extraBlocksLength += 3;
- } else {
- blockObjs[argBlock][4][0] = blockObjsLength + extraBlocksLength;
- blockObjs.push([blockObjsLength + extraBlocksLength, 'divide', 0, 0, [b, blockObjsLength + extraBlocksLength + 1, argBlock]]);
- blockObjs.push([blockObjsLength + extraBlocksLength + 1, ['number', {'value': 1}], 0, 0, [blockObjsLength + extraBlocksLength]]);
- extraBlocksLength += 2;
- }
-
- // (3) create a newnote block instead.
- if (typeof(blockObjs[b][1]) === 'object') {
- blockObjs[b][1][0] = 'new' + name;
- } else {
- blockObjs[b][1] = 'new' + name;
- }
- }
- break;
- case 'action':
- // Ensure that there is a hidden block as the first
- // block in the child flow (connection 2) of an action
- // block (required to make the backward block function
- // propperly).
- var len = blockObjs[b][4].length;
- if (blockObjs[b][4][2] == null) {
- // If there is no child flow block, add a hidden block;
- console.log('last connection of ' + name + ' is null: adding hidden block');
- blockObjs[b][4][2] = blockObjsLength + extraBlocksLength;
- blockObjs.push([blockObjsLength + extraBlocksLength, 'hidden', 0, 0, [b, null]]);
- extraBlocksLength += 1;
- } else {
- var nextBlock = blockObjs[b][4][2];
-
- if (typeof(blockObjs[nextBlock][1]) === 'object') {
- var nextName = blockObjs[nextBlock][1][0];
- } else {
- var nextName = blockObjs[nextBlock][1];
- }
-
- if (nextName !== 'hidden') {
- console.log('last connection of ' + name + ' is ' + nextName + ': adding hidden block');
- // If the next block is not a hidden block, add one.
- blockObjs[b][4][2] = blockObjsLength + extraBlocksLength;
- blockObjs[nextBlock][4][0] = blockObjsLength + extraBlocksLength;
- blockObjs.push([blockObjsLength + extraBlocksLength, 'hidden', 0, 0, [b, nextBlock]]);
- extraBlocksLength += 1;
- }
- }
- break;
- default:
- break;
- }
- }
-
- // Append to the current set of blocks.
- this._adjustTheseStacks = [];
- this._adjustTheseDocks = [];
- this._loadCounter = blockObjs.length;
-
- // We add new blocks to the end of the block list.
- var blockOffset = this.blockList.length;
- var firstBlock = this.blockList.length;
-
- for (var b = 0; b < this._loadCounter; b++) {
- var thisBlock = blockOffset + b;
- var blkData = blockObjs[b];
-
- if (typeof(blkData[1]) === 'object') {
- if (blkData[1].length === 1) {
- var blkInfo = [blkData[1][0], {'value': null}];
- } else if (['number', 'string'].indexOf(typeof(blkData[1][1])) !== -1) {
- var blkInfo = [blkData[1][0], {'value': blkData[1][1]}];
- if (COLLAPSABLES.indexOf(blkData[1][0]) !== -1) {
- blkInfo[1]['collapsed'] = false;
- }
- } else {
- var blkInfo = blkData[1];
- }
- } else {
- var blkInfo = [blkData[1], {'value': null}];
- if (COLLAPSABLES.indexOf(blkData[1]) !== -1) {
- blkInfo[1]['collapsed'] = false;
- }
- }
-
- var name = blkInfo[0];
-
- var collapsed = false;
- if (COLLAPSABLES.indexOf(name) !== -1) {
- collapsed = blkInfo[1]['collapsed'];
- }
-
- if (blkInfo[1] == null) {
- var value = null;
- } else {
- var value = blkInfo[1]['value'];
- }
-
- if (name in NAMEDICT) {
- name = NAMEDICT[name];
- }
-
- var that = this;
-
- // A few special cases.
- switch (name) {
- // Only add 'collapsed' arg to start, action blocks.
- case 'start':
- blkData[4][0] = null;
- blkData[4][2] = null;
- postProcess = function (args) {
- var thisBlock = args[0];
- var blkInfo = args[1];
- that.blockList[thisBlock].value = that.turtles.turtleList.length;
- that.turtles.addTurtle(that.blockList[thisBlock], blkInfo);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, blkInfo[1]], collapsed);
- break;
- case 'drum':
- blkData[4][0] = null;
- blkData[4][2] = null;
- postProcess = function (args) {
- var thisBlock = args[0];
- var blkInfo = args[1];
- that.blockList[thisBlock].value = that.turtles.turtleList.length;
- that.turtles.addDrum(that.blockList[thisBlock], blkInfo);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, blkInfo[1]], collapsed);
-
- if (_THIS_IS_MUSIC_BLOCKS_) {
- // Load the synth for this drum
- this.logo.synth.loadSynth('kick');
- }
- break;
- case 'action':
- case 'hat':
- blkData[4][0] = null;
- blkData[4][3] = null;
- this._makeNewBlockWithConnections('action', blockOffset, blkData[4], null, null, collapsed);
- break;
-
- // Named boxes and dos need private data.
- case 'namedbox':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].privateData = value;
- that.blockList[thisBlock].value = null;
- };
-
- this._makeNewBlockWithConnections('namedbox', blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'namedarg':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].privateData = value;
- that.blockList[thisBlock].value = null;
- };
-
- this._makeNewBlockWithConnections('namedarg', blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'namedcalc':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].privateData = value;
- that.blockList[thisBlock].value = null;
- };
-
- this._makeNewBlockWithConnections('namedcalc', blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'nameddo':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].privateData = value;
- that.blockList[thisBlock].value = null;
- };
-
- this._makeNewBlockWithConnections('nameddo', blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
-
- // Arg clamps may need extra slots added.
- case 'doArg':
- postProcess = function (args) {
- var thisBlock = args[0];
- var extraSlots = args[1].length - 4;
- if (extraSlots > 0) {
- var slotList = that.blockList[thisBlock].argClampSlots;
- for (var i = 0; i < extraSlots; i++) {
- slotList.push(1);
- that._newLocalArgBlock(slotList.length);
- that.blockList[thisBlock].connections.push(null);
- }
- that.blockList[thisBlock].updateArgSlots(slotList);
- for (var i = 0; i < args[1].length; i++) {
- if (args[1][i] != null) {
- that.blockList[thisBlock].connections[i] = args[1][i] + firstBlock;
- } else {
- that.blockList[thisBlock].connections[i] = args[1][i];
- }
- }
- }
- that._checkArgClampBlocks.push(thisBlock);
- };
-
- this._makeNewBlockWithConnections('doArg', blockOffset, blkData[4], postProcess, [thisBlock, blkData[4]]);
- break;
- case 'nameddoArg':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].privateData = value;
- that.blockList[thisBlock].value = null;
- var extraSlots = args[2].length - 3;
- if (extraSlots > 0) {
- var slotList = that.blockList[thisBlock].argClampSlots;
- for (var i = 0; i < extraSlots; i++) {
- slotList.push(1);
- that._newLocalArgBlock(slotList.length);
- that.blockList[thisBlock].connections.push(null);
- }
- that.blockList[thisBlock].updateArgSlots(slotList);
- for (var i = 0; i < args[2].length; i++) {
- if (args[2][i] != null) {
- that.blockList[thisBlock].connections[i] = args[2][i] + firstBlock;
- } else {
- that.blockList[thisBlock].connections[i] = args[2][i];
- }
- }
- }
- that._checkArgClampBlocks.push(thisBlock);
- };
-
- this._makeNewBlockWithConnections('nameddoArg', blockOffset, blkData[4], postProcess, [thisBlock, value, blkData[4]]);
- break;
- case 'calcArg':
- postProcess = function (args) {
- var thisBlock = args[0];
- var extraSlots = args[1].length - 3;
- if (extraSlots > 0) {
- var slotList = that.blockList[thisBlock].argClampSlots;
- for (var i = 0; i < extraSlots; i++) {
- slotList.push(1);
- that._newLocalArgBlock(slotList.length);
- that.blockList[thisBlock].connections.push(null);
- }
- that.blockList[thisBlock].updateArgSlots(slotList);
- for (var i = 0; i < args[1].length; i++) {
- if (args[1][i] != null) {
- that.blockList[thisBlock].connections[i] = args[1][i] + firstBlock;
- } else {
- that.blockList[thisBlock].connections[i] = args[1][i];
- }
- }
- }
- that._checkArgClampBlocks.push(thisBlock);
- };
-
- this._makeNewBlockWithConnections('calcArg', blockOffset, blkData[4], postProcess, [thisBlock, blkData[4]]);
- break;
- case 'namedcalcArg':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].privateData = value;
- that.blockList[thisBlock].value = null;
- var extraSlots = args[2].length - 2;
- if (extraSlots > 0) {
- var slotList = that.blockList[thisBlock].argClampSlots;
- for (var i = 0; i < extraSlots; i++) {
- slotList.push(1);
- that._newLocalArgBlock(slotList.length);
- that.blockList[thisBlock].connections.push(null);
- }
- that.blockList[thisBlock].updateArgSlots(slotList);
- for (var i = 0; i < args[2].length; i++) {
- if (args[2][i] != null) {
- that.blockList[thisBlock].connections[i] = args[2][i] + firstBlock;
- } else {
- that.blockList[thisBlock].connections[i] = args[2][i];
- }
- }
- }
- that._checkArgClampBlocks.push(thisBlock);
- };
-
- this._makeNewBlockWithConnections('namedcalcArg', blockOffset, blkData[4], postProcess, [thisBlock, value, blkData[4]]);
- break;
-
- // Value blocks need a default value set.
- case 'number':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = Number(value);
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'text':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'solfege':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'eastindiansolfege':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'notename':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.updateBlockText(thisBlock);
- };
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'modename':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.updateBlockText(thisBlock);
- };
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'drumname':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
-
- if (_THIS_IS_MUSIC_BLOCKS_) {
- // Load the synth for this drum
- this.logo.synth.loadSynth(getDrumSynthName(value));
- }
- break;
- case 'voicename':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
-
- if (_THIS_IS_MUSIC_BLOCKS_) {
- // Load the synth for this voice
- this.logo.synth.loadSynth(getVoiceSynthName(value));
- }
- break;
- case 'media':
- // Load a thumbnail into a media blocks.
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = value;
- if (value != null) {
- // Load artwork onto media block.
- that.blockList[thisBlock].loadThumbnail(null);
- }
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'camera':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = CAMERAVALUE;
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- case 'video':
- postProcess = function (args) {
- var thisBlock = args[0];
- var value = args[1];
- that.blockList[thisBlock].value = VIDEOVALUE;
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
-
- // Define some constants for legacy blocks for
- // backward compatibility with Python projects.
- case 'red':
- case 'black':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = 0;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'white':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = 100;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'orange':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = 10;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'yellow':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = 20;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'green':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = 40;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'blue':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = 70;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'leftpos':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = -(canvas.width / 2);
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'rightpos':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = (canvas.width / 2);
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'toppos':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = (canvas.height / 2);
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'botpos':
- case 'bottompos':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = -(canvas.height / 2);
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'width':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = canvas.width;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'height':
- postProcess = function (thisBlock) {
- that.blockList[thisBlock].value = canvas.height;
- that.updateBlockText(thisBlock);
- };
-
- this._makeNewBlockWithConnections('number', blockOffset, blkData[4], postProcess, thisBlock);
- break;
- case 'loadFile':
- postProcess = function (args) {
- that.blockList[args[0]].value = args[1];
- that.updateBlockText(args[0]);
- };
-
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], postProcess, [thisBlock, value]);
- break;
- default:
- // Check that name is in the proto list
- if (!name in this.protoBlockDict || this.protoBlockDict[name] == null) {
- // Lots of assumptions here.
- // TODO: figure out if it is a flow or an arg block.
- // Substitute a NOP block for an unknown block.
- n = blkData[4].length;
- console.log(n + ': substituting nop block for ' + name);
- switch (n) {
- case 1:
- name = 'nopValueBlock';
- break;
- case 2:
- name = 'nopZeroArgBlock';
- break;
- case 3:
- name = 'nopOneArgBlock';
- break;
- case 4:
- name = 'nopTwoArgBlock';
- break;
- case 5:
- default:
- name = 'nopThreeArgBlock';
- break;
- }
- }
- this._makeNewBlockWithConnections(name, blockOffset, blkData[4], null);
- break;
- }
-
- if (thisBlock === this.blockList.length - 1) {
- if (this.blockList[thisBlock].connections[0] == null) {
- this.blockList[thisBlock].container.x = blkData[2];
- this.blockList[thisBlock].container.y = blkData[3];
- this._adjustTheseDocks.push(thisBlock);
- if (blkData[4][0] == null) {
- this._adjustTheseStacks.push(thisBlock);
- }
- if (blkData[2] < 0 || blkData[3] < 0 || blkData[2] > canvas.width || blkData[3] > canvas.height) {
- this._homeButtonContainers[0].visible = true;
- this._homeButtonContainers[1].visible = false;
- }
- }
- }
- }
- };
-
- this.cleanupAfterLoad = function (name) {
- // If all the blocks are loaded, we can make the final adjustments.
- this._loadCounter -= 1;
- if (this._loadCounter > 0) {
- return;
- }
-
- this._findDrumURLs();
-
- this.updateBlockPositions();
-
- this._cleanupStacks();
-
- for (var i = 0; i < this.blocksToCollapse.length; i++) {
- this.blockList[this.blocksToCollapse[i]].collapseToggle();
- }
-
- this.blocksToCollapse = [];
-
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (this.blockList[blk].collapseContainer != null) {
- this.blockList[blk].collapseContainer.x = this.blockList[blk].container.x + COLLAPSEBUTTONXOFF * (this.blockList[blk].protoblock.scale / 2);
- this.blockList[blk].collapseContainer.y = this.blockList[blk].container.y + COLLAPSEBUTTONYOFF * (this.blockList[blk].protoblock.scale / 2);
- }
- }
-
- this.refreshCanvas();
-
- // Do a final check on the action and boxes palettes.
- var updatePalettes = false;
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (!this.blockList[blk].trash && this.blockList[blk].name === 'action') {
- var myBlock = this.blockList[blk];
- var arg = null;
- var c = myBlock.connections[1];
- if (c != null && this.blockList[c].value !== _('action')) {
- if (this.newNameddoBlock(this.blockList[c].value, this.actionHasReturn(blk), this.actionHasArgs(blk))) {
- updatePalettes = true;
- }
- }
- }
- }
- if (updatePalettes) {
- this.palettes.hide();
- this.palettes.updatePalettes('action');
- // this.palettes.dict['action'].hide();
- this.palettes.show();
- }
-
- var updatePalettes = false;
- for (var blk = 0; blk < this.blockList.length; blk++) {
- if (!this.blockList[blk].trash && this.blockList[blk].name === 'storein') {
- var myBlock = this.blockList[blk];
- var arg = null;
- var c = myBlock.connections[1];
- if (c != null && this.blockList[c].value !== _('box')) {
- var name = this.blockList[c].value;
- // Is there an old block with this name still around?
- if (this.protoBlockDict['myStorein_' + name] == undefined) {
- console.log('adding new storein block ' + name);
- this.newNamedboxBlock(this.blockList[c].value);
- this.newStoreinBlock(this.blockList[c].value);
- updatePalettes = true;
- }
- }
- }
- }
-
- if (updatePalettes) {
- // Do this update on a slight delay so as not to collide with
- // the actions update.
- var that = this;
- setTimeout(function () {
- that.palettes.hide();
- that.palettes.updatePalettes('boxes');
- // that.palettes.dict['boxes'].hide();
- that.palettes.show();
- }, 1500);
- }
- console.log("Finished block loading");
- var myCustomEvent = new Event('finishedLoading');
- document.dispatchEvent(myCustomEvent);
- };
-
- this._cleanupStacks = function () {
- if (this._checkArgClampBlocks.length > 0) {
- // We make multiple passes because we need to account for nesting.
- // FIXME: needs to be interwoven with TwoArgBlocks check.
- for (var i = 0; i < this._checkArgClampBlocks.length; i++) {
- for (var b = 0; b < this._checkArgClampBlocks.length; b++) {
- this._adjustArgClampBlock([this._checkArgClampBlocks[b]]);
- }
- }
- }
-
- if (this._checkTwoArgBlocks.length > 0) {
- // We make multiple passes because we need to account for nesting.
- for (var i = 0; i < this._checkTwoArgBlocks.length; i++) {
- for (var b = 0; b < this._checkTwoArgBlocks.length; b++) {
- this._adjustExpandableTwoArgBlock([this._checkTwoArgBlocks[b]]);
- }
- }
- }
-
- for (var blk = 0; blk < this._adjustTheseDocks.length; blk++) {
- // console.log('Adjust Docks: ' + this.blockList[this._adjustTheseDocks[blk]].name);
- this.adjustDocks(this._adjustTheseDocks[blk], true);
- // blockBlocks._expandTwoArgs();
- this._expandClamps();
- }
-
- for (var blk = 0; blk < this._adjustTheseStacks.length; blk++) {
- // console.log('Adjust Stack: ' + this.blockList[this._adjustTheseStacks[blk]].name);
- this.raiseStackToTop(this._adjustTheseStacks[blk]);
- }
- };
-
- this.actionHasReturn = function (blk) {
- // Look for a return block in an action stack.
- if (this.blockList[blk].name !== 'action') {
- return false;
- }
- this.findDragGroup(blk);
- for (var b = 0; b < this.dragGroup.length; b++) {
- if (this.blockList[this.dragGroup[b]].name === 'return') {
- return true;
- }
- }
- return false;
- };
-
- this.actionHasArgs = function (blk) {
- // Look for an arg blocks in an action stack.
- if (this.blockList[blk].name !== 'action') {
- return false;
- }
- this.findDragGroup(blk);
- for (var b = 0; b < this.dragGroup.length; b++) {
- if (this.blockList[this.dragGroup[b]].name === 'arg' || this.blockList[this.dragGroup[b]].name === 'namedarg') {
- return true;
- }
- }
- return false;
- };
-
- this.raiseStackToTop = function (blk) {
- // Move the stack associated with blk to the top.
- var topBlk = this.findTopBlock(blk);
- this.findDragGroup(topBlk);
-
- var z = this.stage.getNumChildren() - 1;
- for (var b = 0; b < this.dragGroup.length; b++) {
- this.stage.setChildIndex(this.blockList[this.dragGroup[b]].container, z);
- z -= 1;
- }
-
- this.refreshCanvas;
- };
-
- this.deleteActionBlock = function (myBlock) {
- var actionArg = this.blockList[myBlock.connections[1]];
- if (actionArg) {
- var actionName = actionArg.value;
- for (var blk = 0; blk < this.blockList.length; blk++) {
- var myBlock = this.blockList[blk];
- var blkParent = this.blockList[myBlock.connections[0]];
- if (blkParent == null) {
- continue;
- }
-
- if (['namedcalc', 'calc', 'nameddo', 'do', 'action'].indexOf(blkParent.name) !== -1) {
- continue;
- }
-
- var blockValue = myBlock.value;
- if (blockValue === _('action')) {
- continue;
- }
-
- if (blockValue === actionName) {
- blkParent.hide();
- myBlock.hide();
- myBlock.trash = true;
- blkParent.trash = true;
- }
- }
-
- // Avoid palette refreash race condition.
- this.deleteActionTimeout += 500;
- var timeout = this.deleteActionTimeout;
- var that = this;
- setTimeout(function () {
- that.deleteActionTimeout -= 500;
- that.palettes.removeActionPrototype(actionName);
- }, timeout);
- }
- };
-
- this.sendStackToTrash = function (myBlock) {
- // First, hide the palettes as they will need updating.
- for (var name in this.palettes.dict) {
- this.palettes.dict[name].hideMenu(true);
- }
-
- this.refreshCanvas();
-
- var thisBlock = this.blockList.indexOf(myBlock);
-
- // Add this block to the list of blocks in the trash so we can
- // undo this action.
- this.trashStacks.push(thisBlock);
-
- // Disconnect block.
- var parentBlock = myBlock.connections[0];
- if (parentBlock != null) {
- for (var c in this.blockList[parentBlock].connections) {
- if (this.blockList[parentBlock].connections[c] === thisBlock) {
- this.blockList[parentBlock].connections[c] = null;
- break;
- }
- }
- myBlock.connections[0] = null;
-
- // Add default block if user deletes all blocks from inside the note block
- this.addDefaultBlock(parentBlock, thisBlock);
- }
-
- if (myBlock.name === 'start' || myBlock.name === 'drum') {
- turtle = myBlock.value;
- var turtleNotInTrash = 0;
- for (var i = 0; i < this.turtles.turtleList.length; i++) {
- if (!this.turtles.turtleList[i].trash) {
- turtleNotInTrash += 1;
- }
- }
- if (turtle != null && turtleNotInTrash > 1) {
- console.log('putting turtle ' + turtle + ' in the trash');
- this.turtles.turtleList[turtle].trash = true;
- this.turtles.turtleList[turtle].container.visible = false;
- } else {
- this.errorMsg("You must always have at least one start block");
- console.log('null turtle');
- return;
- }
- } else if (myBlock.name === 'action') {
- if (!myBlock.trash) {
- this.deleteActionBlock(myBlock);
- }
- }
-
- // put drag group in trash
- this.findDragGroup(thisBlock);
- for (var b = 0; b < this.dragGroup.length; b++) {
- var blk = this.dragGroup[b];
- // console.log('putting ' + this.blockList[blk].name + ' in the trash');
- this.blockList[blk].trash = true;
- this.blockList[blk].hide();
- this.refreshCanvas();
- }
-
- // Adjust the stack from which we just deleted blocks.
- if (parentBlock != null) {
- var topBlk = this.findTopBlock(parentBlock);
- this.findDragGroup(topBlk);
-
- // We need to track two-arg blocks in case they need expanding.
- this._checkTwoArgBlocks = [];
-
- // And arg clamp blocks in case they need expanding.
- this._checkArgClampBlocks = [];
-
- for (var b = 0; b < this.dragGroup.length; b++) {
- var blk = this.dragGroup[b];
- var myBlock = this.blockList[blk];
- if (myBlock.isTwoArgBlock()) {
- this._checkTwoArgBlocks.push(blk);
- } else if (myBlock.isArgBlock() && myBlock.isExpandableBlock() || myBlock.isArgClamp()) {
- this._checkTwoArgBlocks.push(blk);
- } else if (['clamp', 'argclamp', 'argclamparg', 'doubleclamp', 'argflowclamp'].indexOf(myBlock.protoblock.style) !== -1) {
- this._checkArgClampBlocks.push(blk);
- }
- }
- this._cleanupStacks();
- this.refreshCanvas();
- }
- };
-
- return this;
- };
|