diff --git a/accessible/app.component.js b/accessible/app.component.js index 5de03e575..763a13c11 100644 --- a/accessible/app.component.js +++ b/accessible/app.component.js @@ -25,9 +25,6 @@ blocklyApp.workspace = new Blockly.Workspace(); -// If the debug flag is true, print console.logs to help with debugging. -blocklyApp.debug = false; - blocklyApp.AppView = ng.core .Component({ selector: 'blockly-app', diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index de9b382a3..db65e2d9f 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -30,6 +30,60 @@ blocklyApp.ClipboardService = ng.core this.clipboardBlockNextConnection_ = null; this.markedConnection_ = null; }, + areConnectionsCompatible_: function(blockConnection, connection) { + // Check that both connections exist, that it's the right kind of + // connection, and that the types match. + return Boolean( + connection && blockConnection && + Blockly.OPPOSITE_TYPE[blockConnection.type] == connection.type && + connection.checkType_(blockConnection)); + }, + isCompatibleWithClipboard: function(connection) { + var superiorConnection = this.clipboardBlockSuperiorConnection_; + var nextConnection = this.clipboardBlockNextConnection_; + return Boolean( + this.areConnectionsCompatible_(connection, superiorConnection) || + this.areConnectionsCompatible_(connection, nextConnection)); + }, + isMovableToMarkedConnection: function(block) { + // It should not be possible to move any ancestor of the block containing + // the marked spot to the marked spot. + if (!this.markedConnection_) { + return false; + } + + var markedSpotAncestorBlock = this.markedConnection_.getSourceBlock(); + while (markedSpotAncestorBlock) { + if (markedSpotAncestorBlock.id == block.id) { + return false; + } + markedSpotAncestorBlock = markedSpotAncestorBlock.getParent(); + } + + return this.canBeCopiedToMarkedConnection(block); + }, + canBeCopiedToMarkedConnection: function(block) { + if (!this.markedConnection_ || + !this.markedConnection_.getSourceBlock().workspace) { + return false; + } + + var potentialConnections = [ + block.outputConnection, + block.previousConnection, + block.nextConnection + ]; + + var that = this; + return potentialConnections.some(function(connection) { + return that.areConnectionsCompatible_( + connection, that.markedConnection_); + }); + }, + markConnection: function(connection) { + this.markedConnection_ = connection; + alert(Blockly.Msg.MARKED_SPOT_MSG); + }, cut: function(block) { var blockSummary = block.toString(); this.copy(block, false); @@ -64,42 +118,36 @@ blocklyApp.ClipboardService = ng.core }, pasteToMarkedConnection: function(block, announce) { var xml = Blockly.Xml.blockToDom(block); - var reconstitutedBlock = - Blockly.Xml.domToBlock(blocklyApp.workspace, xml); - this.markedConnection_.connect( - reconstitutedBlock.outputConnection || - reconstitutedBlock.previousConnection); + var reconstitutedBlock = Blockly.Xml.domToBlock( + blocklyApp.workspace, xml); + + var potentialConnections = [ + reconstitutedBlock.outputConnection, + reconstitutedBlock.previousConnection, + reconstitutedBlock.nextConnection + ]; + + var connectionSuccessful = false; + for (var i = 0; i < potentialConnections.length; i++) { + if (this.areConnectionsCompatible_( + this.markedConnection_, potentialConnections[i])) { + this.markedConnection_.connect(potentialConnections[i]); + connectionSuccessful = true; + break; + } + } + + if (!connectionSuccessful) { + console.error('ERROR: Could not connect block to marked spot.'); + return; + } + if (announce) { alert( Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG + reconstitutedBlock.toString()); } - }, - markConnection: function(connection) { - this.markedConnection_ = connection; - alert(Blockly.Msg.MARKED_SPOT_MSG); - }, - isCompatibleWithConnection_: function(blockConnection, connection) { - // Check that both connections exist, that the types match, and that it's - // the right kind of connection. - return Boolean( - connection && blockConnection && - Blockly.OPPOSITE_TYPE[blockConnection.type] == connection.type && - connection.checkType_(blockConnection)); - }, - isBlockCompatibleWithMarkedConnection: function(block) { - var blockConnection = block.outputConnection || block.previousConnection; - return Boolean( - this.markedConnection_ && - this.markedConnection_.sourceBlock_.workspace && - this.isCompatibleWithConnection_( - blockConnection, this.markedConnection_)); - }, - isClipboardCompatibleWithConnection: function(connection) { - var superiorConnection = this.clipboardBlockSuperiorConnection_; - var nextConnection = this.clipboardBlockNextConnection_; - return Boolean( - this.isCompatibleWithConnection_(connection, superiorConnection) || - this.isCompatibleWithConnection_(connection, nextConnection)); + + this.markedConnection_ = null; } }); diff --git a/accessible/toolbox-tree.component.js b/accessible/toolbox-tree.component.js index cc701fc35..64320458d 100644 --- a/accessible/toolbox-tree.component.js +++ b/accessible/toolbox-tree.component.js @@ -59,12 +59,12 @@ blocklyApp.ToolboxTreeComponent = ng.core
  • @@ -138,6 +138,9 @@ blocklyApp.ToolboxTreeComponent = ng.core return this.utilsService.generateAriaLabelledByAttr( mainLabel, secondLabel, isDisabled); }, + canBeCopiedToMarkedConnection: function(block) { + return this.clipboardService.canBeCopiedToMarkedConnection(block); + }, copyToWorkspace: function(block) { var xml = Blockly.Xml.blockToDom(block); Blockly.Xml.domToBlock(blocklyApp.workspace, xml); diff --git a/accessible/toolbox.component.js b/accessible/toolbox.component.js index 589a73712..081803103 100644 --- a/accessible/toolbox.component.js +++ b/accessible/toolbox.component.js @@ -98,7 +98,7 @@ blocklyApp.ToolboxComponent = ng.core this.idMap['Parent' + i] = 'blockly-toolbox-tree-node' + i; } } else { - // Create a single category is created with all the top-level blocks. + // Create a single category with all the top-level blocks. this.xmlHasCategories = false; this.toolboxCategories = [Array.from(xmlToolboxElt.children)]; } @@ -108,8 +108,7 @@ blocklyApp.ToolboxComponent = ng.core // descendant after the ids have been computed. if (this.xmlHasCategories) { this.treeService.setActiveDesc( - document.getElementById('blockly-toolbox-tree-node0'), - document.getElementById('blockly-toolbox-tree')); + 'blockly-toolbox-tree-node0', 'blockly-toolbox-tree'); } }, getToolboxWorkspace: function(categoryNode) { diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 84ac80541..e464bc788 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -27,12 +27,6 @@ blocklyApp.TreeService = ng.core .Class({ constructor: function() { - // Keeping track of the last key pressed. If the user presses - // enter (to edit a text input or press a button), the keyboard - // focus shifts to that element. In the next keystroke, if the user - // navigates away from the element using the arrow keys, we want - // to shift focus back to the tree as a whole. - this.previousKey_ = null; // Stores active descendant ids for each tree in the page. this.activeDescendantIds_ = {}; }, @@ -76,6 +70,16 @@ blocklyApp.TreeService = ng.core return this.getToolboxTreeNode_(); }, + focusOnCurrentTree_: function(treeId) { + var trees = this.getAllTreeNodes_(); + for (var i = 0; i < trees.length; i++) { + if (trees[i].id == treeId) { + trees[i].focus(); + return true; + } + } + return false; + }, focusOnNextTree_: function(treeId) { var trees = this.getAllTreeNodes_(); for (var i = 0; i < trees.length - 1; i++) { @@ -113,9 +117,10 @@ blocklyApp.TreeService = ng.core }, // Runs the given function while preserving the focus and active descendant // for the given tree. - runWhilePreservingFocus: function(func, treeId) { - var activeDescId = this.getActiveDescId(treeId); - this.unmarkActiveDesc_(activeDescId); + runWhilePreservingFocus: function(func, treeId, optionalNewActiveDescId) { + var oldDescId = this.getActiveDescId(treeId); + var newDescId = optionalNewActiveDescId || oldDescId; + this.unmarkActiveDesc_(oldDescId); func(); // The timeout is needed in order to give the DOM time to stabilize @@ -123,132 +128,136 @@ blocklyApp.TreeService = ng.core // pasteAbove(). var that = this; setTimeout(function() { - that.markActiveDesc_(activeDescId); - that.activeDescendantIds_[treeId] = activeDescId; + that.markActiveDesc_(newDescId); + that.activeDescendantIds_[treeId] = newDescId; document.getElementById(treeId).focus(); }, 0); }, // Make a given node the active descendant of a given tree. - setActiveDesc: function(newActiveDesc, tree) { - this.unmarkActiveDesc_(this.getActiveDescId(tree.id)); - this.markActiveDesc_(newActiveDesc.id); - this.activeDescendantIds_[tree.id] = newActiveDesc.id; + setActiveDesc: function(newActiveDescId, treeId) { + this.unmarkActiveDesc_(this.getActiveDescId(treeId)); + this.markActiveDesc_(newActiveDescId); + this.activeDescendantIds_[treeId] = newActiveDescId; }, onWorkspaceToolbarKeypress: function(e, treeId) { - switch (e.keyCode) { - case 9: - // 16,9: shift, tab - if (e.shiftKey) { - // If the previous key is shift, we're shift-tabbing mode. - this.focusOnPreviousTree_(treeId); - } else { - // If previous key isn't shift, we're tabbing. - this.focusOnNextTree_(treeId); - } - e.preventDefault(); - e.stopPropagation(); - break; + if (e.keyCode == 9) { + // Tab key. + if (e.shiftKey) { + this.focusOnPreviousTree_(treeId); + } else { + this.focusOnNextTree_(treeId); + } + e.preventDefault(); + e.stopPropagation(); } }, + isButtonOrFieldNode_: function(node) { + return ['BUTTON', 'INPUT'].indexOf(node.tagName) != -1; + }, + getNextActiveDescWhenBlockIsDeleted: function(blockRootNode) { + // Go up a level, if possible. + var nextNode = blockRootNode.parentNode; + while (nextNode && nextNode.tagName != 'LI') { + nextNode = nextNode.parentNode; + } + if (nextNode) { + return nextNode; + } + + // Otherwise, go to the next sibling. + var nextSibling = this.getNextSibling(blockRootNode); + if (nextSibling) { + return nextSibling; + } + + // Otherwise, go to the previous sibling. + var previousSibling = this.getPreviousSibling(blockRootNode); + if (previousSibling) { + return previousSibling; + } + + // Otherwise, this is a top-level isolated block, which means that + // something's gone wrong and this function should not have been called + // in the first place. + console.error('Could not handle deletion of block.' + blockRootNode); + }, onKeypress: function(e, tree) { var treeId = tree.id; - var node = document.getElementById(this.getActiveDescId(treeId)); - var keepFocus = this.previousKey_ == 13; - if (!node) { - blocklyApp.debug && console.log('KeyHandler: no active descendant'); + var activeDesc = document.getElementById(this.getActiveDescId(treeId)); + if (!activeDesc) { + console.error('ERROR: no active descendant for current tree.'); + + // TODO(sll): Generalize this to other trees (outside the workspace). + var workspaceTreeNodes = this.getWorkspaceTreeNodes_(); + for (var i = 0; i < workspaceTreeNodes.length; i++) { + if (workspaceTreeNodes[i].id == treeId) { + // Set the active desc to the first child in this tree. + this.setActiveDesc( + this.getFirstChild(workspaceTreeNodes[i]).id, treeId); + break; + } + } + return; } - switch (e.keyCode) { - case 9: - // 16,9: shift, tab - if (e.shiftKey) { - // If the previous key is shift, we're shift-tabbing. - this.focusOnPreviousTree_(treeId); - } else { - // If previous key isn't shift, we're tabbing - // we want to go to the run code button. - this.focusOnNextTree_(treeId); + + var isFocusingIntoField = false; + + if (e.keyCode == 13) { + // Enter key. The user wants to interact with a child. + if (activeDesc.children.length == 1) { + var child = activeDesc.children[0]; + if (child.tagName == 'BUTTON') { + child.click(); + this.isFocusingIntoField = true; + } else if (child.tagName == 'INPUT') { + child.focus(); } - // Setting the previous key variable in each case because - // we only want to save the previous navigation keystroke, - // not any typing. - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 37: - // Left-facing arrow: go out a level, if possible. If not, do nothing. - var nextNode = node.parentNode; - if (node.tagName == 'BUTTON' || node.tagName == 'INPUT') { + } + } else if (e.keyCode == 9) { + // Tab key. + if (e.shiftKey) { + this.focusOnPreviousTree_(treeId); + } else { + this.focusOnNextTree_(treeId); + } + e.preventDefault(); + e.stopPropagation(); + } else if (e.keyCode >= 37 && e.keyCode <= 40) { + // Arrow keys. + if (e.keyCode == 37) { + // Left arrow key. Go up a level, if possible. + var nextNode = activeDesc.parentNode; + if (this.isButtonOrFieldNode_(activeDesc)) { nextNode = nextNode.parentNode; } - while (nextNode && nextNode.className != 'treeview' && - nextNode.tagName != 'LI') { + while (nextNode && nextNode.tagName != 'LI') { nextNode = nextNode.parentNode; } - if (!nextNode || nextNode.className == 'treeview') { - return; + if (nextNode) { + this.setActiveDesc(nextNode.id, treeId); } - this.setActiveDesc(nextNode, tree); - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 38: - // Up-facing arrow: go up a level, if possible. If not, do nothing. - var prevSibling = this.getPreviousSibling(node); - if (prevSibling && prevSibling.tagName != 'H1') { - this.setActiveDesc(prevSibling, tree); - } else { - blocklyApp.debug && console.log('no previous sibling'); + } else if (e.keyCode == 38) { + // Up arrow key. Go to the previous sibling, if possible. + var prevSibling = this.getPreviousSibling(activeDesc); + if (prevSibling) { + this.setActiveDesc(prevSibling.id, treeId); } - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 39: - var firstChild = this.getFirstChild(node); + } else if (e.keyCode == 39) { + // Right arrow key. Go down a level, if possible. + var firstChild = this.getFirstChild(activeDesc); if (firstChild) { - this.setActiveDesc(firstChild, tree); - } else { - blocklyApp.debug && console.log('no valid child'); + this.setActiveDesc(firstChild.id, treeId); } - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 40: - // Down-facing arrow: go down a level, if possible. - // If not, do nothing. - var nextSibling = this.getNextSibling(node); + } else if (e.keyCode == 40) { + // Down arrow key. Go to the next sibling, if possible. + var nextSibling = this.getNextSibling(activeDesc); if (nextSibling) { - this.setActiveDesc(nextSibling, tree); - } else { - blocklyApp.debug && console.log('no next sibling'); + this.setActiveDesc(nextSibling.id, treeId); } - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 13: - // If I've pressed enter, I want to interact with a child. - var activeDesc = node; - if (activeDesc) { - var children = activeDesc.children; - var child = children[0]; - if (children.length == 1 && (child.tagName == 'INPUT' || - child.tagName == 'BUTTON')) { - if (child.tagName == 'BUTTON') { - child.click(); - } - else if (child.tagName == 'INPUT') { - child.focus(); - } - } - } else { - blocklyApp.debug && console.log('no activeDesc'); - } - this.previousKey_ = e.keyCode; - break; + } + + e.preventDefault(); + e.stopPropagation(); } }, getFirstChild: function(element) { @@ -273,13 +282,12 @@ blocklyApp.TreeService = ng.core if (element.nextElementSibling) { // If there is a sibling, find the list element child of the sibling. var node = element.nextElementSibling; - if (node.tagName != 'LI') { - var listElems = node.getElementsByTagName('li'); - // getElementsByTagName returns in DFS order - // therefore the first element is the first relevant list child. - return listElems[0]; + if (node.tagName == 'LI') { + return node; } else { - return element.nextElementSibling; + // getElementsByTagName returns in DFS order, therefore the first + // element is the first relevant list child. + return node.getElementsByTagName('li')[0]; } } else { var parent = element.parentNode; @@ -343,7 +351,6 @@ blocklyApp.TreeService = ng.core } } } - blocklyApp.debug && console.log('no last child'); return null; } } diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index 2fe99002c..5c7f4dc5f 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -28,69 +28,29 @@ blocklyApp.WorkspaceTreeComponent = ng.core .Component({ selector: 'blockly-workspace-tree', template: ` -
  • + [attr.aria-level]="level" aria-selected="false"> -
      + +
      1. + [attr.aria-level]="level+1" aria-selected="false"> -
          -
        1. - -
        2. -
        3. - -
        4. -
        5. -
        6. -
        7. - -
        8. -
        9. - -
        10. -
        11. - -
        12. -
        13. - -
        14. -
        15. - -
      2. +
        -
          +
          1. @@ -135,40 +95,173 @@ blocklyApp.WorkspaceTreeComponent = ng.core }) .Class({ constructor: [ - blocklyApp.ClipboardService, blocklyApp.TreeService, blocklyApp.UtilsService, + blocklyApp.ClipboardService, blocklyApp.TreeService, + blocklyApp.UtilsService, function(_clipboardService, _treeService, _utilsService) { this.infoBlocks = Object.create(null); this.clipboardService = _clipboardService; this.treeService = _treeService; this.utilsService = _utilsService; }], - getElementsNeedingIds_: function() { - var elementsNeedingIds = ['blockSummary', 'listItem', 'label', - 'cutListItem', 'cutButton', 'copyListItem', 'copyButton', - 'pasteBelow', 'pasteBelowButton', 'pasteAbove', 'pasteAboveButton', - 'markBelow', 'markBelowButton', 'markAbove', 'markAboveButton', - 'sendToSelectedListItem', 'sendToSelectedButton', 'delete', - 'deleteButton']; + isIsolatedTopLevelBlock_: function(block) { + // Returns whether the given block is at the top level, and has no + // siblings. + return Boolean( + !block.nextConnection.targetConnection && + !block.previousConnection.targetConnection && + blocklyApp.workspace.topBlocks_.some(function(topBlock) { + return topBlock.id == block.id; + })); + }, + removeBlockAndSetFocus_: function(block, deleteBlockFunc) { + // This method runs the given function and then does one of two things: + // - If the block is an isolated top-level block, it shifts the tree + // focus. + // - Otherwise, it sets the correct new active desc for the current tree. + if (this.isIsolatedTopLevelBlock_(block)) { + var nextNodeToFocusOn = + this.treeService.getNodeToFocusOnWhenTreeIsDeleted(this.tree.id); + deleteBlockFunc(); + nextNodeToFocusOn.focus(); + } else { + var blockRootNode = document.getElementById(this.idMap['blockRoot']); + var nextActiveDesc = + this.treeService.getNextActiveDescWhenBlockIsDeleted( + blockRootNode); + this.treeService.runWhilePreservingFocus( + deleteBlockFunc, this.tree.id, nextActiveDesc.id); + } + }, + cutBlock_: function() { + var that = this; + this.removeBlockAndSetFocus_(this.block, function() { + that.clipboardService.cut(that.block); + }); + }, + deleteBlock_: function() { + var that = this; + this.removeBlockAndSetFocus_(this.block, function() { + that.block.dispose(true); + }); + }, + pasteToConnection_: function(connection) { + var that = this; + this.treeService.runWhilePreservingFocus(function() { + // If the connection is a 'previousConnection' and that connection is + // already joined to something, use the 'nextConnection' of the + // previous block instead in order to do an insertion. + if (connection.type == Blockly.PREVIOUS_STATEMENT && + connection.isConnected()) { + that.clipboardService.pasteFromClipboard( + connection.targetConnection); + } else { + that.clipboardService.pasteFromClipboard(connection); + } + }, this.tree.id); + }, + sendToMarkedSpot_: function() { + this.clipboardService.pasteToMarkedConnection(this.block, false); + var that = this; + this.removeBlockAndSetFocus_(this.block, function() { + that.block.dispose(true); + }); + + alert('Block moved to marked spot: ' + this.block.toString()); + }, + ngOnInit: function() { + var that = this; + + // Generate a list of action buttons. + this.actionButtonsInfo = [{ + baseIdKey: 'cut', + translationIdForText: 'CUT_BLOCK', + action: that.cutBlock_.bind(that), + isDisabled: function() { + return false; + } + }, { + baseIdKey: 'copy', + translationIdForText: 'COPY_BLOCK', + action: that.clipboardService.copy.bind( + that.clipboardService, that.block, true), + isDisabled: function() { + return false; + } + }, { + baseIdKey: 'pasteBelow', + translationIdForText: 'PASTE_BELOW', + action: that.pasteToConnection_.bind(that, that.block.nextConnection), + isDisabled: function() { + return Boolean( + !that.block.nextConnection || + !that.isCompatibleWithClipboard(that.block.nextConnection)); + } + }, { + baseIdKey: 'pasteAbove', + translationIdForText: 'PASTE_ABOVE', + action: that.pasteToConnection_.bind( + that, that.block.previousConnection), + isDisabled: function() { + return Boolean( + !that.block.previousConnection || + !that.isCompatibleWithClipboard(that.block.previousConnection)); + } + }, { + baseIdKey: 'markBelow', + translationIdForText: 'MARK_SPOT_BELOW', + action: that.clipboardService.markConnection.bind( + that.clipboardService, that.block.nextConnection), + isDisabled: function() { + return !that.block.nextConnection; + } + }, { + baseIdKey: 'markAbove', + translationIdForText: 'MARK_SPOT_ABOVE', + action: that.clipboardService.markConnection.bind( + that.clipboardService, that.block.previousConnection), + isDisabled: function() { + return !that.block.previousConnection; + } + }, { + baseIdKey: 'sendToMarkedSpot', + translationIdForText: 'MOVE_TO_MARKED_SPOT', + action: that.sendToMarkedSpot_.bind(that), + isDisabled: function() { + return !that.clipboardService.isMovableToMarkedConnection( + that.block); + } + }, { + baseIdKey: 'delete', + translationIdForText: 'DELETE', + action: that.deleteBlock_.bind(that), + isDisabled: function() { + return false; + } + }]; + + // Make a list of all the id keys. + this.idKeys = ['blockRoot', 'blockSummary', 'listItem', 'label']; + this.actionButtonsInfo.forEach(function(buttonInfo) { + that.idKeys.push(buttonInfo.baseIdKey, buttonInfo.baseIdKey + 'Button'); + }); for (var i = 0; i < this.block.inputList.length; i++) { var inputBlock = this.block.inputList[i]; if (inputBlock.connection && !inputBlock.connection.targetBlock()) { - elementsNeedingIds = elementsNeedingIds.concat( - ['inputList' + i, 'inputMenuLabel' + i, 'markSpot' + i, - 'markSpotButton' + i, 'paste' + i, 'pasteButton' + i]); + that.idKeys.push( + 'inputList' + i, 'inputMenuLabel' + i, 'markSpot' + i, + 'markSpotButton' + i, 'paste' + i, 'pasteButton' + i); } } - - return elementsNeedingIds; }, - ngOnInit: function() { - var elementsNeedingIds = this.getElementsNeedingIds_(); - - this.idMap = {} - this.idMap['parentList'] = this.utilsService.generateUniqueId(); - for (var i = 0; i < elementsNeedingIds.length; i++) { - this.idMap[elementsNeedingIds[i]] = - this.block.id + elementsNeedingIds[i]; + ngDoCheck: function() { + // Generate a unique id for each id key. This needs to be done every time + // changes happen, but after the first ng-init, in order to force the + // element ids to change in cases where, e.g., a block is inserted in the + // middle of a sequence of blocks. + this.idMap = {}; + for (var i = 0; i < this.idKeys.length; i++) { + this.idMap[this.idKeys[i]] = this.block.id + this.idKeys[i]; } }, ngAfterViewInit: function() { @@ -177,80 +270,16 @@ blocklyApp.WorkspaceTreeComponent = ng.core if (this.tree && this.isTopLevel && !this.tree.id) { this.tree.id = this.utilsService.generateUniqueId(); } - if (this.tree && this.isTopLevel && !this.treeService.getActiveDescId(this.tree.id)) { - this.treeService.setActiveDesc( - document.getElementById(this.idMap['parentList']), - this.tree); - } - }, - hasPreviousConnection: function(block) { - return Boolean(block.previousConnection); - }, - hasNextConnection: function(block) { - return Boolean(block.nextConnection); - }, - isCompatibleWithClipboard: function(connection) { - return this.clipboardService.isClipboardCompatibleWithConnection( - connection); - }, - isTopLevelBlock: function(block) { - return blocklyApp.workspace.topBlocks_.some(function(topBlock) { - return topBlock.id == block.id; - }); - }, - pasteAbove: function(block) { - var that = this; - this.treeService.runWhilePreservingFocus(function() { - that.clipboardService.pasteFromClipboard(block.previousConnection); - }, this.tree.id); - }, - pasteBelow: function(block) { - var that = this; - this.treeService.runWhilePreservingFocus(function() { - that.clipboardService.pasteFromClipboard(block.nextConnection); - }, this.tree.id); - }, - cutToClipboard: function(block) { - if (this.isTopLevelBlock(block)) { - nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( - this.tree.id); - this.clipboardService.cut(block); - nextNodeToFocusOn.focus(); - } else { - // TODO(sll): Change the active descendant of the tree. - this.clipboardService.cut(block); - } - }, - deleteBlock: function(block) { - if (this.isTopLevelBlock(block)) { - nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( - this.tree.id); - block.dispose(true); - nextNodeToFocusOn.focus(); - } else { - // TODO(sll): Change the active descendant of the tree. - block.dispose(true); + this.treeService.setActiveDesc(this.idMap['blockRoot'], this.tree.id); } }, generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { return this.utilsService.generateAriaLabelledByAttr( mainLabel, secondLabel, isDisabled); }, - sendToMarkedSpot: function(block) { - this.clipboardService.pasteToMarkedConnection(block, false); - - if (this.isTopLevelBlock(block)) { - nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted( - this.tree.id); - block.dispose(true); - nextNodeToFocusOn.focus(); - } else { - // TODO(sll): Change the active descendant of the tree. - block.dispose(true); - } - - alert('Block moved to marked spot: ' + block.toString()); + isCompatibleWithClipboard: function(connection) { + return this.clipboardService.isCompatibleWithClipboard(connection); } }); diff --git a/blockly_compressed.js b/blockly_compressed.js index c8154fd65..3a5ae9115 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -859,7 +859,7 @@ goog.ui.tree.TreeControl.prototype.handleMouseEvent_=function(a){goog.log.fine(t goog.ui.tree.TreeControl.prototype.handleKeyEvent=function(a){var b;(b=this.typeAhead_.handleNavigation(a)||this.selectedItem_&&this.selectedItem_.onKeyDown(a)||this.typeAhead_.handleTypeAheadChar(a))&&a.preventDefault();return b};goog.ui.tree.TreeControl.prototype.getNodeFromEvent_=function(a){for(var b=a.target;null!=b;){if(a=goog.ui.tree.BaseNode.allNodes[b.id])return a;if(b==this.getElement())break;b=b.parentNode}return null}; goog.ui.tree.TreeControl.prototype.createNode=function(a){return new goog.ui.tree.TreeNode(a||goog.html.SafeHtml.EMPTY,this.getConfig(),this.getDomHelper())};goog.ui.tree.TreeControl.prototype.setNode=function(a){this.typeAhead_.setNodeInMap(a)};goog.ui.tree.TreeControl.prototype.removeNode=function(a){this.typeAhead_.removeNodeFromMap(a)};goog.ui.tree.TreeControl.prototype.clearTypeAhead=function(){this.typeAhead_.clear()};goog.ui.tree.TreeControl.defaultConfig=goog.ui.tree.BaseNode.defaultConfig; // Copyright 2013 Google Inc. Apache License 2.0 -var Blockly={Blocks:{}}; +var Blockly={Blocks:{}};Blockly.Blocks.ONE_BASED_INDEXING=!0; // Copyright 2012 Google Inc. Apache License 2.0 Blockly.Workspace=function(a){this.id=Blockly.genUid();Blockly.Workspace.WorkspaceDB_[this.id]=this;this.options=a||{};this.RTL=!!this.options.RTL;this.horizontalLayout=!!this.options.horizontalLayout;this.toolboxPosition=this.options.toolboxPosition;this.topBlocks_=[];this.listeners_=[];this.undoStack_=[];this.redoStack_=[];this.blockDB_=Object.create(null)};Blockly.Workspace.prototype.rendered=!1;Blockly.Workspace.prototype.MAX_UNDO=1024; Blockly.Workspace.prototype.dispose=function(){this.listeners_.length=0;this.clear();delete Blockly.Workspace.WorkspaceDB_[this.id]};Blockly.Workspace.SCAN_ANGLE=3;Blockly.Workspace.prototype.addTopBlock=function(a){this.topBlocks_.push(a)};Blockly.Workspace.prototype.removeTopBlock=function(a){for(var b=!1,c,d=0;c=this.topBlocks_[d];d++)if(c==a){this.topBlocks_.splice(d,1);b=!0;break}if(!b)throw"Block not present in workspace's list of top-most blocks.";}; @@ -889,7 +889,7 @@ Blockly.Bubble.prototype.renderArrow_=function(){var a=[],b=this.width_/2,c=this p*d,e=c+p*e,p=b+h*m,l=c+h*k,b=b-h*m,c=c-h*k,k=g+this.arrow_radians_;k>2*Math.PI&&(k-=2*Math.PI);g=Math.sin(k)*f/Blockly.Bubble.ARROW_BEND;f=Math.cos(k)*f/Blockly.Bubble.ARROW_BEND;a.push("M"+p+","+l);a.push("C"+(p+f)+","+(l+g)+" "+d+","+e+" "+d+","+e);a.push("C"+d+","+e+" "+(b+f)+","+(c+g)+" "+b+","+c)}a.push("z");this.bubbleArrow_.setAttribute("d",a.join(" "))};Blockly.Bubble.prototype.setColour=function(a){this.bubbleBack_.setAttribute("fill",a);this.bubbleArrow_.setAttribute("fill",a)}; Blockly.Bubble.prototype.dispose=function(){Blockly.Bubble.unbindDragEvents_();goog.dom.removeNode(this.bubbleGroup_);this.shape_=this.content_=this.workspace_=this.resizeGroup_=this.bubbleBack_=this.bubbleArrow_=this.bubbleGroup_=null};Blockly.Icon=function(a){this.block_=a};Blockly.Icon.prototype.collapseHidden=!0;Blockly.Icon.prototype.SIZE=17;Blockly.Icon.prototype.bubble_=null;Blockly.Icon.prototype.iconXY_=null;Blockly.Icon.prototype.createIcon=function(){this.iconGroup_||(this.iconGroup_=Blockly.createSvgElement("g",{"class":"blocklyIconGroup"},null),this.drawIcon_(this.iconGroup_),this.block_.getSvgRoot().appendChild(this.iconGroup_),Blockly.bindEvent_(this.iconGroup_,"mouseup",this,this.iconClick_),this.updateEditable())}; Blockly.Icon.prototype.dispose=function(){goog.dom.removeNode(this.iconGroup_);this.iconGroup_=null;this.setVisible(!1);this.block_=null};Blockly.Icon.prototype.updateEditable=function(){this.block_.isInFlyout||!this.block_.isEditable()?Blockly.addClass_(this.iconGroup_,"blocklyIconGroupReadonly"):Blockly.removeClass_(this.iconGroup_,"blocklyIconGroupReadonly")};Blockly.Icon.prototype.isVisible=function(){return!!this.bubble_}; -Blockly.Icon.prototype.iconClick_=function(a){Blockly.dragMode_!=Blockly.DRAG_FREE&&(this.block_.isInFlyout||Blockly.isRightButton(a)||this.setVisible(!this.isVisible()))};Blockly.Icon.prototype.updateColour=function(){this.isVisible()&&this.bubble_.setColour(this.block_.getColour())}; +Blockly.Icon.prototype.iconClick_=function(a){this.block_.workspace.isDragging()||this.block_.isInFlyout||Blockly.isRightButton(a)||this.setVisible(!this.isVisible())};Blockly.Icon.prototype.updateColour=function(){this.isVisible()&&this.bubble_.setColour(this.block_.getColour())}; Blockly.Icon.prototype.renderIcon=function(a){if(this.collapseHidden&&this.block_.isCollapsed())return this.iconGroup_.setAttribute("display","none"),a;this.iconGroup_.setAttribute("display","block");var b=this.SIZE;this.block_.RTL&&(a-=b);this.iconGroup_.setAttribute("transform","translate("+a+",5)");this.computeIconLocation();return a=this.block_.RTL?a-Blockly.BlockSvg.SEP_SPACE_X:a+(b+Blockly.BlockSvg.SEP_SPACE_X)}; Blockly.Icon.prototype.setIconLocation=function(a){this.iconXY_=a;this.isVisible()&&this.bubble_.setAnchorLocation(a)};Blockly.Icon.prototype.computeIconLocation=function(){var a=this.block_.getRelativeToSurfaceXY(),b=Blockly.getRelativeXY_(this.iconGroup_),a=new goog.math.Coordinate(a.x+b.x+this.SIZE/2,a.y+b.y+this.SIZE/2);goog.math.Coordinate.equals(this.getIconLocation(),a)||this.setIconLocation(a)};Blockly.Icon.prototype.getIconLocation=function(){return this.iconXY_}; // Copyright 2011 Google Inc. Apache License 2.0 @@ -924,13 +924,14 @@ Blockly.Field.prototype.setSourceBlock=function(a){goog.asserts.assert(!this.sou Blockly.Field.prototype.init=function(){this.fieldGroup_||(this.fieldGroup_=Blockly.createSvgElement("g",{},null),this.visible_||(this.fieldGroup_.style.display="none"),this.borderRect_=Blockly.createSvgElement("rect",{rx:4,ry:4,x:-Blockly.BlockSvg.SEP_SPACE_X/2,y:0,height:16},this.fieldGroup_,this.sourceBlock_.workspace),this.textElement_=Blockly.createSvgElement("text",{"class":"blocklyText",y:this.size_.height-12.5},this.fieldGroup_),this.updateEditable(),this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_), this.mouseUpWrapper_=Blockly.bindEvent_(this.fieldGroup_,"mouseup",this,this.onMouseUp_),this.updateTextNode_())};Blockly.Field.prototype.dispose=function(){this.mouseUpWrapper_&&(Blockly.unbindEvent_(this.mouseUpWrapper_),this.mouseUpWrapper_=null);this.sourceBlock_=null;goog.dom.removeNode(this.fieldGroup_);this.validator_=this.borderRect_=this.textElement_=this.fieldGroup_=null}; Blockly.Field.prototype.updateEditable=function(){this.EDITABLE&&this.sourceBlock_&&(this.sourceBlock_.isEditable()?(Blockly.addClass_(this.fieldGroup_,"blocklyEditableText"),Blockly.removeClass_(this.fieldGroup_,"blocklyNoNEditableText"),this.fieldGroup_.style.cursor=this.CURSOR):(Blockly.addClass_(this.fieldGroup_,"blocklyNonEditableText"),Blockly.removeClass_(this.fieldGroup_,"blocklyEditableText"),this.fieldGroup_.style.cursor=""))};Blockly.Field.prototype.isVisible=function(){return this.visible_}; -Blockly.Field.prototype.setVisible=function(a){if(this.visible_!=a){this.visible_=a;var b=this.getSvgRoot();b&&(b.style.display=a?"block":"none",this.render_())}};Blockly.Field.prototype.setValidator=function(a){this.validator_=a};Blockly.Field.prototype.getValidator=function(){return this.validator_};Blockly.Field.prototype.getSvgRoot=function(){return this.fieldGroup_}; +Blockly.Field.prototype.setVisible=function(a){if(this.visible_!=a){this.visible_=a;var b=this.getSvgRoot();b&&(b.style.display=a?"block":"none",this.render_())}};Blockly.Field.prototype.setValidator=function(a){this.validator_=a};Blockly.Field.prototype.getValidator=function(){return this.validator_}; +Blockly.Field.prototype.callValidator=function(a){for(var b=[this.getValidator()],c=this.constructor;c;)b.unshift(c.classValidator),c=c.superClass_;for(c=0;cthis.maxDisplayLength&&(a=a.substring(0,this.maxDisplayLength-2)+"\u2026");goog.dom.removeChildren(this.textElement_);a=a.replace(/\s/g,Blockly.Field.NBSP);this.sourceBlock_.RTL&&a&&(a+="\u200f");a||(a=Blockly.Field.NBSP);a=document.createTextNode(a);this.textElement_.appendChild(a);this.size_.width=0}};Blockly.Field.prototype.getValue=function(){return this.getText()}; Blockly.Field.prototype.setValue=function(a){if(null!==a){var b=this.getValue();b!=a&&(this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,b,a)),this.setText(a))}}; -Blockly.Field.prototype.onMouseUp_=function(a){if(!goog.userAgent.IPHONE&&!goog.userAgent.IPAD||goog.userAgent.isVersionOrHigher("537.51.2")||0===a.layerX||0===a.layerY)Blockly.isRightButton(a)||Blockly.dragMode_!=Blockly.DRAG_FREE&&this.sourceBlock_.isEditable()&&this.showEditor_()};Blockly.Field.prototype.setTooltip=function(a){};Blockly.Field.prototype.getAbsoluteXY_=function(){return goog.style.getPageOffset(this.borderRect_)};Blockly.Tooltip={};Blockly.Tooltip.visible=!1;Blockly.Tooltip.LIMIT=50;Blockly.Tooltip.mouseOutPid_=0;Blockly.Tooltip.showPid_=0;Blockly.Tooltip.lastX_=0;Blockly.Tooltip.lastY_=0;Blockly.Tooltip.element_=null;Blockly.Tooltip.poisonedElement_=null;Blockly.Tooltip.OFFSET_X=0;Blockly.Tooltip.OFFSET_Y=10;Blockly.Tooltip.RADIUS_OK=10;Blockly.Tooltip.HOVER_MS=750;Blockly.Tooltip.MARGINS=5;Blockly.Tooltip.DIV=null; +Blockly.Field.prototype.onMouseUp_=function(a){if(!goog.userAgent.IPHONE&&!goog.userAgent.IPAD||goog.userAgent.isVersionOrHigher("537.51.2")||0===a.layerX||0===a.layerY)Blockly.isRightButton(a)||this.sourceBlock_.workspace.isDragging()||this.sourceBlock_.isEditable()&&this.showEditor_()};Blockly.Field.prototype.setTooltip=function(a){};Blockly.Field.prototype.getAbsoluteXY_=function(){return goog.style.getPageOffset(this.borderRect_)};Blockly.Tooltip={};Blockly.Tooltip.visible=!1;Blockly.Tooltip.LIMIT=50;Blockly.Tooltip.mouseOutPid_=0;Blockly.Tooltip.showPid_=0;Blockly.Tooltip.lastX_=0;Blockly.Tooltip.lastY_=0;Blockly.Tooltip.element_=null;Blockly.Tooltip.poisonedElement_=null;Blockly.Tooltip.OFFSET_X=0;Blockly.Tooltip.OFFSET_Y=10;Blockly.Tooltip.RADIUS_OK=10;Blockly.Tooltip.HOVER_MS=750;Blockly.Tooltip.MARGINS=5;Blockly.Tooltip.DIV=null; Blockly.Tooltip.createDom=function(){Blockly.Tooltip.DIV||(Blockly.Tooltip.DIV=goog.dom.createDom("div","blocklyTooltipDiv"),document.body.appendChild(Blockly.Tooltip.DIV))};Blockly.Tooltip.bindMouseEvents=function(a){Blockly.bindEvent_(a,"mouseover",null,Blockly.Tooltip.onMouseOver_);Blockly.bindEvent_(a,"mouseout",null,Blockly.Tooltip.onMouseOut_);Blockly.bindEvent_(a,"mousemove",null,Blockly.Tooltip.onMouseMove_)}; Blockly.Tooltip.onMouseOver_=function(a){for(a=a.target;!goog.isString(a.tooltip)&&!goog.isFunction(a.tooltip);)a=a.tooltip;Blockly.Tooltip.element_!=a&&(Blockly.Tooltip.hide(),Blockly.Tooltip.poisonedElement_=null,Blockly.Tooltip.element_=a);clearTimeout(Blockly.Tooltip.mouseOutPid_)};Blockly.Tooltip.onMouseOut_=function(a){Blockly.Tooltip.mouseOutPid_=setTimeout(function(){Blockly.Tooltip.element_=null;Blockly.Tooltip.poisonedElement_=null;Blockly.Tooltip.hide()},1);clearTimeout(Blockly.Tooltip.showPid_)}; Blockly.Tooltip.onMouseMove_=function(a){if(Blockly.Tooltip.element_&&Blockly.Tooltip.element_.tooltip&&Blockly.dragMode_==Blockly.DRAG_NONE&&!Blockly.WidgetDiv.isVisible())if(Blockly.Tooltip.visible){var b=Blockly.Tooltip.lastX_-a.pageX;a=Blockly.Tooltip.lastY_-a.pageY;Math.sqrt(b*b+a*a)>Blockly.Tooltip.RADIUS_OK&&Blockly.Tooltip.hide()}else Blockly.Tooltip.poisonedElement_!=Blockly.Tooltip.element_&&(clearTimeout(Blockly.Tooltip.showPid_),Blockly.Tooltip.lastX_=a.pageX,Blockly.Tooltip.lastY_=a.pageY, @@ -997,13 +998,13 @@ Blockly.Xml.domToPrettyText=function(a){a=Blockly.Xml.domToText(a).split("<");fo Blockly.Xml.textToDom=function(a){(a=(new DOMParser).parseFromString(a,"text/xml"))&&a.firstChild&&"xml"==a.firstChild.nodeName.toLowerCase()&&a.firstChild===a.lastChild||goog.asserts.fail("Blockly.Xml.textToDom did not obtain a valid XML tree.");return a.firstChild}; Blockly.Xml.domToWorkspace=function(a,b){if(a instanceof Blockly.Workspace){var c=a;a=b;b=c;console.warn("Deprecated call to Blockly.Xml.domToWorkspace, swap the arguments.")}var d;b.RTL&&(d=b.getWidth());Blockly.Field.startCache();var c=a.childNodes.length,e=Blockly.Events.getGroup();e||Blockly.Events.setGroup(!0);for(var f=0;f=this.remainingCapacity())){Blockly.terminateDrag_();Blockly.Events.disable();var b=Blockly.Xml.domToBlock(a,this),c=parseInt(a.getAttribute("x"),10);a=parseInt(a.getAttribute("y"),10);if(!isNaN(c)&&!isNaN(a)){this.RTL&&(c=-c);do{for(var d=!1,e=this.getAllBlocks(),f=0,g;g=e[f];f++)if(g=g.getRelativeToSurfaceXY(),1>=Math.abs(c-g.x)&&1>=Math.abs(a-g.y)){d=!0;break}if(!d)for(e=b.getConnections_(!1), -f=0;g=e[f];f++)if(g.closest(Blockly.SNAP_RADIUS,new goog.math.Coordinate(c,a)).connection){d=!0;break}d&&(c=this.RTL?c-Blockly.SNAP_RADIUS:c+Blockly.SNAP_RADIUS,a+=2*Blockly.SNAP_RADIUS)}while(d);b.moveBy(c,a)}Blockly.Events.enable();Blockly.Events.isEnabled()&&!b.isShadow()&&Blockly.Events.fire(new Blockly.Events.Create(b));b.select()}}; +Blockly.WorkspaceSvg.prototype.paste=function(a){if(this.rendered&&!(a.getElementsByTagName("block").length>=this.remainingCapacity())){Blockly.terminateDrag_();Blockly.Events.disable();try{var b=Blockly.Xml.domToBlock(a,this),c=parseInt(a.getAttribute("x"),10),d=parseInt(a.getAttribute("y"),10);if(!isNaN(c)&&!isNaN(d)){this.RTL&&(c=-c);do{a=!1;for(var e=this.getAllBlocks(),f=0,g;g=e[f];f++){var h=g.getRelativeToSurfaceXY();if(1>=Math.abs(c-h.x)&&1>=Math.abs(d-h.y)){a=!0;break}}if(!a)for(var k=b.getConnections_(!1), +f=0,m;m=k[f];f++)if(m.closest(Blockly.SNAP_RADIUS,new goog.math.Coordinate(c,d)).connection){a=!0;break}a&&(c=this.RTL?c-Blockly.SNAP_RADIUS:c+Blockly.SNAP_RADIUS,d+=2*Blockly.SNAP_RADIUS)}while(a);b.moveBy(c,d)}}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!b.isShadow()&&Blockly.Events.fire(new Blockly.Events.Create(b));b.select()}}; Blockly.WorkspaceSvg.prototype.recordDeleteAreas=function(){this.deleteAreaTrash_=this.trashcan?this.trashcan.getClientRect():null;this.deleteAreaToolbox_=this.flyout_?this.flyout_.getClientRect():this.toolbox_?this.toolbox_.getClientRect():null}; Blockly.WorkspaceSvg.prototype.isDeleteArea=function(a){a=new goog.math.Coordinate(a.clientX,a.clientY);if(this.deleteAreaTrash_){if(this.deleteAreaTrash_.contains(a))return this.trashcan.setOpen_(!0),Blockly.Css.setCursor(Blockly.Css.Cursor.DELETE),!0;this.trashcan.setOpen_(!1)}if(this.deleteAreaToolbox_&&this.deleteAreaToolbox_.contains(a))return Blockly.Css.setCursor(Blockly.Css.Cursor.DELETE),!0;Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);return!1}; Blockly.WorkspaceSvg.prototype.onMouseDown_=function(a){this.markFocused();Blockly.isTargetInput_(a)||(Blockly.terminateDrag_(),Blockly.hideChaff(),a.target&&a.target.nodeName&&("svg"==a.target.nodeName.toLowerCase()||a.target==this.svgBackground_)&&Blockly.selected&&!this.options.readOnly&&Blockly.selected.unselect(),Blockly.isRightButton(a)?this.showContextMenu_(a):this.scrollbar&&(this.isScrolling=!0,this.startDragMouseX=a.clientX,this.startDragMouseY=a.clientY,this.startDragMetrics=this.getMetrics(), this.startScrollX=this.scrollX,this.startScrollY=this.scrollY,"mouseup"in Blockly.bindEvent_.TOUCH_MAP&&(Blockly.onTouchUpWrapper_=Blockly.onTouchUpWrapper_||[],Blockly.onTouchUpWrapper_=Blockly.onTouchUpWrapper_.concat(Blockly.bindEvent_(document,"mouseup",null,Blockly.onMouseUp_))),Blockly.onMouseMoveWrapper_=Blockly.onMouseMoveWrapper_||[],Blockly.onMouseMoveWrapper_=Blockly.onMouseMoveWrapper_.concat(Blockly.bindEvent_(document,"mousemove",null,Blockly.onMouseMove_))),a.stopPropagation(),a.preventDefault())}; Blockly.WorkspaceSvg.prototype.startDrag=function(a,b){var c=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());c.x/=this.scale;c.y/=this.scale;this.dragDeltaXY_=goog.math.Coordinate.difference(b,c)};Blockly.WorkspaceSvg.prototype.moveDrag=function(a){a=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());a.x/=this.scale;a.y/=this.scale;return goog.math.Coordinate.sum(this.dragDeltaXY_,a)}; -Blockly.WorkspaceSvg.prototype.onMouseWheel_=function(a){Blockly.terminateDrag_();var b=0b.bottomRight.x&&(b.bottomRight.x=d.bottomRight.x);d.topLeft.yb.bottomRight.y&&(b.bottomRight.y=d.bottomRight.y)}return{x:b.topLeft.x,y:b.topLeft.y,width:b.bottomRight.x- +Blockly.WorkspaceSvg.prototype.isDragging=function(){return Blockly.dragMode_==Blockly.DRAG_FREE||this.isScrolling};Blockly.WorkspaceSvg.prototype.onMouseWheel_=function(a){Blockly.terminateDrag_();var b=0b.bottomRight.x&&(b.bottomRight.x=d.bottomRight.x);d.topLeft.yb.bottomRight.y&&(b.bottomRight.y=d.bottomRight.y)}return{x:b.topLeft.x,y:b.topLeft.y,width:b.bottomRight.x- b.topLeft.x,height:b.bottomRight.y-b.topLeft.y}};Blockly.WorkspaceSvg.prototype.cleanUp_=function(){Blockly.Events.setGroup(!0);for(var a=this.getTopBlocks(!0),b=0,c=0,d;d=a[c];c++){var e=d.getRelativeToSurfaceXY();d.moveBy(-e.x,b-e.y);d.snapToGrid();b=d.getRelativeToSurfaceXY().y+d.getHeightWidth().height+Blockly.BlockSvg.MIN_BLOCK_Y}Blockly.Events.setGroup(!1);Blockly.resizeSvgContents(this)}; Blockly.WorkspaceSvg.prototype.showContextMenu_=function(a){function b(a){if(a.isDeletable())l=l.concat(a.getDescendants());else{a=a.getChildren();for(var c=0;c=b.height&&(k-=f.height);c?f.width>=a.clientX&&(g+=f.width):a.clientX+f.width>=b.width&&(g-=f.width);Blockly.WidgetDiv.position(g,k,b,e,c);d.setAllowAutoFocus(!0);setTimeout(function(){h.focus()},1);Blockly.ContextMenu.currentBlock=null}else Blockly.ContextMenu.hide()}; Blockly.ContextMenu.hide=function(){Blockly.WidgetDiv.hideIfOwner(Blockly.ContextMenu);Blockly.ContextMenu.currentBlock=null}; -Blockly.ContextMenu.callbackFactory=function(a,b){return function(){Blockly.Events.disable();var c=Blockly.Xml.domToBlock(b,a.workspace),d=a.getRelativeToSurfaceXY();d.x=a.RTL?d.x-Blockly.SNAP_RADIUS:d.x+Blockly.SNAP_RADIUS;d.y+=2*Blockly.SNAP_RADIUS;c.moveBy(d.x,d.y);Blockly.Events.enable();Blockly.Events.isEnabled()&&!c.isShadow()&&Blockly.Events.fire(new Blockly.Events.Create(c));c.select()}};Blockly.RenderedConnection=function(a,b){Blockly.RenderedConnection.superClass_.constructor.call(this,a,b);this.offsetInBlock_=new goog.math.Coordinate(0,0)};goog.inherits(Blockly.RenderedConnection,Blockly.Connection);Blockly.RenderedConnection.prototype.distanceFrom=function(a){var b=this.x_-a.x_;a=this.y_-a.y_;return Math.sqrt(b*b+a*a)}; +Blockly.ContextMenu.callbackFactory=function(a,b){return function(){Blockly.Events.disable();try{var c=Blockly.Xml.domToBlock(b,a.workspace),d=a.getRelativeToSurfaceXY();d.x=a.RTL?d.x-Blockly.SNAP_RADIUS:d.x+Blockly.SNAP_RADIUS;d.y+=2*Blockly.SNAP_RADIUS;c.moveBy(d.x,d.y)}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!c.isShadow()&&Blockly.Events.fire(new Blockly.Events.Create(c));c.select()}};Blockly.RenderedConnection=function(a,b){Blockly.RenderedConnection.superClass_.constructor.call(this,a,b);this.offsetInBlock_=new goog.math.Coordinate(0,0)};goog.inherits(Blockly.RenderedConnection,Blockly.Connection);Blockly.RenderedConnection.prototype.distanceFrom=function(a){var b=this.x_-a.x_;a=this.y_-a.y_;return Math.sqrt(b*b+a*a)}; Blockly.RenderedConnection.prototype.bumpAwayFrom_=function(a){if(Blockly.dragMode_==Blockly.DRAG_NONE){var b=this.sourceBlock_.getRootBlock();if(!b.isInFlyout){var c=!1;if(!b.isMovable()){b=a.getSourceBlock().getRootBlock();if(!b.isMovable())return;a=this;c=!0}var d=Blockly.selected==b;d||b.select();var e=a.x_+Blockly.SNAP_RADIUS-this.x_;a=a.y_+Blockly.SNAP_RADIUS-this.y_;c&&(a=-a);b.RTL&&(e=-e);b.moveBy(e,a);d||b.unselect()}}}; Blockly.RenderedConnection.prototype.moveTo=function(a,b){this.inDB_&&this.db_.removeConnection_(this);this.x_=a;this.y_=b;this.hidden_||this.db_.addConnection(this)};Blockly.RenderedConnection.prototype.moveBy=function(a,b){this.moveTo(this.x_+a,this.y_+b)};Blockly.RenderedConnection.prototype.moveToOffset=function(a){this.moveTo(a.x+this.offsetInBlock_.x,a.y+this.offsetInBlock_.y)}; Blockly.RenderedConnection.prototype.setOffsetInBlock=function(a,b){this.offsetInBlock_.x=a;this.offsetInBlock_.y=b};Blockly.RenderedConnection.prototype.tighten_=function(){var a=this.targetConnection.x_-this.x_,b=this.targetConnection.y_-this.y_;if(0!=a||0!=b){var c=this.targetBlock(),d=c.getSvgRoot();if(!d)throw"block is not rendered.";d=Blockly.getRelativeXY_(d);c.getSvgRoot().setAttribute("transform","translate("+(d.x-a)+","+(d.y-b)+")");c.moveConnections_(-a,-b)}}; @@ -1120,13 +1121,14 @@ Blockly.RenderedConnection.prototype.closest=function(a,b,c){return this.dbOppos Blockly.RenderedConnection.prototype.highlight=function(){var a;a=this.type==Blockly.INPUT_VALUE||this.type==Blockly.OUTPUT_VALUE?"m 0,0 "+Blockly.BlockSvg.TAB_PATH_DOWN+" v 5":"m -20,0 h 5 "+Blockly.BlockSvg.NOTCH_PATH_LEFT+" h 5";var b=this.sourceBlock_.getRelativeToSurfaceXY();Blockly.Connection.highlightedPath_=Blockly.createSvgElement("path",{"class":"blocklyHighlightedConnectionPath",d:a,transform:"translate("+(this.x_-b.x)+","+(this.y_-b.y)+")"+(this.sourceBlock_.RTL?" scale(-1 1)":"")},this.sourceBlock_.getSvgRoot())}; Blockly.RenderedConnection.prototype.unhideAll=function(){this.setHidden(!1);var a=[];if(this.type!=Blockly.INPUT_VALUE&&this.type!=Blockly.NEXT_STATEMENT)return a;var b=this.targetBlock();if(b){var c;b.isCollapsed()?(c=[],b.outputConnection&&c.push(b.outputConnection),b.nextConnection&&c.push(b.nextConnection),b.previousConnection&&c.push(b.previousConnection)):c=b.getConnections_(!0);for(var d=0;db?!1:Blockly.RenderedConnection.superClass_.isConnectionAllowed.call(this,a)}; +Blockly.RenderedConnection.prototype.hideAll=function(){this.setHidden(!0);if(this.targetConnection)for(var a=this.targetBlock().getDescendants(),b=0;bb?!1:Blockly.RenderedConnection.superClass_.isConnectionAllowed.call(this,a)}; Blockly.RenderedConnection.prototype.disconnectInternal_=function(a,b){Blockly.RenderedConnection.superClass_.disconnectInternal_.call(this,a,b);a.rendered&&a.render();b.rendered&&(b.updateDisabled(),b.render())}; Blockly.RenderedConnection.prototype.respawnShadow_=function(){var a=this.getSourceBlock(),b=this.getShadowDom();if(a.workspace&&b&&Blockly.Events.recordUndo){Blockly.RenderedConnection.superClass_.respawnShadow_.call(this);b=this.targetBlock();if(!b)throw"Couldn't respawn the shadow block that should exist here.";b.initSvg();b.render(!1);a.rendered&&a.render()}};Blockly.RenderedConnection.prototype.neighbours_=function(a){return this.dbOpposite_.getNeighbours(this,a)}; Blockly.RenderedConnection.prototype.connect_=function(a){Blockly.RenderedConnection.superClass_.connect_.call(this,a);var b=this.getSourceBlock();a=a.getSourceBlock();b.rendered&&b.updateDisabled();a.rendered&&a.updateDisabled();b.rendered&&a.rendered&&(this.type==Blockly.NEXT_STATEMENT||this.type==Blockly.PREVIOUS_STATEMENT?a.render():b.render())};Blockly.BlockSvg=function(a,b,c){this.svgGroup_=Blockly.createSvgElement("g",{},null);this.svgPathDark_=Blockly.createSvgElement("path",{"class":"blocklyPathDark",transform:"translate(1,1)"},this.svgGroup_);this.svgPath_=Blockly.createSvgElement("path",{"class":"blocklyPath"},this.svgGroup_);this.svgPathLight_=Blockly.createSvgElement("path",{"class":"blocklyPathLight"},this.svgGroup_);this.svgPath_.tooltip=this;this.rendered=!1;Blockly.Tooltip.bindMouseEvents(this.svgPath_);Blockly.BlockSvg.superClass_.constructor.call(this, a,b,c)};goog.inherits(Blockly.BlockSvg,Blockly.Block);Blockly.BlockSvg.prototype.height=0;Blockly.BlockSvg.prototype.width=0;Blockly.BlockSvg.prototype.dragStartXY_=null;Blockly.BlockSvg.INLINE=-1; Blockly.BlockSvg.prototype.initSvg=function(){goog.asserts.assert(this.workspace.rendered,"Workspace is headless.");for(var a=0,b;b=this.inputList[a];a++)b.init();b=this.getIcons();for(a=0;ac;c+=15)Blockly.createSvgElement("line",{x1:Blockly.FieldAngle.HALF+Blockly.FieldAngle.RADIUS,y1:Blockly.FieldAngle.HALF,x2:Blockly.FieldAngle.HALF+Blockly.FieldAngle.RADIUS- (0==c%45?10:5),y2:Blockly.FieldAngle.HALF,"class":"blocklyAngleMarks",transform:"rotate("+c+","+Blockly.FieldAngle.HALF+","+Blockly.FieldAngle.HALF+")"},a);a.style.marginLeft=15-Blockly.FieldAngle.RADIUS+"px";this.clickWrapper_=Blockly.bindEvent_(a,"click",this,Blockly.WidgetDiv.hide);this.moveWrapper1_=Blockly.bindEvent_(b,"mousemove",this,this.onMouseMove);this.moveWrapper2_=Blockly.bindEvent_(this.gauge_,"mousemove",this,this.onMouseMove);this.updateGraph_()}}; @@ -1253,40 +1253,37 @@ Blockly.FieldAngle.prototype.onMouseMove=function(a){var b=this.gauge_.ownerSVGE b,this.setValue(b),this.validate_(),this.resizeEditor_())};Blockly.FieldAngle.prototype.setText=function(a){Blockly.FieldAngle.superClass_.setText.call(this,a);this.textElement_&&(this.updateGraph_(),this.sourceBlock_.RTL?this.textElement_.insertBefore(this.symbol_,this.textElement_.firstChild):this.textElement_.appendChild(this.symbol_),this.size_.width=0)}; Blockly.FieldAngle.prototype.updateGraph_=function(){if(this.gauge_){var a=Number(this.getText())+Blockly.FieldAngle.OFFSET,b=goog.math.toRadians(a),a=["M ",Blockly.FieldAngle.HALF,",",Blockly.FieldAngle.HALF],c=Blockly.FieldAngle.HALF,d=Blockly.FieldAngle.HALF;if(!isNaN(b)){var e=goog.math.toRadians(Blockly.FieldAngle.OFFSET),f=Math.cos(e)*Blockly.FieldAngle.RADIUS,g=Math.sin(e)*-Blockly.FieldAngle.RADIUS;Blockly.FieldAngle.CLOCKWISE&&(b=2*e-b);c+=Math.cos(b)*Blockly.FieldAngle.RADIUS;d-=Math.sin(b)* Blockly.FieldAngle.RADIUS;b=Math.abs(Math.floor((b-e)/Math.PI)%2);Blockly.FieldAngle.CLOCKWISE&&(b=1-b);a.push(" l ",f,",",g," A ",Blockly.FieldAngle.RADIUS,",",Blockly.FieldAngle.RADIUS," 0 ",b," ",Number(Blockly.FieldAngle.CLOCKWISE)," ",c,",",d," z")}this.gauge_.setAttribute("d",a.join(""));this.line_.setAttribute("x2",c);this.line_.setAttribute("y2",d)}}; -Blockly.FieldAngle.angleValidator=function(a){if(null===a)return null;a=parseFloat(a||0);if(isNaN(a))return null;a%=360;0>a&&(a+=360);a>Blockly.FieldAngle.WRAP&&(a-=360);return String(a)};Blockly.FieldCheckbox=function(a,b){Blockly.FieldCheckbox.superClass_.constructor.call(this,"",b);this.setValue(a)};goog.inherits(Blockly.FieldCheckbox,Blockly.Field);Blockly.FieldCheckbox.CHECK_CHAR="\u2713";Blockly.FieldCheckbox.prototype.CURSOR="default"; +Blockly.FieldAngle.classValidator=function(a){if(null===a)return null;a=parseFloat(a||0);if(isNaN(a))return null;a%=360;0>a&&(a+=360);a>Blockly.FieldAngle.WRAP&&(a-=360);return String(a)};Blockly.FieldCheckbox=function(a,b){Blockly.FieldCheckbox.superClass_.constructor.call(this,"",b);this.setValue(a)};goog.inherits(Blockly.FieldCheckbox,Blockly.Field);Blockly.FieldCheckbox.CHECK_CHAR="\u2713";Blockly.FieldCheckbox.prototype.CURSOR="default"; Blockly.FieldCheckbox.prototype.init=function(){if(!this.fieldGroup_){Blockly.FieldCheckbox.superClass_.init.call(this);this.checkElement_=Blockly.createSvgElement("text",{"class":"blocklyText blocklyCheckbox",x:-3,y:14},this.fieldGroup_);var a=document.createTextNode(Blockly.FieldCheckbox.CHECK_CHAR);this.checkElement_.appendChild(a);this.checkElement_.style.display=this.state_?"block":"none"}};Blockly.FieldCheckbox.prototype.getValue=function(){return String(this.state_).toUpperCase()}; -Blockly.FieldCheckbox.prototype.setValue=function(a){a="TRUE"==a;this.state_!==a&&(this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,this.state_,a)),this.state_=a,this.checkElement_&&(this.checkElement_.style.display=a?"block":"none"))};Blockly.FieldCheckbox.prototype.showEditor_=function(){var a=!this.state_;if(this.sourceBlock_&&this.validator_){var b=this.validator_(a);void 0!==b&&(a=b)}null!==a&&this.setValue(String(a).toUpperCase())};Blockly.FieldColour=function(a,b){Blockly.FieldColour.superClass_.constructor.call(this,a,b);this.setText(Blockly.Field.NBSP+Blockly.Field.NBSP+Blockly.Field.NBSP)};goog.inherits(Blockly.FieldColour,Blockly.Field);Blockly.FieldColour.prototype.colours_=null;Blockly.FieldColour.prototype.columns_=0;Blockly.FieldColour.prototype.init=function(){Blockly.FieldColour.superClass_.init.call(this);this.borderRect_.style.fillOpacity=1;this.setValue(this.getValue())};Blockly.FieldColour.prototype.CURSOR="default"; +Blockly.FieldCheckbox.prototype.setValue=function(a){a="TRUE"==a;this.state_!==a&&(this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,this.state_,a)),this.state_=a,this.checkElement_&&(this.checkElement_.style.display=a?"block":"none"))};Blockly.FieldCheckbox.prototype.showEditor_=function(){var a=!this.state_;this.sourceBlock_&&(a=this.callValidator(a));null!==a&&this.setValue(String(a).toUpperCase())};Blockly.FieldColour=function(a,b){Blockly.FieldColour.superClass_.constructor.call(this,a,b);this.setText(Blockly.Field.NBSP+Blockly.Field.NBSP+Blockly.Field.NBSP)};goog.inherits(Blockly.FieldColour,Blockly.Field);Blockly.FieldColour.prototype.colours_=null;Blockly.FieldColour.prototype.columns_=0;Blockly.FieldColour.prototype.init=function(){Blockly.FieldColour.superClass_.init.call(this);this.borderRect_.style.fillOpacity=1;this.setValue(this.getValue())};Blockly.FieldColour.prototype.CURSOR="default"; Blockly.FieldColour.prototype.dispose=function(){Blockly.WidgetDiv.hideIfOwner(this);Blockly.FieldColour.superClass_.dispose.call(this)};Blockly.FieldColour.prototype.getValue=function(){return this.colour_};Blockly.FieldColour.prototype.setValue=function(a){this.sourceBlock_&&Blockly.Events.isEnabled()&&this.colour_!=a&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,this.colour_,a));this.colour_=a;this.borderRect_&&(this.borderRect_.style.fill=a)}; Blockly.FieldColour.prototype.getText=function(){var a=this.colour_,b=a.match(/^#(.)\1(.)\2(.)\3$/);b&&(a="#"+b[1]+b[2]+b[3]);return a};Blockly.FieldColour.COLOURS=goog.ui.ColorPicker.SIMPLE_GRID_COLORS;Blockly.FieldColour.COLUMNS=7;Blockly.FieldColour.prototype.setColours=function(a){this.colours_=a;return this};Blockly.FieldColour.prototype.setColumns=function(a){this.columns_=a;return this}; Blockly.FieldColour.prototype.showEditor_=function(){Blockly.WidgetDiv.show(this,this.sourceBlock_.RTL,Blockly.FieldColour.widgetDispose_);var a=new goog.ui.ColorPicker;a.setSize(this.columns_||Blockly.FieldColour.COLUMNS);a.setColors(this.colours_||Blockly.FieldColour.COLOURS);var b=goog.dom.getViewportSize(),c=goog.style.getViewportPageOffset(document),d=this.getAbsoluteXY_(),e=this.getScaledBBox_();a.render(Blockly.WidgetDiv.DIV);a.setSelectedColor(this.getValue());var f=goog.style.getSize(a.getElement()); -d.y=d.y+f.height+e.height>=b.height+c.y?d.y-(f.height-1):d.y+(e.height-1);this.sourceBlock_.RTL?(d.x+=e.width,d.x-=f.width,d.xb.width+c.x-f.width&&(d.x=b.width+c.x-f.width);Blockly.WidgetDiv.position(d.x,d.y,b,c,this.sourceBlock_.RTL);var g=this;Blockly.FieldColour.changeEventKey_=goog.events.listen(a,goog.ui.ColorPicker.EventType.CHANGE,function(a){a=a.target.getSelectedColor()||"#000000";Blockly.WidgetDiv.hide();if(g.sourceBlock_&&g.validator_){var b=g.validator_(a);void 0!== -b&&(a=b)}null!==a&&g.setValue(a)})};Blockly.FieldColour.widgetDispose_=function(){Blockly.FieldColour.changeEventKey_&&goog.events.unlistenByKey(Blockly.FieldColour.changeEventKey_)};Blockly.FieldDropdown=function(a,b){this.menuGenerator_=a;this.trimOptions_();var c=this.getOptions_()[0];Blockly.FieldDropdown.superClass_.constructor.call(this,c[1],b)};goog.inherits(Blockly.FieldDropdown,Blockly.Field);Blockly.FieldDropdown.CHECKMARK_OVERHANG=25;Blockly.FieldDropdown.ARROW_CHAR=goog.userAgent.ANDROID?"\u25bc":"\u25be";Blockly.FieldDropdown.prototype.CURSOR="default"; +d.y=d.y+f.height+e.height>=b.height+c.y?d.y-(f.height-1):d.y+(e.height-1);this.sourceBlock_.RTL?(d.x+=e.width,d.x-=f.width,d.xb.width+c.x-f.width&&(d.x=b.width+c.x-f.width);Blockly.WidgetDiv.position(d.x,d.y,b,c,this.sourceBlock_.RTL);var g=this;Blockly.FieldColour.changeEventKey_=goog.events.listen(a,goog.ui.ColorPicker.EventType.CHANGE,function(a){a=a.target.getSelectedColor()||"#000000";Blockly.WidgetDiv.hide();g.sourceBlock_&&(a=g.callValidator(a));null!==a&&g.setValue(a)})}; +Blockly.FieldColour.widgetDispose_=function(){Blockly.FieldColour.changeEventKey_&&goog.events.unlistenByKey(Blockly.FieldColour.changeEventKey_)};Blockly.FieldDropdown=function(a,b){this.menuGenerator_=a;this.trimOptions_();var c=this.getOptions_()[0];Blockly.FieldDropdown.superClass_.constructor.call(this,c[1],b)};goog.inherits(Blockly.FieldDropdown,Blockly.Field);Blockly.FieldDropdown.CHECKMARK_OVERHANG=25;Blockly.FieldDropdown.ARROW_CHAR=goog.userAgent.ANDROID?"\u25bc":"\u25be";Blockly.FieldDropdown.prototype.CURSOR="default"; Blockly.FieldDropdown.prototype.init=function(){if(!this.fieldGroup_){this.arrow_=Blockly.createSvgElement("tspan",{},null);this.arrow_.appendChild(document.createTextNode(this.sourceBlock_.RTL?Blockly.FieldDropdown.ARROW_CHAR+" ":" "+Blockly.FieldDropdown.ARROW_CHAR));Blockly.FieldDropdown.superClass_.init.call(this);var a=this.text_;this.text_=null;this.setText(a)}}; -Blockly.FieldDropdown.prototype.showEditor_=function(){Blockly.WidgetDiv.show(this,this.sourceBlock_.RTL,null);var a=this,b=new goog.ui.Menu;b.setRightToLeft(this.sourceBlock_.RTL);for(var c=this.getOptions_(),d=0;d=c.height+d.y?e.y-(h.height+2):e.y+f.height;this.sourceBlock_.RTL?(e.x+=f.width,e.x+=Blockly.FieldDropdown.CHECKMARK_OVERHANG,e.xc.width+d.x-h.width&&(e.x=c.width+d.x-h.width));Blockly.WidgetDiv.position(e.x,e.y,c,d,this.sourceBlock_.RTL);b.setAllowAutoFocus(!0); -g.focus()}; -Blockly.FieldDropdown.prototype.trimOptions_=function(){this.suffixField=this.prefixField=null;var a=this.menuGenerator_;if(goog.isArray(a)&&!(2>a.length)){var b=a.map(function(a){return a[0]}),c=Blockly.shortestStringLength(b),d=Blockly.commonWordPrefix(b,c),e=Blockly.commonWordSuffix(b,c);if((d||e)&&!(c<=d+e)){d&&(this.prefixField=b[0].substring(0,d-1));e&&(this.suffixField=b[0].substr(1-e));b=[];for(c=0;c=c.height+d.y?e.y-(h.height+2):e.y+f.height;this.sourceBlock_.RTL?(e.x+=f.width,e.x+=Blockly.FieldDropdown.CHECKMARK_OVERHANG,e.xc.width+d.x-h.width&&(e.x=c.width+d.x-h.width));Blockly.WidgetDiv.position(e.x,e.y,c,d,this.sourceBlock_.RTL);b.setAllowAutoFocus(!0);g.focus()}; +Blockly.FieldDropdown.prototype.trimOptions_=function(){this.suffixField=this.prefixField=null;var a=this.menuGenerator_;if(goog.isArray(a)&&!(2>a.length)){var b=a.map(function(a){return a[0]}),c=Blockly.shortestStringLength(b),d=Blockly.commonWordPrefix(b,c),e=Blockly.commonWordSuffix(b,c);if((d||e)&&!(c<=d+e)){d&&(this.prefixField=b[0].substring(0,d-1));e&&(this.suffixField=b[0].substr(1-e));b=[];for(c=0;cb.contentWidth-b.viewWidth)){var c=a.clientX-this.startDragMouseX_;this.startDragMouseX_=a.clientX;a=b.viewLeft-c;a=goog.math.clamp(a,0,b.contentWidth-b.viewWidth);this.scrollbar_.set(a)}}else 0>b.contentHeight-b.viewHeight||(c=a.clientY-this.startDragMouseY_,this.startDragMouseY_=a.clientY,a=b.viewTop-c,a=goog.math.clamp(a,0,b.contentHeight-b.viewHeight),this.scrollbar_.set(a))}; -Blockly.Flyout.prototype.onMouseMoveBlock_=function(a){if("mousemove"==a.type&&1>=a.clientX&&0==a.clientY&&0==a.button)a.stopPropagation();else{var b=a.clientX-Blockly.Flyout.startDownEvent_.clientX;a=a.clientY-Blockly.Flyout.startDownEvent_.clientY;Math.sqrt(b*b+a*a)>Blockly.DRAG_RADIUS&&Blockly.Flyout.startFlyout_.createBlockFunc_(Blockly.Flyout.startBlock_)(Blockly.Flyout.startDownEvent_)}}; -Blockly.Flyout.prototype.createBlockFunc_=function(a){var b=this;return function(c){if(!Blockly.isRightButton(c)&&!a.disabled){Blockly.Events.disable();var d=b.placeNewBlock_(a);Blockly.Events.enable();Blockly.Events.isEnabled()&&(Blockly.Events.setGroup(!0),Blockly.Events.fire(new Blockly.Events.Create(d)));b.autoClose?b.hide():b.filterForCapacity_();d.onMouseDown_(c);Blockly.dragMode_=Blockly.DRAG_FREE;d.setDragging_(!0)}}}; +Blockly.Flyout.prototype.onMouseMoveBlock_=function(a){if(!("mousemove"==a.type&&1>=a.clientX&&0==a.clientY&&0==a.button))if(this.determineDragIntention_(a.clientX-Blockly.Flyout.startDownEvent_.clientX,a.clientY-Blockly.Flyout.startDownEvent_.clientY))this.createBlockFunc_(Blockly.Flyout.startBlock_)(Blockly.Flyout.startDownEvent_);else if(this.dragMode_==Blockly.DRAG_FREE)this.onMouseMove_(a);a.stopPropagation()}; +Blockly.Flyout.prototype.determineDragIntention_=function(a,b){if(this.dragMode_==Blockly.DRAG_FREE)return!1;if(Math.sqrt(a*a+b*b)90-e&&(d=!0):c>-90-e&&c<-90+e&&(d=!0);else if(this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT)c-e&&(d=!0);else if(c<-180+e||c>180-e)d=!0;return d}; +Blockly.Flyout.prototype.createBlockFunc_=function(a){var b=this;return function(c){if(!Blockly.isRightButton(c)&&!a.disabled){Blockly.Events.disable();try{var d=b.placeNewBlock_(a)}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&(Blockly.Events.setGroup(!0),Blockly.Events.fire(new Blockly.Events.Create(d)));b.autoClose?b.hide():b.filterForCapacity_();d.onMouseDown_(c);Blockly.dragMode_=Blockly.DRAG_FREE;d.setDragging_(!0)}}}; Blockly.Flyout.prototype.placeNewBlock_=function(a){var b=this.targetWorkspace_,c=a.getSvgRoot();if(!c)throw"originBlock is not rendered.";var c=Blockly.getSvgXY_(c,b),d=this.workspace_.scrollX,e=this.workspace_.scale;c.x+=d/e-d;this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT&&(d=b.getMetrics().viewWidth-this.width_,e=b.scale,c.x+=d/e-d);d=this.workspace_.scrollY;e=this.workspace_.scale;c.y+=d/e-d;this.toolboxPosition_==Blockly.TOOLBOX_AT_BOTTOM&&(d=b.getMetrics().viewHeight-this.height_,e=b.scale, c.y+=d/e-d);a=Blockly.Xml.blockToDom(a);a=Blockly.Xml.domToBlock(a,b);e=a.getSvgRoot();if(!e)throw"block is not rendered.";e=Blockly.getSvgXY_(e,b);e.x+=b.scrollX/b.scale-b.scrollX;e.y+=b.scrollY/b.scale-b.scrollY;b.toolbox_&&!b.scrollbar&&(e.x+=b.toolbox_.getWidth()/b.scale,e.y+=b.toolbox_.getHeight()/b.scale);a.moveBy(c.x-e.x,c.y-e.y);return a}; Blockly.Flyout.prototype.filterForCapacity_=function(){for(var a=this.targetWorkspace_.remainingCapacity(),b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)if(-1==this.permanentlyDisabled_.indexOf(d)){var e=d.getDescendants();d.setDisabled(e.length>a)}}; Blockly.Flyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.left,c=a.top,d=a.width,a=a.height;return this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP?new goog.math.Rect(-1E9,c-1E9,2E9,1E9+a):this.toolboxPosition_==Blockly.TOOLBOX_AT_BOTTOM?new goog.math.Rect(-1E9,c,2E9,1E9+a):this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT?new goog.math.Rect(b-1E9,-1E9,1E9+d,2E9):new goog.math.Rect(b,-1E9,1E9+d,2E9)}; -Blockly.Flyout.terminateDrag_=function(){Blockly.Flyout.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_),Blockly.Flyout.onMouseUpWrapper_=null);Blockly.Flyout.onMouseMoveBlockWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveBlockWrapper_),Blockly.Flyout.onMouseMoveBlockWrapper_=null);Blockly.Flyout.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveWrapper_),Blockly.Flyout.onMouseMoveWrapper_=null);Blockly.Flyout.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_), -Blockly.Flyout.onMouseUpWrapper_=null);Blockly.Flyout.startDownEvent_=null;Blockly.Flyout.startBlock_=null;Blockly.Flyout.startFlyout_=null}; +Blockly.Flyout.terminateDrag_=function(){Blockly.Flyout.startFlyout_&&(Blockly.Flyout.startFlyout_.dragMode_=Blockly.DRAG_NONE);Blockly.Flyout.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_),Blockly.Flyout.onMouseUpWrapper_=null);Blockly.Flyout.onMouseMoveBlockWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveBlockWrapper_),Blockly.Flyout.onMouseMoveBlockWrapper_=null);Blockly.Flyout.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveWrapper_), +Blockly.Flyout.onMouseMoveWrapper_=null);Blockly.Flyout.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_),Blockly.Flyout.onMouseUpWrapper_=null);Blockly.Flyout.startDownEvent_=null;Blockly.Flyout.startBlock_=null;Blockly.Flyout.startFlyout_=null}; Blockly.Flyout.prototype.reflowHorizontal=function(a){this.workspace_.scale=this.targetWorkspace_.scale;for(var b=0,c=0,d;d=a[c];c++)b=Math.max(b,d.getHeightWidth().height);b+=1.5*this.MARGIN;b*=this.workspace_.scale;b+=Blockly.Scrollbar.scrollbarThickness;if(this.height_!=b){for(c=0;d=a[c];c++){var e=d.getHeightWidth();if(d.flyoutRect_){d.flyoutRect_.setAttribute("width",e.width);d.flyoutRect_.setAttribute("height",e.height);var f=d.outputConnection?Blockly.BlockSvg.TAB_WIDTH:0,g=d.getRelativeToSurfaceXY(); d.flyoutRect_.setAttribute("y",g.y);d.flyoutRect_.setAttribute("x",this.RTL?g.x-e.width+f:g.x-f);(e=d.startHat_?Blockly.BlockSvg.START_HAT_HEIGHT:0)&&d.moveBy(0,e);d.flyoutRect_.setAttribute("y",g.y)}}this.height_=b;this.targetWorkspace_.resize()}}; Blockly.Flyout.prototype.reflowVertical=function(a){this.workspace_.scale=this.targetWorkspace_.scale;for(var b=0,c=0,d;d=a[c];c++){var e=d.getHeightWidth().width;d.outputConnection&&(e-=Blockly.BlockSvg.TAB_WIDTH);b=Math.max(b,e)}b+=1.5*this.MARGIN+Blockly.BlockSvg.TAB_WIDTH;b*=this.workspace_.scale;b+=Blockly.Scrollbar.scrollbarThickness;if(this.width_!=b){for(c=0;d=a[c];c++){e=d.getHeightWidth();if(this.RTL){var f=d.getRelativeToSurfaceXY().x,g=b/this.workspace_.scale-this.MARGIN,g=g-Blockly.BlockSvg.TAB_WIDTH; @@ -1346,10 +1346,10 @@ Blockly.Toolbox.prototype.init=function(){var a=this.workspace_;this.HtmlDiv=goo toolboxPosition:a.options.toolboxPosition});goog.dom.insertSiblingAfter(this.flyout_.createDom(),a.svgGroup_);this.flyout_.init(a);this.config_.cleardotPath=a.options.pathToMedia+"1x1.gif";this.config_.cssCollapsedFolderIcon="blocklyTreeIconClosed"+(a.RTL?"Rtl":"Ltr");var b=new Blockly.Toolbox.TreeControl(this,this.config_);this.tree_=b;b.setShowRootNode(!1);b.setShowLines(!1);b.setShowExpandIcons(!1);b.setSelectedItem(null);this.populate_(a.options.languageTree);b.render(this.HtmlDiv);this.addColour_(); this.position()};Blockly.Toolbox.prototype.dispose=function(){this.flyout_.dispose();this.tree_.dispose();goog.dom.removeNode(this.HtmlDiv);this.lastCategory_=this.workspace_=null};Blockly.Toolbox.prototype.getWidth=function(){return this.width};Blockly.Toolbox.prototype.getHeight=function(){return this.height}; Blockly.Toolbox.prototype.position=function(){var a=this.HtmlDiv;if(a){var b=this.workspace_.getParentSvg(),c=goog.style.getPageOffset(b),b=Blockly.svgSize(b);this.horizontalLayout_?(a.style.left=c.x+"px",a.style.height="auto",a.style.width=b.width+"px",this.height=a.offsetHeight,a.style.top=this.toolboxPosition==Blockly.TOOLBOX_AT_TOP?c.y+"px":c.y+b.height-a.offsetHeight+"px"):(a.style.left=this.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?c.x+b.width-a.offsetWidth+"px":c.x+"px",a.style.height=b.height+ -"px",a.style.top=c.y+"px",this.width=a.offsetWidth,this.toolboxPosition==Blockly.TOOLBOX_AT_LEFT&&--this.width);this.flyout_.position()}}; -Blockly.Toolbox.prototype.populate_=function(a){function b(a,g,h){for(var k=null,m=0,p;p=a.childNodes[m];m++)if(p.tagName)switch(p.tagName.toUpperCase()){case "CATEGORY":k=c.createNode(p.getAttribute("name"));k.blocks=[];g.add(k);var l=p.getAttribute("custom");l?k.blocks=l:b(p,k,h);l=p.getAttribute("colour");goog.isString(l)?(l.match(/^#[0-9a-fA-F]{6}$/)?k.hexColour=l:k.hexColour=Blockly.hueToRgb(l),e=!0):k.hexColour="";"true"==p.getAttribute("expanded")?(k.blocks.length&&c.setSelectedItem(k),k.setExpanded(!0)): -k.setExpanded(!1);k=p;break;case "SEP":k&&("CATEGORY"==k.tagName.toUpperCase()?g.add(new Blockly.Toolbox.TreeSeparator(d.treeSeparatorConfig_)):(p=parseFloat(p.getAttribute("gap")),isNaN(p)||(l=parseFloat(k.getAttribute("gap")),p=isNaN(l)?p:l+p,k.setAttribute("gap",p))));break;case "BLOCK":case "SHADOW":g.blocks.push(p),k=p}}var c=this.tree_,d=this;c.removeChildren();c.blocks=[];var e=!1;b(a,this.tree_,this.workspace_.options.pathToMedia);this.hasColours_=e;if(c.blocks.length)throw"Toolbox cannot have both blocks and categories in the root level."; -Blockly.resizeSvgContents(this.workspace_)};Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren();for(var b=0,c;c=a[b];b++){var d=c.getRowElement();if(d){var e=this.hasColours_?"8px solid "+(c.hexColour||"#ddd"):"none";this.workspace_.RTL?d.style.borderRight=e:d.style.borderLeft=e}this.addColour_(c)}};Blockly.Toolbox.prototype.clearSelection=function(){this.tree_.setSelectedItem(null)}; +"px",a.style.top=c.y+"px",this.width=a.offsetWidth,this.toolboxPosition==Blockly.TOOLBOX_AT_LEFT&&--this.width);this.flyout_.position()}};Blockly.Toolbox.prototype.populate_=function(a){this.tree_.removeChildren();this.tree_.blocks=[];this.hasColours_=!1;this.syncTrees_(a,this.tree_,this.workspace_.options.pathToMedia);if(this.tree_.blocks.length)throw"Toolbox cannot have both blocks and categories in the root level.";Blockly.resizeSvgContents(this.workspace_)}; +Blockly.Toolbox.prototype.syncTrees_=function(a,b,c){for(var d=null,e=0,f;f=a.childNodes[e];e++)if(f.tagName)switch(f.tagName.toUpperCase()){case "CATEGORY":d=this.tree_.createNode(f.getAttribute("name"));d.blocks=[];b.add(d);var g=f.getAttribute("custom");g?d.blocks=g:this.syncTrees_(f,d,c);g=f.getAttribute("colour");goog.isString(g)?(g.match(/^#[0-9a-fA-F]{6}$/)?d.hexColour=g:d.hexColour=Blockly.hueToRgb(g),this.hasColours_=!0):d.hexColour="";"true"==f.getAttribute("expanded")?(d.blocks.length&& +this.tree_.setSelectedItem(d),d.setExpanded(!0)):d.setExpanded(!1);d=f;break;case "SEP":d&&("CATEGORY"==d.tagName.toUpperCase()?b.add(new Blockly.Toolbox.TreeSeparator(this.treeSeparatorConfig_)):(f=parseFloat(f.getAttribute("gap")),isNaN(f)||(g=parseFloat(d.getAttribute("gap")),f=isNaN(g)?f:g+f,d.setAttribute("gap",f))));break;case "BLOCK":case "SHADOW":b.blocks.push(f),d=f}}; +Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren();for(var b=0,c;c=a[b];b++){var d=c.getRowElement();if(d){var e=this.hasColours_?"8px solid "+(c.hexColour||"#ddd"):"none";this.workspace_.RTL?d.style.borderRight=e:d.style.borderLeft=e}this.addColour_(c)}};Blockly.Toolbox.prototype.clearSelection=function(){this.tree_.setSelectedItem(null)}; Blockly.Toolbox.prototype.getClientRect=function(){if(!this.HtmlDiv)return null;var a=this.HtmlDiv.getBoundingClientRect(),b=a.left,c=a.top,d=a.width,a=a.height;return this.toolboxPosition==Blockly.TOOLBOX_AT_LEFT?new goog.math.Rect(-1E7,-1E7,1E7+b+d,2E7):this.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?new goog.math.Rect(b,-1E7,1E7+d,2E7):this.toolboxPosition==Blockly.TOOLBOX_AT_TOP?new goog.math.Rect(-1E7,-1E7,2E7,1E7+c+a):new goog.math.Rect(0,c,2E7,1E7+d)}; Blockly.Toolbox.TreeControl=function(a,b){this.toolbox_=a;goog.ui.tree.TreeControl.call(this,goog.html.SafeHtml.EMPTY,b)};goog.inherits(Blockly.Toolbox.TreeControl,goog.ui.tree.TreeControl);Blockly.Toolbox.TreeControl.prototype.enterDocument=function(){Blockly.Toolbox.TreeControl.superClass_.enterDocument.call(this);if(goog.events.BrowserFeature.TOUCH_ENABLED){var a=this.getElement();Blockly.bindEvent_(a,goog.events.EventType.TOUCHSTART,this,this.handleTouchEvent_)}}; Blockly.Toolbox.TreeControl.prototype.handleTouchEvent_=function(a){a.preventDefault();var b=this.getNodeFromEvent_(a);b&&a.type===goog.events.EventType.TOUCHSTART&&setTimeout(function(){b.onMouseDown(a)},1)};Blockly.Toolbox.TreeControl.prototype.createNode=function(a){return new Blockly.Toolbox.TreeNode(this.toolbox_,a?goog.html.SafeHtml.htmlEscape(a):goog.html.SafeHtml.EMPTY,this.getConfig(),this.getDomHelper())}; @@ -1412,8 +1412,8 @@ Blockly.onMouseUp_=function(a){a=Blockly.getMainWorkspace();Blockly.Css.setCurso Blockly.onMouseMove_=function(a){if(!(a.touches&&2<=a.touches.length)){var b=Blockly.getMainWorkspace();if(b.isScrolling){var c=a.clientX-b.startDragMouseX,d=a.clientY-b.startDragMouseY,e=b.startDragMetrics,f=b.startScrollX+c,g=b.startScrollY+d,f=Math.min(f,-e.contentLeft),g=Math.min(g,-e.contentTop),f=Math.max(f,e.viewWidth-e.contentLeft-e.contentWidth),g=Math.max(g,e.viewHeight-e.contentTop-e.contentHeight);b.scrollbar.set(-f-e.contentLeft,-g-e.contentTop);Math.sqrt(c*c+d*d)>Blockly.DRAG_RADIUS&& Blockly.longStop_();a.stopPropagation();a.preventDefault()}}}; Blockly.onKeyDown_=function(a){if(!Blockly.mainWorkspace.options.readOnly&&!Blockly.isTargetInput_(a)){var b=!1;if(27==a.keyCode)Blockly.hideChaff();else if(8==a.keyCode||46==a.keyCode)a.preventDefault(),Blockly.selected&&Blockly.selected.isDeletable()&&(b=!0);else if(a.altKey||a.ctrlKey||a.metaKey)Blockly.selected&&Blockly.selected.isDeletable()&&Blockly.selected.isMovable()&&(67==a.keyCode?(Blockly.hideChaff(),Blockly.copy_(Blockly.selected)):88==a.keyCode&&(Blockly.copy_(Blockly.selected),b=!0)), -86==a.keyCode?Blockly.clipboardXml_&&Blockly.clipboardSource_.paste(Blockly.clipboardXml_):90==a.keyCode&&(Blockly.hideChaff(),Blockly.mainWorkspace.undo(a.shiftKey));b&&(Blockly.Events.setGroup(!0),Blockly.hideChaff(),Blockly.selected.dispose(Blockly.dragMode_!=Blockly.DRAG_FREE,!0),Blockly.highlightedConnection_&&(Blockly.highlightedConnection_.unhighlight(),Blockly.highlightedConnection_=null),Blockly.Events.setGroup(!1))}};Blockly.terminateDrag_=function(){Blockly.BlockSvg.terminateDrag();Blockly.Flyout.terminateDrag_()}; -Blockly.longPid_=0;Blockly.longStart_=function(a,b){Blockly.longStop_();Blockly.longPid_=setTimeout(function(){a.button=2;b.onMouseDown_(a)},Blockly.LONGPRESS)};Blockly.longStop_=function(){Blockly.longPid_&&(clearTimeout(Blockly.longPid_),Blockly.longPid_=0)}; +86==a.keyCode?Blockly.clipboardXml_&&(Blockly.Events.setGroup(!0),Blockly.clipboardSource_.paste(Blockly.clipboardXml_),Blockly.Events.setGroup(!1)):90==a.keyCode&&(Blockly.hideChaff(),Blockly.mainWorkspace.undo(a.shiftKey));b&&(Blockly.Events.setGroup(!0),Blockly.hideChaff(),Blockly.selected.dispose(Blockly.dragMode_!=Blockly.DRAG_FREE,!0),Blockly.highlightedConnection_&&(Blockly.highlightedConnection_.unhighlight(),Blockly.highlightedConnection_=null),Blockly.Events.setGroup(!1))}}; +Blockly.terminateDrag_=function(){Blockly.BlockSvg.terminateDrag();Blockly.Flyout.terminateDrag_()};Blockly.longPid_=0;Blockly.longStart_=function(a,b){Blockly.longStop_();Blockly.longPid_=setTimeout(function(){a.button=2;b.onMouseDown_(a)},Blockly.LONGPRESS)};Blockly.longStop_=function(){Blockly.longPid_&&(clearTimeout(Blockly.longPid_),Blockly.longPid_=0)}; Blockly.copy_=function(a){var b=Blockly.Xml.blockToDom(a);Blockly.dragMode_!=Blockly.DRAG_FREE&&Blockly.Xml.deleteNext(b);var c=a.getRelativeToSurfaceXY();b.setAttribute("x",a.RTL?-c.x:c.x);b.setAttribute("y",c.y);Blockly.clipboardXml_=b;Blockly.clipboardSource_=a.workspace};Blockly.duplicate_=function(a){var b=Blockly.clipboardXml_,c=Blockly.clipboardSource_;Blockly.copy_(a);a.workspace.paste(Blockly.clipboardXml_);Blockly.clipboardXml_=b;Blockly.clipboardSource_=c}; Blockly.onContextMenu_=function(a){Blockly.isTargetInput_(a)||a.preventDefault()};Blockly.hideChaff=function(a){Blockly.Tooltip.hide();Blockly.WidgetDiv.hide();a||(a=Blockly.getMainWorkspace(),a.toolbox_&&a.toolbox_.flyout_&&a.toolbox_.flyout_.autoClose&&a.toolbox_.clearSelection())}; Blockly.getMainWorkspaceMetrics_=function(){var a=Blockly.svgSize(this.getParentSvg());if(this.toolbox_)if(this.toolboxPosition==Blockly.TOOLBOX_AT_TOP||this.toolboxPosition==Blockly.TOOLBOX_AT_BOTTOM)a.height-=this.toolbox_.getHeight();else if(this.toolboxPosition==Blockly.TOOLBOX_AT_LEFT||this.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT)a.width-=this.toolbox_.getWidth();var b=Blockly.Flyout.prototype.CORNER_RADIUS-1,c=a.width-b,d=a.height-b,e=this.getBlocksBoundingBox(),f=e.width*this.scale,g=e.height* diff --git a/blocks/lists.js b/blocks/lists.js index af0f6624a..0d23db05f 100644 --- a/blocks/lists.js +++ b/blocks/lists.js @@ -300,7 +300,9 @@ Blockly.Blocks['lists_indexOf'] = { this.appendValueInput('FIND') .appendField(new Blockly.FieldDropdown(OPERATORS), 'END'); this.setInputsInline(true); - this.setTooltip(Blockly.Msg.LISTS_INDEX_OF_TOOLTIP); + var tooltip = Blockly.Msg.LISTS_INDEX_OF_TOOLTIP + .replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '0' : '-1'); + this.setTooltip(tooltip); } }; @@ -343,9 +345,58 @@ Blockly.Blocks['lists_getIndex'] = { // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { - var combo = thisBlock.getFieldValue('MODE') + '_' + - thisBlock.getFieldValue('WHERE'); - return Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_' + combo]; + var mode = thisBlock.getFieldValue('MODE'); + var where = thisBlock.getFieldValue('WHERE'); + var tooltip = ''; + switch (mode + ' ' + where) { + case 'GET FROM_START': + case 'GET FROM_END': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM; + break; + case 'GET FIRST': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST; + break; + case 'GET LAST': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST; + break; + case 'GET RANDOM': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM; + break; + case 'GET_REMOVE FROM_START': + case 'GET_REMOVE FROM_END': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM; + break; + case 'GET_REMOVE FIRST': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST; + break; + case 'GET_REMOVE LAST': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST; + break; + case 'GET_REMOVE RANDOM': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM; + break; + case 'REMOVE FROM_START': + case 'REMOVE FROM_END': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM; + break; + case 'REMOVE FIRST': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST; + break; + case 'REMOVE LAST': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST; + break; + case 'REMOVE RANDOM': + tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM; + break; + } + if (where == 'FROM_START') { + tooltip += ' ' + Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP + .replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '#1' : '#0'); + } else if (where == 'FROM_END') { + tooltip += ' ' + Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP + .replace('%1', '#1'); // The end is always 1-indexed. + } + return tooltip; }); }, /** @@ -470,9 +521,45 @@ Blockly.Blocks['lists_setIndex'] = { // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { - var combo = thisBlock.getFieldValue('MODE') + '_' + - thisBlock.getFieldValue('WHERE'); - return Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_' + combo]; + var mode = thisBlock.getFieldValue('MODE'); + var where = thisBlock.getFieldValue('WHERE'); + var tooltip = ''; + switch (mode + ' ' + where) { + case 'SET FROM_START': + case 'SET FROM_END': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM; + break; + case 'SET FIRST': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST; + break; + case 'SET LAST': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST; + break; + case 'SET RANDOM': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM; + break; + case 'INSERT FROM_START': + case 'INSERT FROM_END': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM; + break; + case 'INSERT FIRST': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST; + break; + case 'INSERT LAST': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST; + break; + case 'INSERT RANDOM': + tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM; + break; + } + if (where == 'FROM_START') { + tooltip += ' ' + Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP + .replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '#1' : '#0'); + } else if (where == 'FROM_END') { + tooltip += ' ' + Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP + .replace('%1', '#1'); // The end is always 1-indexed. + } + return tooltip; }); }, /** diff --git a/blocks/procedures.js b/blocks/procedures.js index 7eb785509..82b383b51 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -59,16 +59,6 @@ Blockly.Blocks['procedures_defnoreturn'] = { this.setStatements_(true); this.statementConnection_ = null; }, - /** - * Initialization of the block has completed, clean up anything that may be - * inconsistent as a result of the XML loading. - * @this Blockly.Block - */ - validate: function() { - var name = Blockly.Procedures.findLegalName( - this.getFieldValue('NAME'), this); - this.setFieldValue(name, 'NAME'); - }, /** * Add or remove the statement block from this function definition. * @param {boolean} hasStatements True if a statement block is needed. @@ -247,16 +237,6 @@ Blockly.Blocks['procedures_defnoreturn'] = { } } }, - /** - * Dispose of any callers. - * @this Blockly.Block - */ - dispose: function() { - var name = this.getFieldValue('NAME'); - Blockly.Procedures.disposeCallers(name, this.workspace); - // Call parent's destructor. - this.constructor.prototype.dispose.apply(this, arguments); - }, /** * Return the signature of this procedure definition. * @return {!Array} Tuple containing three elements: @@ -374,13 +354,11 @@ Blockly.Blocks['procedures_defreturn'] = { this.statementConnection_ = null; }, setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_, - validate: Blockly.Blocks['procedures_defnoreturn'].validate, updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_, mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom, domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation, decompose: Blockly.Blocks['procedures_defnoreturn'].decompose, compose: Blockly.Blocks['procedures_defnoreturn'].compose, - dispose: Blockly.Blocks['procedures_defnoreturn'].dispose, /** * Return the signature of this procedure definition. * @return {!Array} Tuple containing three elements: @@ -674,6 +652,72 @@ Blockly.Blocks['procedures_callnoreturn'] = { } } }, + /** + * Procedure calls cannot exist without the corresponding procedure + * definition. Enforce this link whenever an event is fired. + * @this Blockly.Block + */ + onchange: function(event) { + if (!this.workspace || this.workspace.isFlyout) { + // Block is deleted or is in a flyout. + return; + } + if (event.type == Blockly.Events.CREATE && + event.ids.indexOf(this.id) != -1) { + // Look for the case where a procedure call was created (usually through + // paste) and there is no matching definition. In this case, create + // an empty definition block with the correct signature. + var name = this.getProcedureCall(); + var def = Blockly.Procedures.getDefinition(name, this.workspace); + if (def && (def.type != this.defType_ || + JSON.stringify(def.arguments_) != JSON.stringify(this.arguments_))) { + // The signatures don't match. + def = null; + } + if (!def) { + Blockly.Events.setGroup(event.group); + /** + * Create matching definition block. + * + * + * + * + * + * test + * + * + */ + var xml = goog.dom.createDom('xml'); + var block = goog.dom.createDom('block'); + block.setAttribute('type', this.defType_); + var xy = this.getRelativeToSurfaceXY(); + var x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1); + var y = xy.y + Blockly.SNAP_RADIUS * 2; + block.setAttribute('x', x); + block.setAttribute('y', y); + var mutation = this.mutationToDom(); + block.appendChild(mutation); + var field = goog.dom.createDom('field'); + field.setAttribute('name', 'NAME'); + field.appendChild(document.createTextNode(this.getProcedureCall())); + block.appendChild(field); + xml.appendChild(block); + Blockly.Xml.domToWorkspace(xml, this.workspace); + Blockly.Events.setGroup(false); + } + } else if (event.type == Blockly.Events.DELETE) { + // Look for the case where a procedure definition has been deleted, + // leaving this block (a procedure call) orphaned. In this case, delete + // the orphan. + var name = this.getProcedureCall(); + var def = Blockly.Procedures.getDefinition(name, this.workspace); + if (!def) { + Blockly.Events.setGroup(event.group); + this.dispose(true, false); + Blockly.Events.setGroup(false); + } + } + }, /** * Add menu option to find the definition block for this call. * @param {!Array} options List of menu options to add to. @@ -689,7 +733,8 @@ Blockly.Blocks['procedures_callnoreturn'] = { def && def.select(); }; options.push(option); - } + }, + defType_: 'procedures_defnoreturn' }; Blockly.Blocks['procedures_callreturn'] = { @@ -716,7 +761,10 @@ Blockly.Blocks['procedures_callreturn'] = { mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom, domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation, renameVar: Blockly.Blocks['procedures_callnoreturn'].renameVar, - customContextMenu: Blockly.Blocks['procedures_callnoreturn'].customContextMenu + onchange: Blockly.Blocks['procedures_callnoreturn'].onchange, + customContextMenu: + Blockly.Blocks['procedures_callnoreturn'].customContextMenu, + defType_: 'procedures_defreturn' }; Blockly.Blocks['procedures_ifreturn'] = { diff --git a/blocks/text.js b/blocks/text.js index 030940add..23ff52b32 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -322,7 +322,9 @@ Blockly.Blocks['text_indexOf'] = { this.appendDummyInput().appendField(Blockly.Msg.TEXT_INDEXOF_TAIL); } this.setInputsInline(true); - this.setTooltip(Blockly.Msg.TEXT_INDEXOF_TOOLTIP); + var tooltip = Blockly.Msg.TEXT_INDEXOF_TOOLTIP + .replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '0' : '-1'); + this.setTooltip(tooltip); } }; diff --git a/blocks_compressed.js b/blocks_compressed.js index 042a84ad0..b4f2cd6d1 100644 --- a/blocks_compressed.js +++ b/blocks_compressed.js @@ -10,7 +10,7 @@ this.setOutput(!0,"Colour");this.setTooltip(Blockly.Msg.COLOUR_RGB_TOOLTIP)}}; Blockly.Blocks.colour_blend={init:function(){this.setHelpUrl(Blockly.Msg.COLOUR_BLEND_HELPURL);this.setColour(Blockly.Blocks.colour.HUE);this.appendValueInput("COLOUR1").setCheck("Colour").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.COLOUR_BLEND_TITLE).appendField(Blockly.Msg.COLOUR_BLEND_COLOUR1);this.appendValueInput("COLOUR2").setCheck("Colour").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.COLOUR_BLEND_COLOUR2);this.appendValueInput("RATIO").setCheck("Number").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.COLOUR_BLEND_RATIO); this.setOutput(!0,"Colour");this.setTooltip(Blockly.Msg.COLOUR_BLEND_TOOLTIP)}};Blockly.Blocks.lists={};Blockly.Blocks.lists.HUE=260;Blockly.Blocks.lists_create_empty={init:function(){this.jsonInit({message0:Blockly.Msg.LISTS_CREATE_EMPTY_TITLE,output:"Array",colour:Blockly.Blocks.lists.HUE,tooltip:Blockly.Msg.LISTS_CREATE_EMPTY_TOOLTIP,helpUrl:Blockly.Msg.LISTS_CREATE_EMPTY_HELPURL})}}; Blockly.Blocks.lists_create_with={init:function(){this.setHelpUrl(Blockly.Msg.LISTS_CREATE_WITH_HELPURL);this.setColour(Blockly.Blocks.lists.HUE);this.itemCount_=3;this.updateShape_();this.setOutput(!0,"Array");this.setMutator(new Blockly.Mutator(["lists_create_with_item"]));this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP)},mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=parseInt(a.getAttribute("items"), -10);this.updateShape_()},decompose:function(a){var b=a.newBlock("lists_create_with_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d","LT"],["\u2265","LTE"],["<","GT"],["\u2264","GTE"]],b=[["=","EQ"],["\u2260","NEQ"],["<","LT"],["\u2264","LTE"],[">","GT"],["\u2265","GTE"]],a=this.RTL?a:b;this.setHelpUrl(Blockly.Msg.LOGIC_COMPARE_HELPURL);this.setColour(Blockly.Blocks.logic.HUE);this.setOutput(!0,"Boolean");this.appendValueInput("A");this.appendValueInput("B").appendField(new Blockly.FieldDropdown(a),"OP");this.setInputsInline(!0);var c=this;this.setTooltip(function(){var a= c.getFieldValue("OP");return{EQ:Blockly.Msg.LOGIC_COMPARE_TOOLTIP_EQ,NEQ:Blockly.Msg.LOGIC_COMPARE_TOOLTIP_NEQ,LT:Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LT,LTE:Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LTE,GT:Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GT,GTE:Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GTE}[a]});this.prevBlocks_=[null,null]},onchange:function(a){var b=this.getInputTargetBlock("A"),c=this.getInputTargetBlock("B");if(b&&c&&!b.outputConnection.checkType_(c.outputConnection)){Blockly.Events.setGroup(a.group);for(a=0;a< -this.prevBlocks_.length;a++){var d=this.prevBlocks_[a];if(d===b||d===c)d.unplug(),d.bumpNeighbours_()}Blockly.Events.setGroup(!1)}this.prevBlocks_[0]=b;this.prevBlocks_[1]=c}}; +this.prevBlocks_.length;a++){var e=this.prevBlocks_[a];if(e===b||e===c)e.unplug(),e.bumpNeighbours_()}Blockly.Events.setGroup(!1)}this.prevBlocks_[0]=b;this.prevBlocks_[1]=c}}; Blockly.Blocks.logic_operation={init:function(){var a=[[Blockly.Msg.LOGIC_OPERATION_AND,"AND"],[Blockly.Msg.LOGIC_OPERATION_OR,"OR"]];this.setHelpUrl(Blockly.Msg.LOGIC_OPERATION_HELPURL);this.setColour(Blockly.Blocks.logic.HUE);this.setOutput(!0,"Boolean");this.appendValueInput("A").setCheck("Boolean");this.appendValueInput("B").setCheck("Boolean").appendField(new Blockly.FieldDropdown(a),"OP");this.setInputsInline(!0);var b=this;this.setTooltip(function(){var a=b.getFieldValue("OP");return{AND:Blockly.Msg.LOGIC_OPERATION_TOOLTIP_AND, OR:Blockly.Msg.LOGIC_OPERATION_TOOLTIP_OR}[a]})}};Blockly.Blocks.logic_negate={init:function(){this.jsonInit({message0:Blockly.Msg.LOGIC_NEGATE_TITLE,args0:[{type:"input_value",name:"BOOL",check:"Boolean"}],output:"Boolean",colour:Blockly.Blocks.logic.HUE,tooltip:Blockly.Msg.LOGIC_NEGATE_TOOLTIP,helpUrl:Blockly.Msg.LOGIC_NEGATE_HELPURL})}}; Blockly.Blocks.logic_boolean={init:function(){this.jsonInit({message0:"%1",args0:[{type:"field_dropdown",name:"BOOL",options:[[Blockly.Msg.LOGIC_BOOLEAN_TRUE,"TRUE"],[Blockly.Msg.LOGIC_BOOLEAN_FALSE,"FALSE"]]}],output:"Boolean",colour:Blockly.Blocks.logic.HUE,tooltip:Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP,helpUrl:Blockly.Msg.LOGIC_BOOLEAN_HELPURL})}}; Blockly.Blocks.logic_null={init:function(){this.jsonInit({message0:Blockly.Msg.LOGIC_NULL,output:null,colour:Blockly.Blocks.logic.HUE,tooltip:Blockly.Msg.LOGIC_NULL_TOOLTIP,helpUrl:Blockly.Msg.LOGIC_NULL_HELPURL})}}; Blockly.Blocks.logic_ternary={init:function(){this.setHelpUrl(Blockly.Msg.LOGIC_TERNARY_HELPURL);this.setColour(Blockly.Blocks.logic.HUE);this.appendValueInput("IF").setCheck("Boolean").appendField(Blockly.Msg.LOGIC_TERNARY_CONDITION);this.appendValueInput("THEN").appendField(Blockly.Msg.LOGIC_TERNARY_IF_TRUE);this.appendValueInput("ELSE").appendField(Blockly.Msg.LOGIC_TERNARY_IF_FALSE);this.setOutput(!0);this.setTooltip(Blockly.Msg.LOGIC_TERNARY_TOOLTIP);this.prevParentConnection_=null},onchange:function(a){var b= -this.getInputTargetBlock("THEN"),c=this.getInputTargetBlock("ELSE"),d=this.outputConnection.targetConnection;if((b||c)&&d)for(var e=0;2>e;e++){var f=1==e?b:c;f&&!f.outputConnection.checkType_(d)&&(Blockly.Events.setGroup(a.group),d===this.prevParentConnection_?(this.unplug(),d.getSourceBlock().bumpNeighbours_()):(f.unplug(),f.bumpNeighbours_()),Blockly.Events.setGroup(!1))}this.prevParentConnection_=d}};Blockly.Blocks.loops={};Blockly.Blocks.loops.HUE=120;Blockly.Blocks.controls_repeat_ext={init:function(){this.jsonInit({message0:Blockly.Msg.CONTROLS_REPEAT_TITLE,args0:[{type:"input_value",name:"TIMES",check:"Number"}],previousStatement:null,nextStatement:null,colour:Blockly.Blocks.loops.HUE,tooltip:Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,helpUrl:Blockly.Msg.CONTROLS_REPEAT_HELPURL});this.appendStatementInput("DO").appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO)}}; +this.getInputTargetBlock("THEN"),c=this.getInputTargetBlock("ELSE"),e=this.outputConnection.targetConnection;if((b||c)&&e)for(var d=0;2>d;d++){var f=1==d?b:c;f&&!f.outputConnection.checkType_(e)&&(Blockly.Events.setGroup(a.group),e===this.prevParentConnection_?(this.unplug(),e.getSourceBlock().bumpNeighbours_()):(f.unplug(),f.bumpNeighbours_()),Blockly.Events.setGroup(!1))}this.prevParentConnection_=e}};Blockly.Blocks.loops={};Blockly.Blocks.loops.HUE=120;Blockly.Blocks.controls_repeat_ext={init:function(){this.jsonInit({message0:Blockly.Msg.CONTROLS_REPEAT_TITLE,args0:[{type:"input_value",name:"TIMES",check:"Number"}],previousStatement:null,nextStatement:null,colour:Blockly.Blocks.loops.HUE,tooltip:Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,helpUrl:Blockly.Msg.CONTROLS_REPEAT_HELPURL});this.appendStatementInput("DO").appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO)}}; Blockly.Blocks.controls_repeat={init:function(){this.jsonInit({message0:Blockly.Msg.CONTROLS_REPEAT_TITLE,args0:[{type:"field_number",name:"TIMES",value:10,min:0,precision:1}],previousStatement:null,nextStatement:null,colour:Blockly.Blocks.loops.HUE,tooltip:Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,helpUrl:Blockly.Msg.CONTROLS_REPEAT_HELPURL});this.appendStatementInput("DO").appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO)}}; Blockly.Blocks.controls_whileUntil={init:function(){var a=[[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE,"WHILE"],[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL,"UNTIL"]];this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL);this.setColour(Blockly.Blocks.loops.HUE);this.appendValueInput("BOOL").setCheck("Boolean").appendField(new Blockly.FieldDropdown(a),"MODE");this.appendStatementInput("DO").appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO);this.setPreviousStatement(!0);this.setNextStatement(!0); var b=this;this.setTooltip(function(){var a=b.getFieldValue("MODE");return{WHILE:Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE,UNTIL:Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}[a]})}}; @@ -83,36 +88,37 @@ Blockly.Blocks.math_constrain={init:function(){this.jsonInit({message0:Blockly.M Blockly.Blocks.math_random_int={init:function(){this.jsonInit({message0:Blockly.Msg.MATH_RANDOM_INT_TITLE,args0:[{type:"input_value",name:"FROM",check:"Number"},{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",colour:Blockly.Blocks.math.HUE,tooltip:Blockly.Msg.MATH_RANDOM_INT_TOOLTIP,helpUrl:Blockly.Msg.MATH_RANDOM_INT_HELPURL})}}; Blockly.Blocks.math_random_float={init:function(){this.jsonInit({message0:Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM,output:"Number",colour:Blockly.Blocks.math.HUE,tooltip:Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP,helpUrl:Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL})}};Blockly.Blocks.procedures={};Blockly.Blocks.procedures.HUE=290; Blockly.Blocks.procedures_defnoreturn={init:function(){var a=new Blockly.FieldTextInput(Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE,Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);this.setColour(Blockly.Blocks.procedures.HUE); -this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.setStatements_(!0);this.statementConnection_=null},validate:function(){var a=Blockly.Procedures.findLegalName(this.getFieldValue("NAME"),this);this.setFieldValue(a,"NAME")},setStatements_:function(a){this.hasStatements_!==a&&(a?(this.appendStatementInput("STACK").appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO),this.getInput("RETURN")&&this.moveInputBefore("STACK", -"RETURN")):this.removeInput("STACK",!0),this.hasStatements_=a)},updateParams_:function(){for(var a=!1,b={},c=0;c Blockly.DRAG_RADIUS) { - // Create the block. - Blockly.Flyout.startFlyout_.createBlockFunc_(Blockly.Flyout.startBlock_)( + + var createBlock = this.determineDragIntention_(dx, dy); + if (createBlock) { + this.createBlockFunc_(Blockly.Flyout.startBlock_)( Blockly.Flyout.startDownEvent_); + } else if (this.dragMode_ == Blockly.DRAG_FREE) { + // Do a scroll. + this.onMouseMove_(e); } + e.stopPropagation(); +}; + +/** + * Determine the intention of a drag. + * Updates dragMode_ based on a drag delta and the current mode, + * and returns true if we should create a new block. + * @param {number} dx X delta of the drag. + * @param {number} dy Y delta of the drag. + * @return {boolean} True if a new block should be created. + * @private + */ +Blockly.Flyout.prototype.determineDragIntention_ = function(dx, dy) { + if (this.dragMode_ == Blockly.DRAG_FREE) { + // Once in free mode, always stay in free mode and never create a block. + return false; + } + var dragDistance = Math.sqrt(dx * dx + dy * dy); + if (dragDistance < this.DRAG_RADIUS) { + // Still within the sticky drag radius. + this.dragMode_ = Blockly.DRAG_STICKY; + return false; + } else { + if (this.isDragTowardWorkspace_(dx, dy) || !this.scrollbar_.isVisible()) { + // Immediately create a block. + return true; + } else { + // Immediately move to free mode - the drag is away from the workspace. + this.dragMode_ = Blockly.DRAG_FREE; + return false; + } + } +}; + +/** + * Determine if a drag delta is toward the workspace, based on the position + * and orientation of the flyout. This is used in determineDragIntention_ to + * determine if a new block should be created or if the flyout should scroll. + * @param {number} dx X delta of the drag. + * @param {number} dy Y delta of the drag. + * @return {boolean} true if the drag is toward the workspace. + * @private + */ +Blockly.Flyout.prototype.isDragTowardWorkspace_ = function(dx, dy) { + // Direction goes from -180 to 180, with 0 toward the right and 90 on top. + var dragDirection = Math.atan2(dy, dx) / Math.PI * 180; + + var draggingTowardWorkspace = false; + var range = this.dragAngleRange_; + if (this.horizontalLayout_) { + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { + // Horizontal at top. + if (dragDirection < 90 + range && dragDirection > 90 - range) { + draggingTowardWorkspace = true; + } + } else { + // Horizontal at bottom. + if (dragDirection > -90 - range && dragDirection < -90 + range) { + draggingTowardWorkspace = true; + } + } + } else { + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { + // Vertical at left. + if (dragDirection < range && dragDirection > -range) { + draggingTowardWorkspace = true; + } + } else { + // Vertical at right. + if (dragDirection < -180 + range || dragDirection > 180 - range) { + draggingTowardWorkspace = true; + } + } + } + return draggingTowardWorkspace; }; /** @@ -994,6 +1106,9 @@ Blockly.Flyout.prototype.getClientRect = function() { * @private */ Blockly.Flyout.terminateDrag_ = function() { + if (Blockly.Flyout.startFlyout_) { + Blockly.Flyout.startFlyout_.dragMode_ = Blockly.DRAG_NONE; + } if (Blockly.Flyout.onMouseUpWrapper_) { Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_); Blockly.Flyout.onMouseUpWrapper_ = null; diff --git a/core/generator.js b/core/generator.js index 8ee110f86..fecc35531 100644 --- a/core/generator.js +++ b/core/generator.js @@ -132,7 +132,7 @@ Blockly.Generator.prototype.workspaceToCode = function(workspace) { * @return {string} The prefixed lines of code. */ Blockly.Generator.prototype.prefixLines = function(text, prefix) { - return prefix + text.replace(/\n(.)/g, '\n' + prefix + '$1'); + return prefix + text.replace(/(?!\n$)\n/g, '\n' + prefix); }; /** @@ -143,8 +143,8 @@ Blockly.Generator.prototype.prefixLines = function(text, prefix) { Blockly.Generator.prototype.allNestedComments = function(block) { var comments = []; var blocks = block.getDescendants(); - for (var x = 0; x < blocks.length; x++) { - var comment = blocks[x].getCommentText(); + for (var i = 0; i < blocks.length; i++) { + var comment = blocks[i].getCommentText(); if (comment) { comments.push(comment); } diff --git a/core/icon.js b/core/icon.js index 9d2cbeaad..395c98544 100644 --- a/core/icon.js +++ b/core/icon.js @@ -124,7 +124,7 @@ Blockly.Icon.prototype.isVisible = function() { * @private */ Blockly.Icon.prototype.iconClick_ = function(e) { - if (Blockly.dragMode_ == Blockly.DRAG_FREE) { + if (this.block_.workspace.isDragging()) { // Drag operation is concluding. Don't open the editor. return; } diff --git a/core/input.js b/core/input.js index 346b053cb..4b4eb1df9 100644 --- a/core/input.js +++ b/core/input.js @@ -222,8 +222,8 @@ Blockly.Input.prototype.init = function() { if (!this.sourceBlock_.workspace.rendered) { return; // Headless blocks don't need fields initialized. } - for (var x = 0; x < this.fieldRow.length; x++) { - this.fieldRow[x].init(); + for (var i = 0; i < this.fieldRow.length; i++) { + this.fieldRow[i].init(); } }; diff --git a/core/procedures.js b/core/procedures.js index f98fbd92a..beb4a17b8 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -89,7 +89,7 @@ Blockly.Procedures.findLegalName = function(name, block) { // Flyouts can have multiple procedures called 'do something'. return name; } - while (!Blockly.Procedures.isLegalName(name, block.workspace, block)) { + while (!Blockly.Procedures.isLegalName_(name, block.workspace, block)) { // Collision with another procedure. var r = name.match(/^(.*?)(\d+)$/); if (!r) { @@ -109,8 +109,9 @@ Blockly.Procedures.findLegalName = function(name, block) { * @param {Blockly.Block=} opt_exclude Optional block to exclude from * comparisons (one doesn't want to collide with oneself). * @return {boolean} True if the name is legal. + * @private */ -Blockly.Procedures.isLegalName = function(name, workspace, opt_exclude) { +Blockly.Procedures.isLegalName_ = function(name, workspace, opt_exclude) { var blocks = workspace.getAllBlocks(); // Iterate through every block and check the name. for (var i = 0; i < blocks.length; i++) { @@ -129,24 +130,27 @@ Blockly.Procedures.isLegalName = function(name, workspace, opt_exclude) { /** * Rename a procedure. Called by the editable field. - * @param {string} text The proposed new name. + * @param {string} name The proposed new name. * @return {string} The accepted name. * @this {!Blockly.Field} */ -Blockly.Procedures.rename = function(text) { +Blockly.Procedures.rename = function(name) { // Strip leading and trailing whitespace. Beyond this, all names are legal. - text = text.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); + name = name.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); // Ensure two identically-named procedures don't exist. - text = Blockly.Procedures.findLegalName(text, this.sourceBlock_); - // Rename any callers. - var blocks = this.sourceBlock_.workspace.getAllBlocks(); - for (var i = 0; i < blocks.length; i++) { - if (blocks[i].renameProcedure) { - blocks[i].renameProcedure(this.text_, text); + var legalName = Blockly.Procedures.findLegalName(name, this.sourceBlock_); + var oldName = this.text_; + if (oldName != name && oldName != legalName) { + // Rename any callers. + var blocks = this.sourceBlock_.workspace.getAllBlocks(); + for (var i = 0; i < blocks.length; i++) { + if (blocks[i].renameProcedure) { + blocks[i].renameProcedure(oldName, legalName); + } } } - return text; + return legalName; }; /** @@ -197,9 +201,9 @@ Blockly.Procedures.flyoutCategory = function(workspace) { var mutation = goog.dom.createDom('mutation'); mutation.setAttribute('name', name); block.appendChild(mutation); - for (var t = 0; t < args.length; t++) { + for (var j = 0; j < args.length; j++) { var arg = goog.dom.createDom('arg'); - arg.setAttribute('name', args[t]); + arg.setAttribute('name', args[j]); mutation.appendChild(arg); } xmlList.push(block); @@ -234,19 +238,6 @@ Blockly.Procedures.getCallers = function(name, workspace) { return callers; }; -/** - * When a procedure definition is disposed of, find and dispose of all its - * callers. - * @param {string} name Name of deleted procedure definition. - * @param {!Blockly.Workspace} workspace The workspace to delete callers from. - */ -Blockly.Procedures.disposeCallers = function(name, workspace) { - var callers = Blockly.Procedures.getCallers(name, workspace); - for (var i = 0; i < callers.length; i++) { - callers[i].dispose(true, false); - } -}; - /** * When a procedure definition changes its parameters, find and edit all its * callers. @@ -282,7 +273,8 @@ Blockly.Procedures.mutateCallers = function(defBlock) { * @return {Blockly.Block} The procedure definition block, or null not found. */ Blockly.Procedures.getDefinition = function(name, workspace) { - var blocks = workspace.getAllBlocks(); + // Assume that a procedure definition is a top block. + var blocks = workspace.getTopBlocks(false); for (var i = 0; i < blocks.length; i++) { if (blocks[i].getProcedureDef) { var tuple = blocks[i].getProcedureDef(); diff --git a/core/rendered_connection.js b/core/rendered_connection.js index ef7130dc6..552248526 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -234,8 +234,8 @@ Blockly.RenderedConnection.prototype.unhideAll = function() { // Show all connections of this block. connections = block.getConnections_(true); } - for (var c = 0; c < connections.length; c++) { - renderList.push.apply(renderList, connections[c].unhideAll()); + for (var i = 0; i < connections.length; i++) { + renderList.push.apply(renderList, connections[i].unhideAll()); } if (!renderList.length) { // Leaf block. @@ -275,17 +275,17 @@ Blockly.RenderedConnection.prototype.hideAll = function() { this.setHidden(true); if (this.targetConnection) { var blocks = this.targetBlock().getDescendants(); - for (var b = 0; b < blocks.length; b++) { - var block = blocks[b]; + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i]; // Hide all connections of all children. var connections = block.getConnections_(true); - for (var c = 0; c < connections.length; c++) { - connections[c].setHidden(true); + for (var j = 0; j < connections.length; j++) { + connections[j].setHidden(true); } // Close all bubbles of all children. var icons = block.getIcons(); - for (var i = 0; i < icons.length; i++) { - icons[i].setVisible(false); + for (var j = 0; j < icons.length; j++) { + icons[j].setVisible(false); } } } diff --git a/core/variables.js b/core/variables.js index 9399177fe..450d33fcf 100644 --- a/core/variables.js +++ b/core/variables.js @@ -54,10 +54,10 @@ Blockly.Variables.allVariables = function(root) { } var variableHash = Object.create(null); // Iterate through every block and add each variable to the hash. - for (var x = 0; x < blocks.length; x++) { - var blockVariables = blocks[x].getVars(); - for (var y = 0; y < blockVariables.length; y++) { - var varName = blockVariables[y]; + for (var i = 0; i < blocks.length; i++) { + var blockVariables = blocks[i].getVars(); + for (var j = 0; j < blockVariables.length; j++) { + var varName = blockVariables[j]; // Variable name may be null if the block is only half-built. if (varName) { variableHash[varName.toLowerCase()] = varName; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 251d5ef10..4c2d85661 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -713,6 +713,14 @@ Blockly.WorkspaceSvg.prototype.moveDrag = function(e) { return goog.math.Coordinate.sum(this.dragDeltaXY_, point); }; +/** + * Is the user currently dragging a block or scrolling the workspace? + * @return {boolean} True if currently dragging or scrolling. + */ +Blockly.WorkspaceSvg.prototype.isDragging = function() { + return Blockly.dragMode_ == Blockly.DRAG_FREE || this.isScrolling; +}; + /** * Handle a mouse-wheel on SVG drawing surface. * @param {!Event} e Mouse wheel event. @@ -735,7 +743,7 @@ Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) { * containing the blocks on the workspace. */ Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox = function() { - var topBlocks = this.getTopBlocks(); + var topBlocks = this.getTopBlocks(false); // There are no blocks, return empty rectangle. if (!topBlocks.length) { return {x: 0, y: 0, width: 0, height: 0}; diff --git a/core/xml.js b/core/xml.js index 7f096956a..95e6471c2 100644 --- a/core/xml.js +++ b/core/xml.js @@ -535,10 +535,6 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { } block.setShadow(true); } - // Give the block a chance to clean up any initial inputs. - if (block.validate) { - block.validate(); - } return block; }; diff --git a/dart_compressed.js b/dart_compressed.js index 963bbc380..20a2cafa1 100644 --- a/dart_compressed.js +++ b/dart_compressed.js @@ -4,33 +4,38 @@ // Copyright 2014 Google Inc. Apache License 2.0 Blockly.Dart=new Blockly.Generator("Dart");Blockly.Dart.addReservedWords("assert,break,case,catch,class,const,continue,default,do,else,enum,extends,false,final,finally,for,if,in,is,new,null,rethrow,return,super,switch,this,throw,true,try,var,void,while,with,print,identityHashCode,identical,BidirectionalIterator,Comparable,double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,ArgumentError,AssertionError,CastError,ConcurrentModificationError,CyclicInitializationError,Error,Exception,FallThroughError,FormatException,IntegerDivisionByZeroException,NoSuchMethodError,NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,StateError,TypeError,UnimplementedError,UnsupportedError"); -Blockly.Dart.ORDER_ATOMIC=0;Blockly.Dart.ORDER_UNARY_POSTFIX=1;Blockly.Dart.ORDER_UNARY_PREFIX=2;Blockly.Dart.ORDER_MULTIPLICATIVE=3;Blockly.Dart.ORDER_ADDITIVE=4;Blockly.Dart.ORDER_SHIFT=5;Blockly.Dart.ORDER_BITWISE_AND=6;Blockly.Dart.ORDER_BITWISE_XOR=7;Blockly.Dart.ORDER_BITWISE_OR=8;Blockly.Dart.ORDER_RELATIONAL=9;Blockly.Dart.ORDER_EQUALITY=10;Blockly.Dart.ORDER_LOGICAL_AND=11;Blockly.Dart.ORDER_LOGICAL_OR=12;Blockly.Dart.ORDER_CONDITIONAL=13;Blockly.Dart.ORDER_CASCADE=14; -Blockly.Dart.ORDER_ASSIGNMENT=15;Blockly.Dart.ORDER_NONE=99; +Blockly.Dart.ORDER_ATOMIC=0;Blockly.Dart.ORDER_UNARY_POSTFIX=1;Blockly.Dart.ORDER_UNARY_PREFIX=2;Blockly.Dart.ORDER_MULTIPLICATIVE=3;Blockly.Dart.ORDER_ADDITIVE=4;Blockly.Dart.ORDER_SHIFT=5;Blockly.Dart.ORDER_BITWISE_AND=6;Blockly.Dart.ORDER_BITWISE_XOR=7;Blockly.Dart.ORDER_BITWISE_OR=8;Blockly.Dart.ORDER_RELATIONAL=9;Blockly.Dart.ORDER_EQUALITY=10;Blockly.Dart.ORDER_LOGICAL_AND=11;Blockly.Dart.ORDER_LOGICAL_OR=12;Blockly.Dart.ORDER_IF_NULL=13;Blockly.Dart.ORDER_CONDITIONAL=14; +Blockly.Dart.ORDER_CASCADE=15;Blockly.Dart.ORDER_ASSIGNMENT=16;Blockly.Dart.ORDER_NONE=99;Blockly.Dart.ONE_BASED_INDEXING=!0; Blockly.Dart.init=function(a){Blockly.Dart.definitions_=Object.create(null);Blockly.Dart.functionNames_=Object.create(null);Blockly.Dart.variableDB_?Blockly.Dart.variableDB_.reset():Blockly.Dart.variableDB_=new Blockly.Names(Blockly.Dart.RESERVED_WORDS_);var b=[];a=Blockly.Variables.allVariables(a);if(a.length){for(var c=0;cc&&(a=a+" - "+-c,g=Blockly.Dart.ORDER_ADDITIVE);d&&(a= +c?"-("+a+")":"-"+a,g=Blockly.Dart.ORDER_UNARY_PREFIX);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.Dart.colour={};Blockly.Dart.addReservedWords("Math");Blockly.Dart.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.Dart.ORDER_ATOMIC]}; Blockly.Dart.colour_random=function(a){Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";return[Blockly.Dart.provideFunction_("colour_random",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"() {"," String hex = '0123456789abcdef';"," var rnd = new Math.Random();"," return '#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}'"," '${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}'"," '${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}';","}"])+"()",Blockly.Dart.ORDER_UNARY_POSTFIX]}; Blockly.Dart.colour_rgb=function(a){var b=Blockly.Dart.valueToCode(a,"RED",Blockly.Dart.ORDER_NONE)||0,c=Blockly.Dart.valueToCode(a,"GREEN",Blockly.Dart.ORDER_NONE)||0;a=Blockly.Dart.valueToCode(a,"BLUE",Blockly.Dart.ORDER_NONE)||0;Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";return[Blockly.Dart.provideFunction_("colour_rgb",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(num r, num g, num b) {"," num rn = (Math.max(Math.min(r, 1), 0) * 255).round();"," String rs = rn.toInt().toRadixString(16);", " rs = '0$rs';"," rs = rs.substring(rs.length - 2);"," num gn = (Math.max(Math.min(g, 1), 0) * 255).round();"," String gs = gn.toInt().toRadixString(16);"," gs = '0$gs';"," gs = gs.substring(gs.length - 2);"," num bn = (Math.max(Math.min(b, 1), 0) * 255).round();"," String bs = bn.toInt().toRadixString(16);"," bs = '0$bs';"," bs = bs.substring(bs.length - 2);"," return '#$rs$gs$bs';","}"])+"("+b+", "+c+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}; Blockly.Dart.colour_blend=function(a){var b=Blockly.Dart.valueToCode(a,"COLOUR1",Blockly.Dart.ORDER_NONE)||"'#000000'",c=Blockly.Dart.valueToCode(a,"COLOUR2",Blockly.Dart.ORDER_NONE)||"'#000000'";a=Blockly.Dart.valueToCode(a,"RATIO",Blockly.Dart.ORDER_NONE)||.5;Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";return[Blockly.Dart.provideFunction_("colour_blend",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(String c1, String c2, num ratio) {"," ratio = Math.max(Math.min(ratio, 1), 0);", " int r1 = int.parse('0x${c1.substring(1, 3)}');"," int g1 = int.parse('0x${c1.substring(3, 5)}');"," int b1 = int.parse('0x${c1.substring(5, 7)}');"," int r2 = int.parse('0x${c2.substring(1, 3)}');"," int g2 = int.parse('0x${c2.substring(3, 5)}');"," int b2 = int.parse('0x${c2.substring(5, 7)}');"," num rn = (r1 * (1 - ratio) + r2 * ratio).round();"," String rs = rn.toInt().toRadixString(16);"," num gn = (g1 * (1 - ratio) + g2 * ratio).round();"," String gs = gn.toInt().toRadixString(16);", -" num bn = (b1 * (1 - ratio) + b2 * ratio).round();"," String bs = bn.toInt().toRadixString(16);"," rs = '0$rs';"," rs = rs.substring(rs.length - 2);"," gs = '0$gs';"," gs = gs.substring(gs.length - 2);"," bs = '0$bs';"," bs = bs.substring(bs.length - 2);"," return '#$rs$gs$bs';","}"])+"("+b+", "+c+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.lists={};Blockly.Dart.addReservedWords("Math");Blockly.Dart.lists_create_empty=function(a){return["[]",Blockly.Dart.ORDER_ATOMIC]};Blockly.Dart.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c direction * a.compareTo(b),',' "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()),',' "IGNORE_CASE": '," (a, b) => direction * ", +Blockly.Dart.lists_isEmpty=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+".isEmpty",Blockly.Dart.ORDER_UNARY_POSTFIX]}; +Blockly.Dart.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''";a=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+"."+b+"("+c+")";return Blockly.Dart.ONE_BASED_INDEXING?[a+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[a,Blockly.Dart.ORDER_UNARY_POSTFIX]}; +Blockly.Dart.lists_getIndex=function(a){function b(){var a=Blockly.Dart.variableDB_.getDistinctName("tmp_list",Blockly.Variables.NAME_TYPE),b="List "+a+" = "+e+";\n";e=a;return b}var c=a.getFieldValue("MODE")||"GET",d=a.getFieldValue("WHERE")||"FROM_START",e=Blockly.Dart.valueToCode(a,"VALUE","RANDOM"==d||"FROM_END"==d?Blockly.Dart.ORDER_NONE:Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]";if(("RANDOM"!=d||"REMOVE"!=c)&&"FROM_END"!=d||e.match(/^\w+$/))switch(d){case "FIRST":if("GET"==c)return[e+".first", +Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+".removeAt(0)",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeAt(0);\n";break;case "LAST":if("GET"==c)return[e+".last",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+".removeLast()",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeLast();\n";break;case "FROM_START":d=Blockly.Dart.getAdjusted(a,"AT");if("GET"==c)return[e+"["+d+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+ +".removeAt("+d+")",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeAt("+d+");\n";break;case "FROM_END":d=Blockly.Dart.getAdjusted(a,"AT",1,!1,Blockly.Dart.ORDER_ADDITIVE);if("GET"==c)return[e+"["+e+".length - "+d+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c||"REMOVE"==c){a=e+".removeAt("+e+".length - "+d+")";if("GET_REMOVE"==c)return[a,Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return a+";\n"}break;case "RANDOM":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;"; +if("REMOVE"==c)return c=Blockly.Dart.variableDB_.getDistinctName("tmp_x",Blockly.Variables.NAME_TYPE),"int "+c+" = new Math.Random().nextInt("+e+".length);\n"+(e+".removeAt("+c+");\n");if("GET"==c)return c=Blockly.Dart.provideFunction_("lists_get_random_item",["dynamic "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List my_list) {"," int x = new Math.Random().nextInt(my_list.length);"," return my_list[x];","}"]),[c+"("+e+")",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return c=Blockly.Dart.provideFunction_("lists_remove_random_item", +["dynamic "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List my_list) {"," int x = new Math.Random().nextInt(my_list.length);"," return my_list.removeAt(x);","}"]),[c+"("+e+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}else{if("RANDOM"==d)return Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;",a=b(),c=Blockly.Dart.variableDB_.getDistinctName("tmp_x",Blockly.Variables.NAME_TYPE),a+("int "+c+" = new Math.Random().nextInt("+e+".length);\n")+(e+".removeAt("+c+");\n");if("REMOVE"==c)return d= +Blockly.Dart.getAdjusted(a,"AT",1,!1,Blockly.Dart.ORDER_ADDITIVE),a=b(),a+=e+".removeAt("+e+".length - "+d+");\n";if("GET"==c)return d=Blockly.Dart.getAdjusted(a,"AT",1),c=Blockly.Dart.provideFunction_("lists_get_from_end",["dynamic "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List my_list, num x) {"," x = my_list.length - x;"," return my_list[x];","}"]),[c+"("+e+", "+d+")",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return d=Blockly.Dart.getAdjusted(a,"AT",1),c=Blockly.Dart.provideFunction_("lists_remove_from_end", +["dynamic "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List my_list, num x) {"," x = my_list.length - x;"," return my_list.removeAt(x);","}"]),[c+"("+e+", "+d+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}throw"Unhandled combination (lists_getIndex).";}; +Blockly.Dart.lists_setIndex=function(a){function b(){if(e.match(/^\w+$/))return"";var a=Blockly.Dart.variableDB_.getDistinctName("tmp_list",Blockly.Variables.NAME_TYPE),b="List "+a+" = "+e+";\n";e=a;return b}var c=a.getFieldValue("MODE")||"GET",d=a.getFieldValue("WHERE")||"FROM_START",e=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]",f=Blockly.Dart.valueToCode(a,"TO",Blockly.Dart.ORDER_ASSIGNMENT)||"null";switch(d){case "FIRST":if("SET"==c)return e+"[0] = "+f+";\n";if("INSERT"== +c)return e+".insert(0, "+f+");\n";break;case "LAST":if("SET"==c)return a=b(),a+(e+"["+e+".length - 1] = "+f+";\n");if("INSERT"==c)return e+".add("+f+");\n";break;case "FROM_START":d=Blockly.Dart.getAdjusted(a,"AT");if("SET"==c)return e+"["+d+"] = "+f+";\n";if("INSERT"==c)return e+".insert("+d+", "+f+");\n";break;case "FROM_END":d=Blockly.Dart.getAdjusted(a,"AT",1,!1,Blockly.Dart.ORDER_ADDITIVE);a=b();if("SET"==c)return a+(e+"["+e+".length - "+d+"] = "+f+";\n");if("INSERT"==c)return a+(e+".insert("+ +e+".length - "+d+", "+f+");\n");break;case "RANDOM":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";a=b();d=Blockly.Dart.variableDB_.getDistinctName("tmp_x",Blockly.Variables.NAME_TYPE);a+="int "+d+" = new Math.Random().nextInt("+e+".length);\n";if("SET"==c)return a+(e+"["+d+"] = "+f+";\n");if("INSERT"==c)return a+(e+".insert("+d+", "+f+");\n")}throw"Unhandled combination (lists_setIndex).";}; +Blockly.Dart.lists_getSublist=function(a){var b=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if(b.match(/^\w+$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":var e=Blockly.Dart.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.Dart.getAdjusted(a,"AT1",1,!1,Blockly.Dart.ORDER_ADDITIVE);e=b+".length - "+e;break;case "FIRST":e="0";break;default:throw"Unhandled option (lists_getSublist).";}switch(d){case "FROM_START":var f= +Blockly.Dart.getAdjusted(a,"AT2",1);break;case "FROM_END":f=Blockly.Dart.getAdjusted(a,"AT2",0,!1,Blockly.Dart.ORDER_ADDITIVE);f=b+".length - "+f;break;case "LAST":break;default:throw"Unhandled option (lists_getSublist).";}a="LAST"==d?b+".sublist("+e+")":b+".sublist("+e+", "+f+")"}else e=Blockly.Dart.getAdjusted(a,"AT1"),f=Blockly.Dart.getAdjusted(a,"AT2"),a=Blockly.Dart.provideFunction_("lists_get_sublist",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(list, where1, at1, where2, at2) {"," int getAt(where, at) {", +" if (where == 'FROM_END') {"," at = list.length - 1 - at;"," } else if (where == 'FIRST') {"," at = 0;"," } else if (where == 'LAST') {"," at = list.length - 1;"," } else if (where != 'FROM_START') {"," throw 'Unhandled option (lists_getSublist).';"," }"," return at;"," }"," at1 = getAt(where1, at1);"," at2 = getAt(where2, at2) + 1;"," return list.sublist(at1, at2);","}"])+"("+b+", '"+c+"', "+e+", '"+d+"', "+f+")";return[a,Blockly.Dart.ORDER_UNARY_POSTFIX]}; +Blockly.Dart.lists_sort=function(a){var b=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_NONE)||"[]",c="1"===a.getFieldValue("DIRECTION")?1:-1;a=a.getFieldValue("TYPE");return[Blockly.Dart.provideFunction_("lists_sort",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(list, type, direction) {"," var compareFuncs = {",' "NUMERIC": (a, b) => direction * a.compareTo(b),',' "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()),',' "IGNORE_CASE": '," (a, b) => direction * ", " a.toString().toLowerCase().compareTo(b.toString().toLowerCase())"," };"," list = new List.from(list);"," var compare = compareFuncs[type];"," list.sort(compare);"," return list;","}"])+"("+b+', "'+a+'", '+c+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}; Blockly.Dart.lists_split=function(a){var b=Blockly.Dart.valueToCode(a,"INPUT",Blockly.Dart.ORDER_UNARY_POSTFIX),c=Blockly.Dart.valueToCode(a,"DELIM",Blockly.Dart.ORDER_NONE)||"''";a=a.getFieldValue("MODE");if("SPLIT"==a)b||(b="''"),a="split";else if("JOIN"==a)b||(b="[]"),a="join";else throw"Unknown mode: "+a;return[b+"."+a+"("+c+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.logic={};Blockly.Dart.controls_if=function(a){for(var b=0,c=Blockly.Dart.valueToCode(a,"IF"+b,Blockly.Dart.ORDER_NONE)||"false",d=Blockly.Dart.statementToCode(a,"DO"+b),e="if ("+c+") {\n"+d+"}",b=1;b<=a.elseifCount_;b++)c=Blockly.Dart.valueToCode(a,"IF"+b,Blockly.Dart.ORDER_NONE)||"false",d=Blockly.Dart.statementToCode(a,"DO"+b),e+=" else if ("+c+") {\n"+d+"}";a.elseCount_&&(d=Blockly.Dart.statementToCode(a,"ELSE"),e+=" else {\n"+d+"}");return e+"\n"}; Blockly.Dart.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.Dart.ORDER_EQUALITY:Blockly.Dart.ORDER_RELATIONAL,d=Blockly.Dart.valueToCode(a,"A",c)||"0";a=Blockly.Dart.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]}; @@ -56,11 +61,11 @@ Blockly.Dart.math_number_property=function(a){var b=Blockly.Dart.valueToCode(a," Blockly.Dart.math_change=function(a){var b=Blockly.Dart.valueToCode(a,"DELTA",Blockly.Dart.ORDER_ADDITIVE)||"0";a=Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);return a+" = ("+a+" is num ? "+a+" : 0) + "+b+";\n"};Blockly.Dart.math_round=Blockly.Dart.math_single;Blockly.Dart.math_trig=Blockly.Dart.math_single; Blockly.Dart.math_on_list=function(a){var b=a.getFieldValue("OP");a=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_NONE)||"[]";switch(b){case "SUM":b=Blockly.Dart.provideFunction_("math_sum",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," num sumVal = 0;"," myList.forEach((num entry) {sumVal += entry;});"," return sumVal;","}"]);b=b+"("+a+")";break;case "MIN":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";b=Blockly.Dart.provideFunction_("math_min", ["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," if (myList.isEmpty) return null;"," num minVal = myList[0];"," myList.forEach((num entry) {minVal = Math.min(minVal, entry);});"," return minVal;","}"]);b=b+"("+a+")";break;case "MAX":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";b=Blockly.Dart.provideFunction_("math_max",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," if (myList.isEmpty) return null;"," num maxVal = myList[0];", -" myList.forEach((num entry) {maxVal = Math.max(maxVal, entry);});"," return maxVal;","}"]);b=b+"("+a+")";break;case "AVERAGE":b=Blockly.Dart.provideFunction_("math_mean",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," // First filter list for numbers only."," List localList = new List.from(myList);"," localList.removeMatching((a) => a is! num);"," if (localList.isEmpty) return null;"," num sumVal = 0;"," localList.forEach((num entry) {sumVal += entry;});"," return sumVal / localList.length;", -"}"]);b=b+"("+a+")";break;case "MEDIAN":b=Blockly.Dart.provideFunction_("math_median",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," // First filter list for numbers only, then sort, then return middle value"," // or the average of two middle values if list has an even number of elements."," List localList = new List.from(myList);"," localList.removeMatching((a) => a is! num);"," if (localList.isEmpty) return null;"," localList.sort((a, b) => (a - b));"," int index = localList.length ~/ 2;", +" myList.forEach((num entry) {maxVal = Math.max(maxVal, entry);});"," return maxVal;","}"]);b=b+"("+a+")";break;case "AVERAGE":b=Blockly.Dart.provideFunction_("math_mean",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," // First filter list for numbers only."," List localList = new List.from(myList);"," localList.removeWhere((a) => a is! num);"," if (localList.isEmpty) return null;"," num sumVal = 0;"," localList.forEach((num entry) {sumVal += entry;});"," return sumVal / localList.length;", +"}"]);b=b+"("+a+")";break;case "MEDIAN":b=Blockly.Dart.provideFunction_("math_median",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," // First filter list for numbers only, then sort, then return middle value"," // or the average of two middle values if list has an even number of elements."," List localList = new List.from(myList);"," localList.removeWhere((a) => a is! num);"," if (localList.isEmpty) return null;"," localList.sort((a, b) => (a - b));"," int index = localList.length ~/ 2;", " if (localList.length % 2 == 1) {"," return localList[index];"," } else {"," return (localList[index - 1] + localList[index]) / 2;"," }","}"]);b=b+"("+a+")";break;case "MODE":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";b=Blockly.Dart.provideFunction_("math_modes",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List values) {"," List modes = [];"," List counts = [];"," int maxCount = 0;"," for (int i = 0; i < values.length; i++) {"," var value = values[i];", " bool found = false;"," int thisCount;"," for (int j = 0; j < counts.length; j++) {"," if (counts[j][0] == value) {"," thisCount = ++counts[j][1];"," found = true;"," break;"," }"," }"," if (!found) {"," counts.add([value, 1]);"," thisCount = 1;"," }"," maxCount = Math.max(thisCount, maxCount);"," }"," for (int j = 0; j < counts.length; j++) {"," if (counts[j][1] == maxCount) {"," modes.add(counts[j][0]);"," }"," }"," return modes;", -"}"]);b=b+"("+a+")";break;case "STD_DEV":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";b=Blockly.Dart.provideFunction_("math_standard_deviation",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," // First filter list for numbers only."," List numbers = new List.from(myList);"," numbers.removeMatching((a) => a is! num);"," if (numbers.isEmpty) return null;"," num n = numbers.length;"," num sum = 0;"," numbers.forEach((x) => sum += x);"," num mean = sum / n;", +"}"]);b=b+"("+a+")";break;case "STD_DEV":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";b=Blockly.Dart.provideFunction_("math_standard_deviation",["num "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," // First filter list for numbers only."," List numbers = new List.from(myList);"," numbers.removeWhere((a) => a is! num);"," if (numbers.isEmpty) return null;"," num n = numbers.length;"," num sum = 0;"," numbers.forEach((x) => sum += x);"," num mean = sum / n;", " num sumSquare = 0;"," numbers.forEach((x) => sumSquare += Math.pow(x - mean, 2));"," return Math.sqrt(sumSquare / n);","}"]);b=b+"("+a+")";break;case "RANDOM":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";b=Blockly.Dart.provideFunction_("math_random_item",["dynamic "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(List myList) {"," int x = new Math.Random().nextInt(myList.length);"," return myList[x];","}"]);b=b+"("+a+")";break;default:throw"Unknown operator: "+b;}return[b, Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.math_modulo=function(a){var b=Blockly.Dart.valueToCode(a,"DIVIDEND",Blockly.Dart.ORDER_MULTIPLICATIVE)||"0";a=Blockly.Dart.valueToCode(a,"DIVISOR",Blockly.Dart.ORDER_MULTIPLICATIVE)||"0";return[b+" % "+a,Blockly.Dart.ORDER_MULTIPLICATIVE]}; Blockly.Dart.math_constrain=function(a){Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";var b=Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_NONE)||"0",c=Blockly.Dart.valueToCode(a,"LOW",Blockly.Dart.ORDER_NONE)||"0";a=Blockly.Dart.valueToCode(a,"HIGH",Blockly.Dart.ORDER_NONE)||"double.INFINITY";return["Math.min(Math.max("+b+", "+c+"), "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}; @@ -71,15 +76,17 @@ d+";\n");for(var e=d?"dynamic":"void",f=[],g=0;g list = str.split(exp);"," final title = new StringBuffer();"," for (String part in list) {"," if (part.length > 0) {", -" title.write(part[0].toUpperCase());"," if (part.length > 0) {"," title.write(part.substring(1).toLowerCase());"," }"," }"," }"," return title.toString();","}"]),a=Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_NONE)||"''",a=b+"("+a+")");return[a,Blockly.Dart.ORDER_UNARY_POSTFIX]}; +Blockly.Dart.text_join=function(a){switch(a.itemCount_){case 0:return["''",Blockly.Dart.ORDER_ATOMIC];case 1:return[(Blockly.Dart.valueToCode(a,"ADD0",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".toString()",Blockly.Dart.ORDER_UNARY_POSTFIX];default:for(var b=Array(a.itemCount_),c=0;c list = str.split(exp);"," final title = new StringBuffer();"," for (String part in list) {", +" if (part.length > 0) {"," title.write(part[0].toUpperCase());"," if (part.length > 0) {"," title.write(part.substring(1).toLowerCase());"," }"," }"," }"," return title.toString();","}"])+"("+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}; Blockly.Dart.text_trim=function(a){var b={LEFT:".replaceFirst(new RegExp(r'^\\s+'), '')",RIGHT:".replaceFirst(new RegExp(r'\\s+$'), '')",BOTH:".trim()"}[a.getFieldValue("MODE")];return[(Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+b,Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.text_print=function(a){return"print("+(Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_NONE)||"''")+");\n"}; Blockly.Dart.text_prompt_ext=function(a){Blockly.Dart.definitions_.import_dart_html="import 'dart:html' as Html;";var b="Html.window.prompt("+(a.getField("TEXT")?Blockly.Dart.quote_(a.getFieldValue("TEXT")):Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_NONE)||"''")+", '')";"NUMBER"==a.getFieldValue("TYPE")&&(Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;",b="Math.parseDouble("+b+")");return[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.text_prompt=Blockly.Dart.text_prompt_ext;Blockly.Dart.variables={};Blockly.Dart.variables_get=function(a){return[Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),Blockly.Dart.ORDER_ATOMIC]};Blockly.Dart.variables_set=function(a){var b=Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_ASSIGNMENT)||"0";return Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE)+" = "+b+";\n"}; \ No newline at end of file diff --git a/demos/blockfactory/blocks.js b/demos/blockfactory/blocks.js index 5008e6a20..012c76fe0 100644 --- a/demos/blockfactory/blocks.js +++ b/demos/blockfactory/blocks.js @@ -505,13 +505,13 @@ Blockly.Blocks['type_group'] = { // Parse XML to restore the group of types. this.typeCount_ = parseInt(container.getAttribute('types'), 10); this.updateShape_(); - for (var x = 0; x < this.typeCount_; x++) { - this.removeInput('TYPE' + x); + for (var i = 0; i < this.typeCount_; i++) { + this.removeInput('TYPE' + i); } - for (var x = 0; x < this.typeCount_; x++) { - var input = this.appendValueInput('TYPE' + x) + for (var i = 0; i < this.typeCount_; i++) { + var input = this.appendValueInput('TYPE' + i) .setCheck('Type'); - if (x == 0) { + if (i == 0) { input.appendField('any of'); } } diff --git a/demos/blockfactory/factory.js b/demos/blockfactory/factory.js index 3e1cf16a0..1bfdf6ce9 100644 --- a/demos/blockfactory/factory.js +++ b/demos/blockfactory/factory.js @@ -501,8 +501,8 @@ function getTypesFrom_(block, name) { types = [escapeString(typeBlock.getFieldValue('TYPE'))]; } else if (typeBlock.type == 'type_group') { types = []; - for (var n = 0; n < typeBlock.typeCount_; n++) { - types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + n)); + for (var i = 0; i < typeBlock.typeCount_; i++) { + types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + i)); } // Remove duplicates. var hash = Object.create(null); diff --git a/generators/dart.js b/generators/dart.js index f0d7715ea..6fb1236d4 100644 --- a/generators/dart.js +++ b/generators/dart.js @@ -45,16 +45,28 @@ Blockly.Dart = new Blockly.Generator('Dart'); Blockly.Dart.addReservedWords( // https://www.dartlang.org/docs/spec/latest/dart-language-specification.pdf // Section 16.1.1 - 'assert,break,case,catch,class,const,continue,default,do,else,enum,extends,false,final,finally,for,if,in,is,new,null,rethrow,return,super,switch,this,throw,true,try,var,void,while,with,' + + 'assert,break,case,catch,class,const,continue,default,do,else,enum,' + + 'extends,false,final,finally,for,if,in,is,new,null,rethrow,return,super,' + + 'switch,this,throw,true,try,var,void,while,with,' + // https://api.dartlang.org/dart_core.html - 'print,identityHashCode,identical,BidirectionalIterator,Comparable,double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,ArgumentError,AssertionError,CastError,ConcurrentModificationError,CyclicInitializationError,Error,Exception,FallThroughError,FormatException,IntegerDivisionByZeroException,NoSuchMethodError,NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,StateError,TypeError,UnimplementedError,UnsupportedError'); + 'print,identityHashCode,identical,BidirectionalIterator,Comparable,' + + 'double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,' + + 'Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,' + + 'Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,' + + 'StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,' + + 'ArgumentError,AssertionError,CastError,ConcurrentModificationError,' + + 'CyclicInitializationError,Error,Exception,FallThroughError,' + + 'FormatException,IntegerDivisionByZeroException,NoSuchMethodError,' + + 'NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,' + + 'StateError,TypeError,UnimplementedError,UnsupportedError' +); /** * Order of operation ENUMs. * https://www.dartlang.org/docs/dart-up-and-running/ch02.html#operator_table */ Blockly.Dart.ORDER_ATOMIC = 0; // 0 "" ... -Blockly.Dart.ORDER_UNARY_POSTFIX = 1; // expr++ expr-- () [] . +Blockly.Dart.ORDER_UNARY_POSTFIX = 1; // expr++ expr-- () [] . ?. Blockly.Dart.ORDER_UNARY_PREFIX = 2; // -expr !expr ~expr ++expr --expr Blockly.Dart.ORDER_MULTIPLICATIVE = 3; // * / % ~/ Blockly.Dart.ORDER_ADDITIVE = 4; // + - @@ -66,11 +78,18 @@ Blockly.Dart.ORDER_RELATIONAL = 9; // >= > <= < as is is! Blockly.Dart.ORDER_EQUALITY = 10; // == != Blockly.Dart.ORDER_LOGICAL_AND = 11; // && Blockly.Dart.ORDER_LOGICAL_OR = 12; // || -Blockly.Dart.ORDER_CONDITIONAL = 13; // expr ? expr : expr -Blockly.Dart.ORDER_CASCADE = 14; // .. -Blockly.Dart.ORDER_ASSIGNMENT = 15; // = *= /= ~/= %= += -= <<= >>= &= ^= |= +Blockly.Dart.ORDER_IF_NULL = 13; // ?? +Blockly.Dart.ORDER_CONDITIONAL = 14; // expr ? expr : expr +Blockly.Dart.ORDER_CASCADE = 15; // .. +Blockly.Dart.ORDER_ASSIGNMENT = 16; // = *= /= ~/= %= += -= <<= >>= &= ^= |= Blockly.Dart.ORDER_NONE = 99; // (...) +/** + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. + */ +Blockly.Dart.ONE_BASED_INDEXING = true; + /** * Initialise the database of variable names. * @param {!Blockly.Workspace} workspace Workspace to generate code from. @@ -182,9 +201,9 @@ Blockly.Dart.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.Dart.allNestedComments(childBlock); if (comment) { @@ -198,3 +217,63 @@ Blockly.Dart.scrub_ = function(block, code) { var nextCode = Blockly.Dart.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value while taking into account indexing. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @param {number=} opt_order The highest order acting on this value. + * @return {string|number} + */ +Blockly.Dart.getAdjusted = function(block, atId, opt_delta, opt_negate, + opt_order) { + var delta = opt_delta || 0; + var order = opt_order || Blockly.Dart.ORDER_NONE; + if (Blockly.Dart.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.Dart.ONE_BASED_INDEXING ? '1' : '0'; + if (delta) { + var at = Blockly.Dart.valueToCode(block, atId, + Blockly.Dart.ORDER_ADDITIVE) || defaultAtIndex; + } else if (opt_negate) { + var at = Blockly.Dart.valueToCode(block, atId, + Blockly.Dart.ORDER_UNARY_PREFIX) || defaultAtIndex; + } else { + var at = Blockly.Dart.valueToCode(block, atId, order) || + defaultAtIndex; + } + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseInt(at, 10) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = at + ' + ' + delta; + var innerOrder = Blockly.Dart.ORDER_ADDITIVE; + } else if (delta < 0) { + at = at + ' - ' + -delta; + var innerOrder = Blockly.Dart.ORDER_ADDITIVE; + } + if (opt_negate) { + if (delta) { + at = '-(' + at + ')'; + } else { + at = '-' + at; + } + var innerOrder = Blockly.Dart.ORDER_UNARY_PREFIX; + } + innerOrder = Math.floor(innerOrder); + order = Math.floor(order); + if (innerOrder && order >= innerOrder) { + at = '(' + at + ')'; + } + } + return at; +}; diff --git a/generators/dart/colour.js b/generators/dart/colour.js index d73415522..7ded91419 100644 --- a/generators/dart/colour.js +++ b/generators/dart/colour.js @@ -43,13 +43,13 @@ Blockly.Dart['colour_random'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'colour_random', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '() {', - ' String hex = \'0123456789abcdef\';', - ' var rnd = new Math.Random();', - ' return \'#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', - ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', - ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\';', - '}']); + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '() {', + ' String hex = \'0123456789abcdef\';', + ' var rnd = new Math.Random();', + ' return \'#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', + ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', + ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\';', + '}']); var code = functionName + '()'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; @@ -67,22 +67,22 @@ Blockly.Dart['colour_rgb'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'colour_rgb', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num r, num g, num b) {', - ' num rn = (Math.max(Math.min(r, 1), 0) * 255).round();', - ' String rs = rn.toInt().toRadixString(16);', - ' rs = \'0$rs\';', - ' rs = rs.substring(rs.length - 2);', - ' num gn = (Math.max(Math.min(g, 1), 0) * 255).round();', - ' String gs = gn.toInt().toRadixString(16);', - ' gs = \'0$gs\';', - ' gs = gs.substring(gs.length - 2);', - ' num bn = (Math.max(Math.min(b, 1), 0) * 255).round();', - ' String bs = bn.toInt().toRadixString(16);', - ' bs = \'0$bs\';', - ' bs = bs.substring(bs.length - 2);', - ' return \'#$rs$gs$bs\';', - '}']); + ' num rn = (Math.max(Math.min(r, 1), 0) * 255).round();', + ' String rs = rn.toInt().toRadixString(16);', + ' rs = \'0$rs\';', + ' rs = rs.substring(rs.length - 2);', + ' num gn = (Math.max(Math.min(g, 1), 0) * 255).round();', + ' String gs = gn.toInt().toRadixString(16);', + ' gs = \'0$gs\';', + ' gs = gs.substring(gs.length - 2);', + ' num bn = (Math.max(Math.min(b, 1), 0) * 255).round();', + ' String bs = bn.toInt().toRadixString(16);', + ' bs = \'0$bs\';', + ' bs = bs.substring(bs.length - 2);', + ' return \'#$rs$gs$bs\';', + '}']); var code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; @@ -100,29 +100,29 @@ Blockly.Dart['colour_blend'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'colour_blend', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String c1, String c2, num ratio) {', - ' ratio = Math.max(Math.min(ratio, 1), 0);', - ' int r1 = int.parse(\'0x${c1.substring(1, 3)}\');', - ' int g1 = int.parse(\'0x${c1.substring(3, 5)}\');', - ' int b1 = int.parse(\'0x${c1.substring(5, 7)}\');', - ' int r2 = int.parse(\'0x${c2.substring(1, 3)}\');', - ' int g2 = int.parse(\'0x${c2.substring(3, 5)}\');', - ' int b2 = int.parse(\'0x${c2.substring(5, 7)}\');', - ' num rn = (r1 * (1 - ratio) + r2 * ratio).round();', - ' String rs = rn.toInt().toRadixString(16);', - ' num gn = (g1 * (1 - ratio) + g2 * ratio).round();', - ' String gs = gn.toInt().toRadixString(16);', - ' num bn = (b1 * (1 - ratio) + b2 * ratio).round();', - ' String bs = bn.toInt().toRadixString(16);', - ' rs = \'0$rs\';', - ' rs = rs.substring(rs.length - 2);', - ' gs = \'0$gs\';', - ' gs = gs.substring(gs.length - 2);', - ' bs = \'0$bs\';', - ' bs = bs.substring(bs.length - 2);', - ' return \'#$rs$gs$bs\';', - '}']); + ' ratio = Math.max(Math.min(ratio, 1), 0);', + ' int r1 = int.parse(\'0x${c1.substring(1, 3)}\');', + ' int g1 = int.parse(\'0x${c1.substring(3, 5)}\');', + ' int b1 = int.parse(\'0x${c1.substring(5, 7)}\');', + ' int r2 = int.parse(\'0x${c2.substring(1, 3)}\');', + ' int g2 = int.parse(\'0x${c2.substring(3, 5)}\');', + ' int b2 = int.parse(\'0x${c2.substring(5, 7)}\');', + ' num rn = (r1 * (1 - ratio) + r2 * ratio).round();', + ' String rs = rn.toInt().toRadixString(16);', + ' num gn = (g1 * (1 - ratio) + g2 * ratio).round();', + ' String gs = gn.toInt().toRadixString(16);', + ' num bn = (b1 * (1 - ratio) + b2 * ratio).round();', + ' String bs = bn.toInt().toRadixString(16);', + ' rs = \'0$rs\';', + ' rs = rs.substring(rs.length - 2);', + ' gs = \'0$gs\';', + ' gs = gs.substring(gs.length - 2);', + ' bs = \'0$bs\';', + ' bs = bs.substring(bs.length - 2);', + ' return \'#$rs$gs$bs\';', + '}']); var code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/lists.js b/generators/dart/lists.js index 24fcd2243..ae5ecc20e 100644 --- a/generators/dart/lists.js +++ b/generators/dart/lists.js @@ -38,49 +38,52 @@ Blockly.Dart['lists_create_empty'] = function(block) { Blockly.Dart['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Dart.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Dart.valueToCode(block, 'ADD' + i, Blockly.Dart.ORDER_NONE) || 'null'; } - code = '[' + code.join(', ') + ']'; + var code = '[' + elements.join(', ') + ']'; return [code, Blockly.Dart.ORDER_ATOMIC]; }; Blockly.Dart['lists_repeat'] = function(block) { // Create a list with one element repeated. - var argument0 = Blockly.Dart.valueToCode(block, 'ITEM', - Blockly.Dart.ORDER_NONE) || 'null'; - var argument1 = Blockly.Dart.valueToCode(block, 'NUM', - Blockly.Dart.ORDER_NONE) || '0'; - var code = 'new List.filled(' + argument1 + ', ' + argument0 + ')'; + var element = Blockly.Dart.valueToCode(block, 'ITEM', + Blockly.Dart.ORDER_NONE) || 'null'; + var repeatCount = Blockly.Dart.valueToCode(block, 'NUM', + Blockly.Dart.ORDER_NONE) || '0'; + var code = 'new List.filled(' + repeatCount + ', ' + element + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var list = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; - return [argument0 + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [list + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var list = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; - return [argument0 + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [list + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_indexOf'] = function(block) { // Find an item in the list. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.Dart.valueToCode(block, 'FIND', + var item = Blockly.Dart.valueToCode(block, 'FIND', Blockly.Dart.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Dart.valueToCode(block, 'VALUE', + var list = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Dart.ORDER_ADDITIVE]; + var code = list + '.' + operator + '(' + item + ')'; + if (Blockly.Dart.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.Dart.ORDER_ADDITIVE]; + } + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_getIndex'] = function(block) { @@ -88,94 +91,156 @@ Blockly.Dart['lists_getIndex'] = function(block) { // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Dart.valueToCode(block, 'AT', - Blockly.Dart.ORDER_UNARY_PREFIX) || '1'; - var list = Blockly.Dart.valueToCode(block, 'VALUE', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; + var listOrder = (where == 'RANDOM' || where == 'FROM_END') ? + Blockly.Dart.ORDER_NONE : Blockly.Dart.ORDER_UNARY_POSTFIX; + var list = Blockly.Dart.valueToCode(block, 'VALUE', listOrder) || '[]'; + // Cache non-trivial values to variables to prevent repeated look-ups. + // Closure, which accesses and modifies 'list'. + function cacheList() { + var listVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_list', Blockly.Variables.NAME_TYPE); + var code = 'List ' + listVar + ' = ' + list + ';\n'; + list = listVar; + return code; + } + // If `list` would be evaluated more than once (which is the case for + // RANDOM REMOVE and FROM_END) and is non-trivial, make sure to access it + // only once. + if (((where == 'RANDOM' && mode == 'REMOVE') || where == 'FROM_END') && + !list.match(/^\w+$/)) { + // `list` is an expression, so we may not evaluate it more than once. + if (where == 'RANDOM') { + Blockly.Dart.definitions_['import_dart_math'] = + 'import \'dart:math\' as Math;'; + // We can use multiple statements. + var code = cacheList(); + var xVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += 'int ' + xVar + ' = new Math.Random().nextInt(' + list + + '.length);\n'; + code += list + '.removeAt(' + xVar + ');\n'; + return code; + } else { // where == 'FROM_END' + if (mode == 'REMOVE') { + // We can use multiple statements. + var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + var code = cacheList(); + code += list + '.removeAt(' + list + '.length' + ' - ' + at + ');\n'; + return code; - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '.first'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.removeAt(0)'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return list + '.removeAt(0);\n'; - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = list + '.last'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.removeLast()'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return list + '.removeLast();\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.removeAt(' + at + ')'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return list + '.removeAt(' + at + ');\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var functionName = Blockly.Dart.provideFunction_( - 'lists_get_from_end', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List myList, num x) {', - ' x = myList.length - x;', - ' return myList.removeAt(x);', - '}']); - code = functionName + '(' + list + ', ' + at + ')'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { - var functionName = Blockly.Dart.provideFunction_( - 'lists_remove_from_end', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List myList, num x) {', - ' x = myList.length - x;', - ' return myList.removeAt(x);', - '}']); - code = functionName + '(' + list + ', ' + at + ')'; - if (mode == 'GET_REMOVE') { + } else if (mode == 'GET') { + var at = Blockly.Dart.getAdjusted(block, 'AT', 1); + // We need to create a procedure to avoid reevaluating values. + var functionName = Blockly.Dart.provideFunction_( + 'lists_get_from_end', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list, num x) {', + ' x = my_list.length - x;', + ' return my_list[x];', + '}']); + var code = functionName + '(' + list + ', ' + at + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var at = Blockly.Dart.getAdjusted(block, 'AT', 1); + // We need to create a procedure to avoid reevaluating values. + var functionName = Blockly.Dart.provideFunction_( + 'lists_remove_from_end', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list, num x) {', + ' x = my_list.length - x;', + ' return my_list.removeAt(x);', + '}']); + var code = functionName + '(' + list + ', ' + at + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return code + ';\n'; } } - } else if (where == 'RANDOM') { - Blockly.Dart.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - var functionName = Blockly.Dart.provideFunction_( - 'lists_get_random_item', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List myList, bool remove) {', - ' int x = new Math.Random().nextInt(myList.length);', - ' if (remove) {', - ' return myList.removeAt(x);', - ' } else {', - ' return myList[x];', - ' }', - '}']); - code = functionName + '(' + list + ', ' + (mode != 'GET') + ')'; - if (mode == 'GET' || mode == 'GET_REMOVE') { - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return code + ';\n'; + } else { + // Either `list` is a simple variable, or we only need to refer to `list` + // once. + switch (where) { + case 'FIRST': + if (mode == 'GET') { + var code = list + '.first'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.removeAt(0)'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return list + '.removeAt(0);\n'; + } + break; + case 'LAST': + if (mode == 'GET') { + var code = list + '.last'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.removeLast()'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return list + '.removeLast();\n'; + } + break; + case 'FROM_START': + var at = Blockly.Dart.getAdjusted(block, 'AT'); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.removeAt(' + at + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return list + '.removeAt(' + at + ');\n'; + } + break; + case 'FROM_END': + var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + if (mode == 'GET') { + var code = list + '[' + list + '.length - ' + at + ']'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { + var code = list + '.removeAt(' + list + '.length - ' + at + ')'; + if (mode == 'GET_REMOVE') { + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return code + ';\n'; + } + } + break; + case 'RANDOM': + Blockly.Dart.definitions_['import_dart_math'] = + 'import \'dart:math\' as Math;'; + if (mode == 'REMOVE') { + // We can use multiple statements. + var xVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + var code = 'int ' + xVar + ' = new Math.Random().nextInt(' + list + + '.length);\n'; + code += list + '.removeAt(' + xVar + ');\n'; + return code; + } else if (mode == 'GET') { + var functionName = Blockly.Dart.provideFunction_( + 'lists_get_random_item', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list) {', + ' int x = new Math.Random().nextInt(my_list.length);', + ' return my_list[x];', + '}']); + var code = functionName + '(' + list + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var functionName = Blockly.Dart.provideFunction_( + 'lists_remove_random_item', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list) {', + ' int x = new Math.Random().nextInt(my_list.length);', + ' return my_list.removeAt(x);', + '}']); + var code = functionName + '(' + list + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } + break; } } throw 'Unhandled combination (lists_getIndex).'; @@ -184,12 +249,10 @@ Blockly.Dart['lists_getIndex'] = function(block) { Blockly.Dart['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. - var list = Blockly.Dart.valueToCode(block, 'LIST', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Dart.valueToCode(block, 'AT', - Blockly.Dart.ORDER_ADDITIVE) || '1'; + var list = Blockly.Dart.valueToCode(block, 'LIST', + Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; var value = Blockly.Dart.valueToCode(block, 'TO', Blockly.Dart.ORDER_ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. @@ -204,59 +267,61 @@ Blockly.Dart['lists_setIndex'] = function(block) { list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.insert(0, ' + value + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { + switch (where) { + case 'FIRST': + if (mode == 'SET') { + return list + '[0] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.insert(0, ' + value + ');\n'; + } + break; + case 'LAST': + if (mode == 'SET') { + var code = cacheList(); + code += list + '[' + list + '.length - 1] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + return list + '.add(' + value + ');\n'; + } + break; + case 'FROM_START': + var at = Blockly.Dart.getAdjusted(block, 'AT'); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.insert(' + at + ', ' + value + ');\n'; + } + break; + case 'FROM_END': + var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false, + Blockly.Dart.ORDER_ADDITIVE); var code = cacheList(); - code += list + '[' + list + '.length - 1] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - return list + '.add(' + value + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.insert(' + at + ', ' + value + ');\n'; - } - } else if (where == 'FROM_END') { - var code = cacheList(); - if (mode == 'SET') { - code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.insert(' + list + '.length - ' + at + ', ' + - value + ');\n'; - return code; - } - } else if (where == 'RANDOM') { - Blockly.Dart.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - var code = cacheList(); - var xVar = Blockly.Dart.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += 'int ' + xVar + - ' = new Math.Random().nextInt(' + list + '.length);'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.insert(' + xVar + ', ' + value + ');\n'; - return code; - } + if (mode == 'SET') { + code += list + '[' + list + '.length - ' + at + '] = ' + value + + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.insert(' + list + '.length - ' + at + ', ' + + value + ');\n'; + return code; + } + break; + case 'RANDOM': + Blockly.Dart.definitions_['import_dart_math'] = + 'import \'dart:math\' as Math;'; + var code = cacheList(); + var xVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += 'int ' + xVar + + ' = new Math.Random().nextInt(' + list + '.length);\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.insert(' + xVar + ', ' + value + ');\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; @@ -267,43 +332,67 @@ Blockly.Dart['lists_getSublist'] = function(block) { Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Dart.valueToCode(block, 'AT1', - Blockly.Dart.ORDER_NONE) || '1'; - var at2 = Blockly.Dart.valueToCode(block, 'AT2', - Blockly.Dart.ORDER_NONE) || '1'; - if ((where1 == 'FIRST' || where1 == 'FROM_START' && Blockly.isNumber(at1)) && - (where2 == 'LAST' || where2 == 'FROM_START' && Blockly.isNumber(at2))) { - // Simple case that can be done inline. - at1 = where1 == 'FIRST' ? 0 : parseInt(at1, 10) - 1; + if (list.match(/^\w+$/) || (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the list is a is a variable or doesn't require a call for length, + // don't generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + at1 = list + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 0, false, + Blockly.Dart.ORDER_ADDITIVE); + at2 = list + '.length - ' + at2; + break; + case 'LAST': + // There is no second index if LAST option is chosen. + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } if (where2 == 'LAST') { - code = list + '.sublist(' + at1 + ')'; + var code = list + '.sublist(' + at1 + ')'; } else { - at2 = parseInt(at2, 10); - code = list + '.sublist(' + at1 + ', ' + at2 + ')'; + var code = list + '.sublist(' + at1 + ', ' + at2 + ')'; } } else { + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + var at2 = Blockly.Dart.getAdjusted(block, 'AT2'); var functionName = Blockly.Dart.provideFunction_( 'lists_get_sublist', - [ 'List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(list, where1, at1, where2, at2) {', - ' int getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = list.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = list.length - 1;', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return list.sublist(at1, at2);', - '}']); + ' int getAt(where, at) {', + ' if (where == \'FROM_END\') {', + ' at = list.length - 1 - at;', + ' } else if (where == \'FIRST\') {', + ' at = 0;', + ' } else if (where == \'LAST\') {', + ' at = list.length - 1;', + ' } else if (where != \'FROM_START\') {', + ' throw \'Unhandled option (lists_getSublist).\';', + ' }', + ' return at;', + ' }', + ' at1 = getAt(where1, at1);', + ' at2 = getAt(where2, at2) + 1;', + ' return list.sublist(at1, at2);', + '}']); var code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -312,51 +401,52 @@ Blockly.Dart['lists_getSublist'] = function(block) { Blockly.Dart['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = Blockly.Dart.valueToCode(block, 'LIST', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; + var list = Blockly.Dart.valueToCode(block, 'LIST', + Blockly.Dart.ORDER_NONE) || '[]'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); var sortFunctionName = Blockly.Dart.provideFunction_( - 'lists_sort', - ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(list, type, direction) {', - ' var compareFuncs = {', - ' "NUMERIC": (a, b) => direction * a.compareTo(b),', - ' "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()),', - ' "IGNORE_CASE": ', - ' (a, b) => direction * ', - ' a.toString().toLowerCase().compareTo(b.toString().toLowerCase())', - ' };', - ' list = new List.from(list);', // Clone the list. - ' var compare = compareFuncs[type];', - ' list.sort(compare);', - ' return list;', - '}']); - return [sortFunctionName + '(' + listCode + ', ' + + 'lists_sort', + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(list, type, direction) {', + ' var compareFuncs = {', + ' "NUMERIC": (a, b) => direction * a.compareTo(b),', + ' "TEXT": (a, b) => direction * ' + + 'a.toString().compareTo(b.toString()),', + ' "IGNORE_CASE": ', + ' (a, b) => direction * ', + ' a.toString().toLowerCase().compareTo(b.toString().toLowerCase())', + ' };', + ' list = new List.from(list);', // Clone the list. + ' var compare = compareFuncs[type];', + ' list.sort(compare);', + ' return list;', + '}']); + return [sortFunctionName + '(' + list + ', ' + '"' + type + '", ' + direction + ')', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. - var value_input = Blockly.Dart.valueToCode(block, 'INPUT', + var input = Blockly.Dart.valueToCode(block, 'INPUT', Blockly.Dart.ORDER_UNARY_POSTFIX); - var value_delim = Blockly.Dart.valueToCode(block, 'DELIM', + var delimiter = Blockly.Dart.valueToCode(block, 'DELIM', Blockly.Dart.ORDER_NONE) || '\'\''; var mode = block.getFieldValue('MODE'); if (mode == 'SPLIT') { - if (!value_input) { - value_input = '\'\''; + if (!input) { + input = '\'\''; } var functionName = 'split'; } else if (mode == 'JOIN') { - if (!value_input) { - value_input = '[]'; + if (!input) { + input = '[]'; } var functionName = 'join'; } else { throw 'Unknown mode: ' + mode; } - var code = value_input + '.' + functionName + '(' + value_delim + ')'; + var code = input + '.' + functionName + '(' + delimiter + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/math.js b/generators/dart/math.js index 2f7c8027a..867822beb 100644 --- a/generators/dart/math.js +++ b/generators/dart/math.js @@ -200,25 +200,25 @@ Blockly.Dart['math_number_property'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_isPrime', - [ 'bool ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if (n == 2 || n == 3) {', - ' return true;', - ' }', - ' // False if n is null, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + + ['bool ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if (n == 2 || n == 3) {', + ' return true;', + ' }', + ' // False if n is null, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + ' n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', - ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', + ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); code = functionName + '(' + number_to_check + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; } @@ -275,12 +275,12 @@ Blockly.Dart['math_on_list'] = function(block) { case 'SUM': var functionName = Blockly.Dart.provideFunction_( 'math_sum', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' num sumVal = 0;', - ' myList.forEach((num entry) {sumVal += entry;});', - ' return sumVal;', - '}']); + ' num sumVal = 0;', + ' myList.forEach((num entry) {sumVal += entry;});', + ' return sumVal;', + '}']); code = functionName + '(' + list + ')'; break; case 'MIN': @@ -288,14 +288,14 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_min', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' if (myList.isEmpty) return null;', - ' num minVal = myList[0];', - ' myList.forEach((num entry) ' + + ' if (myList.isEmpty) return null;', + ' num minVal = myList[0];', + ' myList.forEach((num entry) ' + '{minVal = Math.min(minVal, entry);});', - ' return minVal;', - '}']); + ' return minVal;', + '}']); code = functionName + '(' + list + ')'; break; case 'MAX': @@ -303,14 +303,14 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_max', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' if (myList.isEmpty) return null;', - ' num maxVal = myList[0];', - ' myList.forEach((num entry) ' + - '{maxVal = Math.max(maxVal, entry);});', - ' return maxVal;', - '}']); + ' if (myList.isEmpty) return null;', + ' num maxVal = myList[0];', + ' myList.forEach((num entry) ' + + '{maxVal = Math.max(maxVal, entry);});', + ' return maxVal;', + '}']); code = functionName + '(' + list + ')'; break; case 'AVERAGE': @@ -318,38 +318,38 @@ Blockly.Dart['math_on_list'] = function(block) { // math_mean([null,null,"aString",1,9]) == 5.0. var functionName = Blockly.Dart.provideFunction_( 'math_mean', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only.', - ' List localList = new List.from(myList);', - ' localList.removeMatching((a) => a is! num);', - ' if (localList.isEmpty) return null;', - ' num sumVal = 0;', - ' localList.forEach((num entry) {sumVal += entry;});', - ' return sumVal / localList.length;', - '}']); + ' // First filter list for numbers only.', + ' List localList = new List.from(myList);', + ' localList.removeWhere((a) => a is! num);', + ' if (localList.isEmpty) return null;', + ' num sumVal = 0;', + ' localList.forEach((num entry) {sumVal += entry;});', + ' return sumVal / localList.length;', + '}']); code = functionName + '(' + list + ')'; break; case 'MEDIAN': var functionName = Blockly.Dart.provideFunction_( 'math_median', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only, then sort, ' + + ' // First filter list for numbers only, then sort, ' + 'then return middle value', - ' // or the average of two middle values if list has an ' + + ' // or the average of two middle values if list has an ' + 'even number of elements.', - ' List localList = new List.from(myList);', - ' localList.removeMatching((a) => a is! num);', - ' if (localList.isEmpty) return null;', - ' localList.sort((a, b) => (a - b));', - ' int index = localList.length ~/ 2;', - ' if (localList.length % 2 == 1) {', - ' return localList[index];', - ' } else {', - ' return (localList[index - 1] + localList[index]) / 2;', - ' }', - '}']); + ' List localList = new List.from(myList);', + ' localList.removeWhere((a) => a is! num);', + ' if (localList.isEmpty) return null;', + ' localList.sort((a, b) => (a - b));', + ' int index = localList.length ~/ 2;', + ' if (localList.length % 2 == 1) {', + ' return localList[index];', + ' } else {', + ' return (localList[index - 1] + localList[index]) / 2;', + ' }', + '}']); code = functionName + '(' + list + ')'; break; case 'MODE': @@ -360,35 +360,35 @@ Blockly.Dart['math_on_list'] = function(block) { // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Blockly.Dart.provideFunction_( 'math_modes', - [ 'List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List values) {', - ' List modes = [];', - ' List counts = [];', - ' int maxCount = 0;', - ' for (int i = 0; i < values.length; i++) {', - ' var value = values[i];', - ' bool found = false;', - ' int thisCount;', - ' for (int j = 0; j < counts.length; j++) {', - ' if (counts[j][0] == value) {', - ' thisCount = ++counts[j][1];', - ' found = true;', - ' break;', - ' }', - ' }', - ' if (!found) {', - ' counts.add([value, 1]);', - ' thisCount = 1;', - ' }', - ' maxCount = Math.max(thisCount, maxCount);', - ' }', - ' for (int j = 0; j < counts.length; j++) {', - ' if (counts[j][1] == maxCount) {', - ' modes.add(counts[j][0]);', - ' }', - ' }', - ' return modes;', - '}']); + ' List modes = [];', + ' List counts = [];', + ' int maxCount = 0;', + ' for (int i = 0; i < values.length; i++) {', + ' var value = values[i];', + ' bool found = false;', + ' int thisCount;', + ' for (int j = 0; j < counts.length; j++) {', + ' if (counts[j][0] == value) {', + ' thisCount = ++counts[j][1];', + ' found = true;', + ' break;', + ' }', + ' }', + ' if (!found) {', + ' counts.add([value, 1]);', + ' thisCount = 1;', + ' }', + ' maxCount = Math.max(thisCount, maxCount);', + ' }', + ' for (int j = 0; j < counts.length; j++) {', + ' if (counts[j][1] == maxCount) {', + ' modes.add(counts[j][0]);', + ' }', + ' }', + ' return modes;', + '}']); code = functionName + '(' + list + ')'; break; case 'STD_DEV': @@ -396,21 +396,21 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_standard_deviation', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only.', - ' List numbers = new List.from(myList);', - ' numbers.removeMatching((a) => a is! num);', - ' if (numbers.isEmpty) return null;', - ' num n = numbers.length;', - ' num sum = 0;', - ' numbers.forEach((x) => sum += x);', - ' num mean = sum / n;', - ' num sumSquare = 0;', - ' numbers.forEach((x) => sumSquare += ' + - 'Math.pow(x - mean, 2));', - ' return Math.sqrt(sumSquare / n);', - '}']); + ' // First filter list for numbers only.', + ' List numbers = new List.from(myList);', + ' numbers.removeWhere((a) => a is! num);', + ' if (numbers.isEmpty) return null;', + ' num n = numbers.length;', + ' num sum = 0;', + ' numbers.forEach((x) => sum += x);', + ' num mean = sum / n;', + ' num sumSquare = 0;', + ' numbers.forEach((x) => sumSquare += ' + + 'Math.pow(x - mean, 2));', + ' return Math.sqrt(sumSquare / n);', + '}']); code = functionName + '(' + list + ')'; break; case 'RANDOM': @@ -418,11 +418,11 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_random_item', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' int x = new Math.Random().nextInt(myList.length);', - ' return myList[x];', - '}']); + ' int x = new Math.Random().nextInt(myList.length);', + ' return myList[x];', + '}']); code = functionName + '(' + list + ')'; break; default: @@ -466,15 +466,15 @@ Blockly.Dart['math_random_int'] = function(block) { Blockly.Dart.ORDER_NONE) || '0'; var functionName = Blockly.Dart.provideFunction_( 'math_random_int', - [ 'int ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num a, num b) {', - ' if (a > b) {', - ' // Swap a and b to ensure a is smaller.', - ' num c = a;', - ' a = b;', - ' b = c;', - ' }', - ' return new Math.Random().nextInt(b - a + 1) + a;', - '}']); + ['int ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num a, num b) {', + ' if (a > b) {', + ' // Swap a and b to ensure a is smaller.', + ' num c = a;', + ' a = b;', + ' b = c;', + ' }', + ' return new Math.Random().nextInt(b - a + 1) + a;', + '}']); var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/procedures.js b/generators/dart/procedures.js index 0085c6bf6..ad2550cc7 100644 --- a/generators/dart/procedures.js +++ b/generators/dart/procedures.js @@ -50,8 +50,8 @@ Blockly.Dart['procedures_defreturn'] = function(block) { } var returnType = returnValue ? 'dynamic' : 'void'; var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Dart.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Dart.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = returnType + ' ' + funcName + '(' + args.join(', ') + ') {\n' + @@ -71,8 +71,8 @@ Blockly.Dart['procedures_callreturn'] = function(block) { var funcName = Blockly.Dart.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Dart.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Dart.valueToCode(block, 'ARG' + i, Blockly.Dart.ORDER_NONE) || 'null'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -84,8 +84,8 @@ Blockly.Dart['procedures_callnoreturn'] = function(block) { var funcName = Blockly.Dart.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Dart.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Dart.valueToCode(block, 'ARG' + i, Blockly.Dart.ORDER_NONE) || 'null'; } var code = funcName + '(' + args.join(', ') + ');\n'; diff --git a/generators/dart/text.js b/generators/dart/text.js index ba5a75cb0..5cdbb03ec 100644 --- a/generators/dart/text.js +++ b/generators/dart/text.js @@ -39,22 +39,22 @@ Blockly.Dart['text'] = function(block) { Blockly.Dart['text_join'] = function(block) { // Create a string made up of any number of elements of any type. - var code; - if (block.itemCount_ == 0) { - return ['\'\'', Blockly.Dart.ORDER_ATOMIC]; - } else if (block.itemCount_ == 1) { - var argument0 = Blockly.Dart.valueToCode(block, 'ADD0', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - code = argument0 + '.toString()'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else { - code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Dart.valueToCode(block, 'ADD' + n, - Blockly.Dart.ORDER_NONE) || '\'\''; - } - code = '[' + code.join(',') + '].join()'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + switch (block.itemCount_) { + case 0: + return ['\'\'', Blockly.Dart.ORDER_ATOMIC]; + case 1: + var element = Blockly.Dart.valueToCode(block, 'ADD0', + Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; + var code = element + '.toString()'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + default: + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Dart.valueToCode(block, 'ADD' + i, + Blockly.Dart.ORDER_NONE) || '\'\''; + } + var code = '[' + elements.join(',') + '].join()'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; } }; @@ -62,43 +62,44 @@ Blockly.Dart['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.Dart.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return varName + ' = [' + varName + ', ' + argument0 + '].join();\n'; + var value = Blockly.Dart.valueToCode(block, 'TEXT', + Blockly.Dart.ORDER_NONE) || '\'\''; + return varName + ' = [' + varName + ', ' + value + '].join();\n'; }; Blockly.Dart['text_length'] = function(block) { // String or array length. - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return [argument0 + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [text + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return [argument0 + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [text + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_indexOf'] = function(block) { // Search the text for a substring. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.Dart.valueToCode(block, 'FIND', + var substring = Blockly.Dart.valueToCode(block, 'FIND', Blockly.Dart.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Dart.valueToCode(block, 'VALUE', + var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Dart.ORDER_ADDITIVE]; + var code = text + '.' + operator + '(' + substring + ')'; + if (Blockly.Dart.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.Dart.ORDER_ADDITIVE]; + } + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Dart.valueToCode(block, 'AT', - Blockly.Dart.ORDER_NONE) || '1'; var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; switch (where) { @@ -106,26 +107,20 @@ Blockly.Dart['text_charAt'] = function(block) { var code = text + '[0]'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; case 'FROM_START': - // Blockly uses one-based indicies. - if (at.match(/^-?\d+$/)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } + var at = Blockly.Dart.getAdjusted(block, 'AT'); var code = text + '[' + at + ']'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; case 'LAST': at = 1; // Fall through. case 'FROM_END': + var at = Blockly.Dart.getAdjusted(block, 'AT', 1); var functionName = Blockly.Dart.provideFunction_( 'text_get_from_end', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String text, num x) {', - ' return text[text.length - x];', - '}']); + ' return text[text.length - x];', + '}']); code = functionName + '(' + text + ', ' + at + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; case 'RANDOM': @@ -133,11 +128,11 @@ Blockly.Dart['text_charAt'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'text_random_letter', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String text) {', - ' int x = new Math.Random().nextInt(text.length);', - ' return text[x];', - '}']); + ' int x = new Math.Random().nextInt(text.length);', + ' return text[x];', + '}']); code = functionName + '(' + text + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; } @@ -150,35 +145,69 @@ Blockly.Dart['text_getSubstring'] = function(block) { Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Dart.valueToCode(block, 'AT1', - Blockly.Dart.ORDER_NONE) || '1'; - var at2 = Blockly.Dart.valueToCode(block, 'AT2', - Blockly.Dart.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = text; + } else if (text.match(/^'?\w+'?$/) || + (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the text is a variable or literal or doesn't require a call for + // length, don't generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + at1 = text + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 0, false, + Blockly.Dart.ORDER_ADDITIVE); + at2 = text + '.length - ' + at2; + break; + case 'LAST': + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + if (where2 == 'LAST') { + var code = text + '.substring(' + at1 + ')'; + } else { + var code = text + '.substring(' + at1 + ', ' + at2 + ')'; + } } else { + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + var at2 = Blockly.Dart.getAdjusted(block, 'AT2'); var functionName = Blockly.Dart.provideFunction_( 'text_get_substring', - [ 'function ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(text, where1, at1, where2, at2) {', - ' function getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = text.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = text.length - 1;', - ' } else {', - ' throw \'Unhandled option (text_getSubstring).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return text.substring(at1, at2);', - '}']); + ' int getAt(where, at) {', + ' if (where == \'FROM_END\') {', + ' at = text.length - 1 - at;', + ' } else if (where == \'FIRST\') {', + ' at = 0;', + ' } else if (where == \'LAST\') {', + ' at = text.length - 1;', + ' } else if (where != \'FROM_START\') {', + ' throw \'Unhandled option (text_getSubstring).\';', + ' }', + ' return at;', + ' }', + ' at1 = getAt(where1, at1);', + ' at2 = getAt(where2, at2) + 1;', + ' return text.substring(at1, at2);', + '}']); var code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -193,34 +222,32 @@ Blockly.Dart['text_changeCase'] = function(block) { 'TITLECASE': null }; var operator = OPERATORS[block.getFieldValue('CASE')]; - var code; + var textOrder = operator ? Blockly.Dart.ORDER_UNARY_POSTFIX : + Blockly.Dart.ORDER_NONE; + var text = Blockly.Dart.valueToCode(block, 'TEXT', textOrder) || '\'\''; if (operator) { // Upper and lower case are functions built into Dart. - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - code = argument0 + operator; + var code = text + operator; } else { // Title case is not a native Dart function. Define one. var functionName = Blockly.Dart.provideFunction_( 'text_toTitleCase', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String str) {', - ' RegExp exp = new RegExp(r\'\\b\');', - ' List list = str.split(exp);', - ' final title = new StringBuffer();', - ' for (String part in list) {', - ' if (part.length > 0) {', - ' title.write(part[0].toUpperCase());', - ' if (part.length > 0) {', - ' title.write(part.substring(1).toLowerCase());', - ' }', - ' }', - ' }', - ' return title.toString();', - '}']); - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', - Blockly.Dart.ORDER_NONE) || '\'\''; - code = functionName + '(' + argument0 + ')'; + ' RegExp exp = new RegExp(r\'\\b\');', + ' List list = str.split(exp);', + ' final title = new StringBuffer();', + ' for (String part in list) {', + ' if (part.length > 0) {', + ' title.write(part[0].toUpperCase());', + ' if (part.length > 0) {', + ' title.write(part.substring(1).toLowerCase());', + ' }', + ' }', + ' }', + ' return title.toString();', + '}']); + var code = functionName + '(' + text + ')'; } return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; @@ -233,16 +260,16 @@ Blockly.Dart['text_trim'] = function(block) { 'BOTH': '.trim()' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', + var text = Blockly.Dart.valueToCode(block, 'TEXT', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return [argument0 + operator, Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [text + operator, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', + var msg = Blockly.Dart.valueToCode(block, 'TEXT', Blockly.Dart.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ');\n'; + return 'print(' + msg + ');\n'; }; Blockly.Dart['text_prompt_ext'] = function(block) { diff --git a/generators/javascript.js b/generators/javascript.js index f14671899..db0952532 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -135,8 +135,8 @@ Blockly.JavaScript.ORDER_OVERRIDES = [ ]; /** - * Allow for switching between one and zero based indexing, one based by - * default. + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. */ Blockly.JavaScript.ONE_BASED_INDEXING = true; @@ -258,3 +258,66 @@ Blockly.JavaScript.scrub_ = function(block, code) { var nextCode = Blockly.JavaScript.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value while taking into account indexing. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @param {number=} opt_order The highest order acting on this value. + * @return {string|number} + */ +Blockly.JavaScript.getAdjusted = function(block, atId, opt_delta, opt_negate, + opt_order) { + var delta = opt_delta || 0; + var order = opt_order || Blockly.JavaScript.ORDER_NONE; + if (Blockly.JavaScript.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.JavaScript.ONE_BASED_INDEXING ? '1' : '0'; + if (delta > 0) { + var at = Blockly.JavaScript.valueToCode(block, atId, + Blockly.JavaScript.ORDER_ADDITION) || defaultAtIndex; + } else if (delta < 0) { + var at = Blockly.JavaScript.valueToCode(block, atId, + Blockly.JavaScript.ORDER_SUBTRACTION) || defaultAtIndex; + } else if (opt_negate) { + var at = Blockly.JavaScript.valueToCode(block, atId, + Blockly.JavaScript.ORDER_UNARY_NEGATION) || defaultAtIndex; + } else { + var at = Blockly.JavaScript.valueToCode(block, atId, order) || + defaultAtIndex; + } + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseFloat(at) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = at + ' + ' + delta; + var innerOrder = Blockly.JavaScript.ORDER_ADDITION; + } else if (delta < 0) { + at = at + ' - ' + -delta; + var innerOrder = Blockly.JavaScript.ORDER_SUBTRACTION; + } + if (opt_negate) { + if (delta) { + at = '-(' + at + ')'; + } else { + at = '-' + at; + } + var innerOrder = Blockly.JavaScript.ORDER_UNARY_NEGATION; + } + innerOrder = Math.floor(innerOrder); + order = Math.floor(order); + if (innerOrder && order >= innerOrder) { + at = '(' + at + ')'; + } + } + return at; +}; diff --git a/generators/javascript/colour.js b/generators/javascript/colour.js index b8e290360..21b372e9c 100644 --- a/generators/javascript/colour.js +++ b/generators/javascript/colour.js @@ -39,7 +39,7 @@ Blockly.JavaScript['colour_random'] = function(block) { // Generate a random colour. var functionName = Blockly.JavaScript.provideFunction_( 'colourRandom', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', ' var num = Math.floor(Math.random() * Math.pow(2, 24));', ' return \'#\' + (\'00000\' + num.toString(16)).substr(-6);', '}']); @@ -57,16 +57,16 @@ Blockly.JavaScript['colour_rgb'] = function(block) { Blockly.JavaScript.ORDER_COMMA) || 0; var functionName = Blockly.JavaScript.provideFunction_( 'colourRgb', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b) {', - ' r = Math.max(Math.min(Number(r), 100), 0) * 2.55;', - ' g = Math.max(Math.min(Number(g), 100), 0) * 2.55;', - ' b = Math.max(Math.min(Number(b), 100), 0) * 2.55;', - ' r = (\'0\' + (Math.round(r) || 0).toString(16)).slice(-2);', - ' g = (\'0\' + (Math.round(g) || 0).toString(16)).slice(-2);', - ' b = (\'0\' + (Math.round(b) || 0).toString(16)).slice(-2);', - ' return \'#\' + r + g + b;', - '}']); + ' r = Math.max(Math.min(Number(r), 100), 0) * 2.55;', + ' g = Math.max(Math.min(Number(g), 100), 0) * 2.55;', + ' b = Math.max(Math.min(Number(b), 100), 0) * 2.55;', + ' r = (\'0\' + (Math.round(r) || 0).toString(16)).slice(-2);', + ' g = (\'0\' + (Math.round(g) || 0).toString(16)).slice(-2);', + ' b = (\'0\' + (Math.round(b) || 0).toString(16)).slice(-2);', + ' return \'#\' + r + g + b;', + '}']); var code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; @@ -81,23 +81,23 @@ Blockly.JavaScript['colour_blend'] = function(block) { Blockly.JavaScript.ORDER_COMMA) || 0.5; var functionName = Blockly.JavaScript.provideFunction_( 'colourBlend', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(c1, c2, ratio) {', - ' ratio = Math.max(Math.min(Number(ratio), 1), 0);', - ' var r1 = parseInt(c1.substring(1, 3), 16);', - ' var g1 = parseInt(c1.substring(3, 5), 16);', - ' var b1 = parseInt(c1.substring(5, 7), 16);', - ' var r2 = parseInt(c2.substring(1, 3), 16);', - ' var g2 = parseInt(c2.substring(3, 5), 16);', - ' var b2 = parseInt(c2.substring(5, 7), 16);', - ' var r = Math.round(r1 * (1 - ratio) + r2 * ratio);', - ' var g = Math.round(g1 * (1 - ratio) + g2 * ratio);', - ' var b = Math.round(b1 * (1 - ratio) + b2 * ratio);', - ' r = (\'0\' + (r || 0).toString(16)).slice(-2);', - ' g = (\'0\' + (g || 0).toString(16)).slice(-2);', - ' b = (\'0\' + (b || 0).toString(16)).slice(-2);', - ' return \'#\' + r + g + b;', - '}']); + ' ratio = Math.max(Math.min(Number(ratio), 1), 0);', + ' var r1 = parseInt(c1.substring(1, 3), 16);', + ' var g1 = parseInt(c1.substring(3, 5), 16);', + ' var b1 = parseInt(c1.substring(5, 7), 16);', + ' var r2 = parseInt(c2.substring(1, 3), 16);', + ' var g2 = parseInt(c2.substring(3, 5), 16);', + ' var b2 = parseInt(c2.substring(5, 7), 16);', + ' var r = Math.round(r1 * (1 - ratio) + r2 * ratio);', + ' var g = Math.round(g1 * (1 - ratio) + g2 * ratio);', + ' var b = Math.round(b1 * (1 - ratio) + b2 * ratio);', + ' r = (\'0\' + (r || 0).toString(16)).slice(-2);', + ' g = (\'0\' + (g || 0).toString(16)).slice(-2);', + ' b = (\'0\' + (b || 0).toString(16)).slice(-2);', + ' return \'#\' + r + g + b;', + '}']); var code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/lists.js b/generators/javascript/lists.js index 4bb6aac20..d1e66a0ed 100644 --- a/generators/javascript/lists.js +++ b/generators/javascript/lists.js @@ -36,12 +36,12 @@ Blockly.JavaScript['lists_create_empty'] = function(block) { Blockly.JavaScript['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.JavaScript.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.JavaScript.valueToCode(block, 'ADD' + i, Blockly.JavaScript.ORDER_COMMA) || 'null'; } - code = '[' + code.join(', ') + ']'; + var code = '[' + elements.join(', ') + ']'; return [code, Blockly.JavaScript.ORDER_ATOMIC]; }; @@ -49,46 +49,49 @@ Blockly.JavaScript['lists_repeat'] = function(block) { // Create a list with one element repeated. var functionName = Blockly.JavaScript.provideFunction_( 'listsRepeat', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(value, n) {', - ' var array = [];', - ' for (var i = 0; i < n; i++) {', - ' array[i] = value;', - ' }', - ' return array;', - '}']); - var argument0 = Blockly.JavaScript.valueToCode(block, 'ITEM', + ' var array = [];', + ' for (var i = 0; i < n; i++) {', + ' array[i] = value;', + ' }', + ' return array;', + '}']); + var element = Blockly.JavaScript.valueToCode(block, 'ITEM', Blockly.JavaScript.ORDER_COMMA) || 'null'; - var argument1 = Blockly.JavaScript.valueToCode(block, 'NUM', + var repeatCount = Blockly.JavaScript.valueToCode(block, 'NUM', Blockly.JavaScript.ORDER_COMMA) || '0'; - var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; + var code = functionName + '(' + element + ', ' + repeatCount + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', - Blockly.JavaScript.ORDER_FUNCTION_CALL) || '[]'; - return [argument0 + '.length', Blockly.JavaScript.ORDER_MEMBER]; + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', + Blockly.JavaScript.ORDER_MEMBER) || '[]'; + return [list + '.length', Blockly.JavaScript.ORDER_MEMBER]; }; Blockly.JavaScript['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '[]'; - return ['!' + argument0 + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; + return ['!' + list + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; }; Blockly.JavaScript['lists_indexOf'] = function(block) { // Find an item in the list. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.JavaScript.valueToCode(block, 'FIND', + var item = Blockly.JavaScript.valueToCode(block, 'FIND', Blockly.JavaScript.ORDER_NONE) || '\'\''; - var argument1 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '[]'; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.JavaScript.ORDER_ADDITION]; + var code = list + '.' + operator + '(' + item + ')'; + if (Blockly.JavaScript.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.JavaScript.ORDER_ADDITION]; + } + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_getIndex'] = function(block) { @@ -96,86 +99,76 @@ Blockly.JavaScript['lists_getIndex'] = function(block) { // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.JavaScript.valueToCode(block, 'AT', - Blockly.JavaScript.ORDER_UNARY_NEGATION) || '1'; - var list = Blockly.JavaScript.valueToCode(block, 'VALUE', - Blockly.JavaScript.ORDER_MEMBER) || '[]'; + var listOrder = (where == 'RANDOM') ? Blockly.JavaScript.ORDER_COMMA : + Blockly.JavaScript.ORDER_MEMBER; + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', listOrder) || '[]'; - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '[0]'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.shift()'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'REMOVE') { - return list + '.shift();\n'; - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = list + '.slice(-1)[0]'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.pop()'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'REMOVE') { - return list + '.pop();\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.splice(' + at + ', 1)[0]'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return list + '.splice(' + at + ', 1);\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var code = list + '.slice(-' + at + ')[0]'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { + switch (where) { + case ('FIRST'): + if (mode == 'GET') { + var code = list + '[0]'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.shift()'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'REMOVE') { + return list + '.shift();\n'; + } + break; + case ('LAST'): + if (mode == 'GET') { + var code = list + '.slice(-1)[0]'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop()'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'REMOVE') { + return list + '.pop();\n'; + } + break; + case ('FROM_START'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT'); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.splice(' + at + ', 1)[0]'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return list + '.splice(' + at + ', 1);\n'; + } + break; + case ('FROM_END'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, true); + if (mode == 'GET') { + var code = list + '.slice(' + at + ')[0]'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.splice(' + at + ', 1)[0]'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return list + '.splice(' + at + ', 1);'; + } + break; + case ('RANDOM'): var functionName = Blockly.JavaScript.provideFunction_( - 'listsRemoveFromEnd', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list, x) {', - ' x = list.length - x;', - ' return list.splice(x, 1)[0];', - '}']); - code = functionName + '(' + list + ', ' + at + ')'; - if (mode == 'GET_REMOVE') { + 'listsGetRandomItem', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(list, remove) {', + ' var x = Math.floor(Math.random() * list.length);', + ' if (remove) {', + ' return list.splice(x, 1)[0];', + ' } else {', + ' return list[x];', + ' }', + '}']); + code = functionName + '(' + list + ', ' + (mode != 'GET') + ')'; + if (mode == 'GET' || mode == 'GET_REMOVE') { return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { return code + ';\n'; } - } - } else if (where == 'RANDOM') { - var functionName = Blockly.JavaScript.provideFunction_( - 'listsGetRandomItem', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list, remove) {', - ' var x = Math.floor(Math.random() * list.length);', - ' if (remove) {', - ' return list.splice(x, 1)[0];', - ' } else {', - ' return list[x];', - ' }', - '}']); - code = functionName + '(' + list + ', ' + (mode != 'GET') + ')'; - if (mode == 'GET' || mode == 'GET_REMOVE') { - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return code + ';\n'; - } + break; } throw 'Unhandled combination (lists_getIndex).'; }; @@ -187,8 +180,6 @@ Blockly.JavaScript['lists_setIndex'] = function(block) { Blockly.JavaScript.ORDER_MEMBER) || '[]'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.JavaScript.valueToCode(block, 'AT', - Blockly.JavaScript.ORDER_NONE) || '1'; var value = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. @@ -198,155 +189,206 @@ Blockly.JavaScript['lists_setIndex'] = function(block) { return ''; } var listVar = Blockly.JavaScript.variableDB_.getDistinctName( - 'tmp_list', Blockly.Variables.NAME_TYPE); + 'tmpList', Blockly.Variables.NAME_TYPE); var code = 'var ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.unshift(' + value + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { + switch (where) { + case ('FIRST'): + if (mode == 'SET') { + return list + '[0] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.unshift(' + value + ');\n'; + } + break; + case ('LAST'): + if (mode == 'SET') { + var code = cacheList(); + code += list + '[' + list + '.length - 1] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + return list + '.push(' + value + ');\n'; + } + break; + case ('FROM_START'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT'); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.splice(' + at + ', 0, ' + value + ');\n'; + } + break; + case ('FROM_END'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, false, + Blockly.JavaScript.ORDER_SUBTRACTION); var code = cacheList(); - code += list + '[' + list + '.length - 1] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - return list + '.push(' + value + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.splice(' + at + ', 0, ' + value + ');\n'; - } - } else if (where == 'FROM_END') { - var code = cacheList(); - if (mode == 'SET') { - code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.splice(' + list + '.length - ' + at + ', 0, ' + value + - ');\n'; - return code; - } - } else if (where == 'RANDOM') { - var code = cacheList(); - var xVar = Blockly.JavaScript.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += 'var ' + xVar + ' = Math.floor(Math.random() * ' + list + - '.length);\n'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.splice(' + xVar + ', 0, ' + value + ');\n'; - return code; - } + if (mode == 'SET') { + code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.splice(' + list + '.length - ' + at + ', 0, ' + value + + ');\n'; + return code; + } + break; + case ('RANDOM'): + var code = cacheList(); + var xVar = Blockly.JavaScript.variableDB_.getDistinctName( + 'tmpX', Blockly.Variables.NAME_TYPE); + code += 'var ' + xVar + ' = Math.floor(Math.random() * ' + list + + '.length);\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.splice(' + xVar + ', 0, ' + value + ');\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; +/** + * Returns an expression calculating the index into a list. + * @private + * @param {string} listName Name of the list, used to calculate length. + * @param {string} where The method of indexing, selected by dropdown in Blockly + * @param {string=} opt_at The optional offset when indexing from start/end. + * @return {string} Index expression. + */ +Blockly.JavaScript.lists.getIndex_ = function(listName, where, opt_at) { + if (where == 'FIRST') { + return '0'; + } else if (where == 'FROM_END') { + return listName + '.length - 1 - ' + opt_at; + } else if (where == 'LAST') { + return listName + '.length - 1'; + } else { + return opt_at; + } +}; + Blockly.JavaScript['lists_getSublist'] = function(block) { // Get sublist. var list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_MEMBER) || '[]'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.JavaScript.valueToCode(block, 'AT1', - Blockly.JavaScript.ORDER_NONE) || '1'; - var at2 = Blockly.JavaScript.valueToCode(block, 'AT2', - Blockly.JavaScript.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { - var code = list + '.concat()'; + var code = list + '.slice(0)'; + } else if (list.match(/^\w+$/) || + (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the list is a variable or doesn't require a call for length, don't + // generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at1 = list + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 0, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at2 = list + '.length - ' + at2; + break; + case 'LAST': + var at2 = list + '.length'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + code = list + '.slice(' + at1 + ', ' + at2 + ')'; } else { + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + var getIndex_ = Blockly.JavaScript.lists.getIndex_; + var wherePascalCase = {'FIRST': 'First', 'LAST': 'Last', + 'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'}; var functionName = Blockly.JavaScript.provideFunction_( - 'listsGetSublist', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list, where1, at1, where2, at2) {', - ' function getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = list.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = list.length - 1;', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return list.slice(at1, at2);', + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(sequence' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', at1' : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', at2' : '') + + ') {', + ' var start = ' + getIndex_('sequence', where1, 'at1') + ';', + ' var end = ' + getIndex_('sequence', where2, 'at2') + ' + 1;', + ' return sequence.slice(start, end);', '}']); - var code = functionName + '(' + list + ', \'' + - where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; + var code = functionName + '(' + list + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', ' + at1 : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', ' + at2 : '') + + ')'; } return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = Blockly.JavaScript.valueToCode( - block, 'LIST', + var list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '[]'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); var getCompareFunctionName = Blockly.JavaScript.provideFunction_( - 'listsGetSortCompare', - ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(type, direction) {', - ' var compareFuncs = {', - ' "NUMERIC": function(a, b) {', - ' return parseFloat(a) - parseFloat(b); },', - ' "TEXT": function(a, b) {', - ' return a.toString() > b.toString() ? 1 : -1; },', - ' "IGNORE_CASE": function(a, b) {', - ' return a.toString().toLowerCase() > ' + - 'b.toString().toLowerCase() ? 1 : -1; },', - ' };', - ' var compare = compareFuncs[type];', - ' return function(a, b) { return compare(a, b) * direction; }', - '}']); - return ['(' + listCode + ').slice().sort(' + + 'listsGetSortCompare', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(type, direction) {', + ' var compareFuncs = {', + ' "NUMERIC": function(a, b) {', + ' return parseFloat(a) - parseFloat(b); },', + ' "TEXT": function(a, b) {', + ' return a.toString() > b.toString() ? 1 : -1; },', + ' "IGNORE_CASE": function(a, b) {', + ' return a.toString().toLowerCase() > ' + + 'b.toString().toLowerCase() ? 1 : -1; },', + ' };', + ' var compare = compareFuncs[type];', + ' return function(a, b) { return compare(a, b) * direction; }', + '}']); + return [list + '.slice().sort(' + getCompareFunctionName + '("' + type + '", ' + direction + '))', Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. - var value_input = Blockly.JavaScript.valueToCode(block, 'INPUT', + var input = Blockly.JavaScript.valueToCode(block, 'INPUT', Blockly.JavaScript.ORDER_MEMBER); - var value_delim = Blockly.JavaScript.valueToCode(block, 'DELIM', + var delimiter = Blockly.JavaScript.valueToCode(block, 'DELIM', Blockly.JavaScript.ORDER_NONE) || '\'\''; var mode = block.getFieldValue('MODE'); if (mode == 'SPLIT') { - if (!value_input) { - value_input = '\'\''; + if (!input) { + input = '\'\''; } var functionName = 'split'; } else if (mode == 'JOIN') { - if (!value_input) { - value_input = '[]'; + if (!input) { + input = '[]'; } var functionName = 'join'; } else { throw 'Unknown mode: ' + mode; } - var code = value_input + '.' + functionName + '(' + value_delim + ')'; + var code = input + '.' + functionName + '(' + delimiter + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/math.js b/generators/javascript/math.js index 0a7d14621..a31b94392 100644 --- a/generators/javascript/math.js +++ b/generators/javascript/math.js @@ -168,25 +168,25 @@ Blockly.JavaScript['math_number_property'] = function(block) { // Prime is a special case as it is not a one-liner test. var functionName = Blockly.JavaScript.provideFunction_( 'mathIsPrime', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if (n == 2 || n == 3) {', - ' return true;', - ' }', - ' // False if n is NaN, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if (n == 2 || n == 3) {', + ' return true;', + ' }', + ' // False if n is NaN, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + ' n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', - ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', + ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); code = functionName + '(' + number_to_check + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } @@ -254,7 +254,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { // mathMean([null,null,1,3]) == 2.0. var functionName = Blockly.JavaScript.provideFunction_( 'mathMean', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(myList) {', ' return myList.reduce(function(x, y) {return x + y;}) / ' + 'myList.length;', @@ -267,7 +267,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { // mathMedian([null,null,1,3]) == 2.0. var functionName = Blockly.JavaScript.provideFunction_( 'mathMedian', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(myList) {', ' var localList = myList.filter(function (x) ' + '{return typeof x == \'number\';});', @@ -290,7 +290,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Blockly.JavaScript.provideFunction_( 'mathModes', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(values) {', ' var modes = [];', ' var counts = [];', @@ -326,7 +326,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { case 'STD_DEV': var functionName = Blockly.JavaScript.provideFunction_( 'mathStandardDeviation', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(numbers) {', ' var n = numbers.length;', ' if (!n) return null;', @@ -345,7 +345,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { case 'RANDOM': var functionName = Blockly.JavaScript.provideFunction_( 'mathRandomList', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(list) {', ' var x = Math.floor(Math.random() * list.length);', ' return list[x];', @@ -391,16 +391,16 @@ Blockly.JavaScript['math_random_int'] = function(block) { Blockly.JavaScript.ORDER_COMMA) || '0'; var functionName = Blockly.JavaScript.provideFunction_( 'mathRandomInt', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(a, b) {', - ' if (a > b) {', - ' // Swap a and b to ensure a is smaller.', - ' var c = a;', - ' a = b;', - ' b = c;', - ' }', - ' return Math.floor(Math.random() * (b - a + 1) + a);', - '}']); + ' if (a > b) {', + ' // Swap a and b to ensure a is smaller.', + ' var c = a;', + ' a = b;', + ' b = c;', + ' }', + ' return Math.floor(Math.random() * (b - a + 1) + a);', + '}']); var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/text.js b/generators/javascript/text.js index 86befb9ef..9ecfdc387 100644 --- a/generators/javascript/text.js +++ b/generators/javascript/text.js @@ -37,29 +37,29 @@ Blockly.JavaScript['text'] = function(block) { Blockly.JavaScript['text_join'] = function(block) { // Create a string made up of any number of elements of any type. - var code; - if (block.itemCount_ == 0) { - return ['\'\'', Blockly.JavaScript.ORDER_ATOMIC]; - } else if (block.itemCount_ == 1) { - var argument0 = Blockly.JavaScript.valueToCode(block, 'ADD0', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - code = 'String(' + argument0 + ')'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (block.itemCount_ == 2) { - var argument0 = Blockly.JavaScript.valueToCode(block, 'ADD0', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - var argument1 = Blockly.JavaScript.valueToCode(block, 'ADD1', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - code = 'String(' + argument0 + ') + String(' + argument1 + ')'; - return [code, Blockly.JavaScript.ORDER_ADDITION]; - } else { - code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.JavaScript.valueToCode(block, 'ADD' + n, - Blockly.JavaScript.ORDER_COMMA) || '\'\''; - } - code = '[' + code.join(',') + '].join(\'\')'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + switch (block.itemCount_) { + case 0: + return ['\'\'', Blockly.JavaScript.ORDER_ATOMIC]; + case 1: + var element = Blockly.JavaScript.valueToCode(block, 'ADD0', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var code = 'String(' + element + ')'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + case 2: + var element0 = Blockly.JavaScript.valueToCode(block, 'ADD0', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var element1 = Blockly.JavaScript.valueToCode(block, 'ADD1', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var code = 'String(' + element0 + ') + String(' + element1 + ')'; + return [code, Blockly.JavaScript.ORDER_ADDITION]; + default: + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.JavaScript.valueToCode(block, 'ADD' + i, + Blockly.JavaScript.ORDER_COMMA) || '\'\''; + } + var code = '[' + elements.join(',') + '].join(\'\')'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } }; @@ -67,45 +67,49 @@ Blockly.JavaScript['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', + var value = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; - return varName + ' = String(' + varName + ') + String(' + argument0 + ');\n'; + return varName + ' = String(' + varName + ') + String(' + value + ');\n'; }; Blockly.JavaScript['text_length'] = function(block) { // String or array length. - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\''; - return [argument0 + '.length', Blockly.JavaScript.ORDER_MEMBER]; + return [text + '.length', Blockly.JavaScript.ORDER_MEMBER]; }; Blockly.JavaScript['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - return ['!' + argument0 + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; + return ['!' + text + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; }; Blockly.JavaScript['text_indexOf'] = function(block) { // Search the text for a substring. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.JavaScript.valueToCode(block, 'FIND', + var substring = Blockly.JavaScript.valueToCode(block, 'FIND', Blockly.JavaScript.ORDER_NONE) || '\'\''; - var argument1 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.JavaScript.ORDER_ADDITION]; + var code = text + '.' + operator + '(' + substring + ')'; + // Adjust index if using one-based indices. + if (Blockly.JavaScript.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.JavaScript.ORDER_ADDITION]; + } + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.JavaScript.valueToCode(block, 'AT', - Blockly.JavaScript.ORDER_UNARY_NEGATION) || '1'; + var textOrder = (where == 'RANDOM') ? Blockly.JavaScript.ORDER_NONE : + Blockly.JavaScript.ORDER_MEMBER; var text = Blockly.JavaScript.valueToCode(block, 'VALUE', - Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + textOrder) || '\'\''; switch (where) { case 'FIRST': var code = text + '.charAt(0)'; @@ -114,70 +118,117 @@ Blockly.JavaScript['text_charAt'] = function(block) { var code = text + '.slice(-1)'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; case 'FROM_START': - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } + var at = Blockly.JavaScript.getAdjusted(block, 'AT'); + // Adjust index if using one-based indices. var code = text + '.charAt(' + at + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; case 'FROM_END': - var code = text + '.slice(-' + at + ').charAt(0)'; + var at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, true); + var code = text + '.slice(' + at + ').charAt(0)'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; case 'RANDOM': var functionName = Blockly.JavaScript.provideFunction_( 'textRandomLetter', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(text) {', - ' var x = Math.floor(Math.random() * text.length);', - ' return text[x];', - '}']); - code = functionName + '(' + text + ')'; + ' var x = Math.floor(Math.random() * text.length);', + ' return text[x];', + '}']); + var code = functionName + '(' + text + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } throw 'Unhandled option (text_charAt).'; }; +/** + * Returns an expression calculating the index into a string. + * @private + * @param {string} stringName Name of the string, used to calculate length. + * @param {string} where The method of indexing, selected by dropdown in Blockly + * @param {string=} opt_at The optional offset when indexing from start/end. + * @return {string} Index expression. + */ +Blockly.JavaScript.text.getIndex_ = function(stringName, where, opt_at) { + if (where == 'FIRST') { + return '0'; + } else if (where == 'FROM_END') { + return stringName + '.length - 1 - ' + opt_at; + } else if (where == 'LAST') { + return stringName + '.length - 1'; + } else { + return opt_at; + } +}; + Blockly.JavaScript['text_getSubstring'] = function(block) { // Get substring. var text = Blockly.JavaScript.valueToCode(block, 'STRING', - Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.JavaScript.valueToCode(block, 'AT1', - Blockly.JavaScript.ORDER_NONE) || '1'; - var at2 = Blockly.JavaScript.valueToCode(block, 'AT2', - Blockly.JavaScript.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = text; + } else if (text.match(/^'?\w+'?$/) || + (where1 != 'FROM_END' && where1 != 'LAST' && + where2 != 'FROM_END' && where2 != 'LAST')) { + // If the text is a variable or literal or doesn't require a call for + // length, don't generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at1 = text + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 0, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at2 = text + '.length - ' + at2; + break; + case 'LAST': + var at2 = text + '.length'; + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + code = text + '.slice(' + at1 + ', ' + at2 + ')'; } else { + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + var getIndex_ = Blockly.JavaScript.text.getIndex_; + var wherePascalCase = {'FIRST': 'First', 'LAST': 'Last', + 'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'}; var functionName = Blockly.JavaScript.provideFunction_( - 'textGetSubstring', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(text, where1, at1, where2, at2) {', - ' function getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = text.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = text.length - 1;', - ' } else {', - ' throw \'Unhandled option (text_getSubstring).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return text.slice(at1, at2);', + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(sequence' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', at1' : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', at2' : '') + + ') {', + ' var start = ' + getIndex_('sequence', where1, 'at1') + ';', + ' var end = ' + getIndex_('sequence', where2, 'at2') + ' + 1;', + ' return sequence.slice(start, end);', '}']); - var code = functionName + '(' + text + ', \'' + - where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; + var code = functionName + '(' + text + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', ' + at1 : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', ' + at2 : '') + + ')'; } return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; @@ -190,25 +241,24 @@ Blockly.JavaScript['text_changeCase'] = function(block) { 'TITLECASE': null }; var operator = OPERATORS[block.getFieldValue('CASE')]; - var code; + var textOrder = operator ? Blockly.JavaScript.ORDER_MEMBER : + Blockly.JavaScript.ORDER_NONE; + var text = Blockly.JavaScript.valueToCode(block, 'TEXT', + textOrder) || '\'\''; if (operator) { // Upper and lower case are functions built into JavaScript. - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', - Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - code = argument0 + operator; + var code = text + operator; } else { // Title case is not a native JavaScript function. Define one. var functionName = Blockly.JavaScript.provideFunction_( 'textToTitleCase', - [ 'function ' + - Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(str) {', - ' return str.replace(/\\S+/g,', - ' function(txt) {return txt[0].toUpperCase() + ' + - 'txt.substring(1).toLowerCase();});', - '}']); - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - code = functionName + '(' + argument0 + ')'; + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(str) {', + ' return str.replace(/\\S+/g,', + ' function(txt) {return txt[0].toUpperCase() + ' + + 'txt.substring(1).toLowerCase();});', + '}']); + var code = functionName + '(' + text + ')'; } return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; @@ -221,16 +271,16 @@ Blockly.JavaScript['text_trim'] = function(block) { 'BOTH': '.trim()' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', + var text = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - return [argument0 + operator, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + return [text + operator, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', + var msg = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; - return 'window.alert(' + argument0 + ');\n'; + return 'window.alert(' + msg + ');\n'; }; Blockly.JavaScript['text_prompt_ext'] = function(block) { diff --git a/generators/lua.js b/generators/lua.js index 6dbecb666..225489641 100644 --- a/generators/lua.js +++ b/generators/lua.js @@ -77,7 +77,7 @@ Blockly.Lua.ORDER_ATOMIC = 0; // literals // The next level was not explicit in documentation and inferred by Ellen. Blockly.Lua.ORDER_HIGH = 1; // Function calls, tables[] Blockly.Lua.ORDER_EXPONENTIATION = 2; // ^ -Blockly.Lua.ORDER_UNARY = 3; // not # - () +Blockly.Lua.ORDER_UNARY = 3; // not # - ~ Blockly.Lua.ORDER_MULTIPLICATIVE = 4; // * / % Blockly.Lua.ORDER_ADDITIVE = 5; // + - Blockly.Lua.ORDER_CONCATENATION = 6; // .. @@ -86,6 +86,12 @@ Blockly.Lua.ORDER_AND = 8; // and Blockly.Lua.ORDER_OR = 9; // or Blockly.Lua.ORDER_NONE = 99; +/** + * Lua is not supporting zero-indexing since the language itself is one-indexed, + * so there is not flag for ONE_BASED_INDEXING to indicate which indexing is + * used for lists and text. + */ + /** * Initialise the database of variable names. * @param {!Blockly.Workspace} workspace Workspace to generate code from. @@ -170,9 +176,9 @@ Blockly.Lua.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { comment = Blockly.Lua.allNestedComments(childBlock); if (comment) { diff --git a/generators/lua/lists.js b/generators/lua/lists.js index 8c89a1bd3..5b7711dbb 100644 --- a/generators/lua/lists.js +++ b/generators/lua/lists.js @@ -31,18 +31,17 @@ goog.require('Blockly.Lua'); Blockly.Lua['lists_create_empty'] = function(block) { // Create an empty list. - // List literals must be parenthesized before indexing into. - return ['({})', Blockly.Lua.ORDER_ATOMIC]; + return ['{}', Blockly.Lua.ORDER_ATOMIC]; }; Blockly.Lua['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Lua.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Lua.valueToCode(block, 'ADD' + i, Blockly.Lua.ORDER_NONE) || 'None'; } - code = '({' + code.join(', ') + '})'; + var code = '{' + elements.join(', ') + '}'; return [code, Blockly.Lua.ORDER_ATOMIC]; }; @@ -57,38 +56,37 @@ Blockly.Lua['lists_repeat'] = function(block) { ' end', ' return t', 'end']); - var argument0 = Blockly.Lua.valueToCode(block, 'ITEM', + var element = Blockly.Lua.valueToCode(block, 'ITEM', Blockly.Lua.ORDER_NONE) || 'None'; - var argument1 = Blockly.Lua.valueToCode(block, 'NUM', + var repeatCount = Blockly.Lua.valueToCode(block, 'NUM', Blockly.Lua.ORDER_NONE) || '0'; - var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; + var code = functionName + '(' + element + ', ' + repeatCount + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; Blockly.Lua['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '({})'; - return ['#' + argument0, Blockly.Lua.ORDER_HIGH]; + var list = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '{}'; + return ['#' + list, Blockly.Lua.ORDER_UNARY]; }; Blockly.Lua['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '({})'; - var code = '#' + argument0 + ' == 0'; + var list = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '{}'; + var code = '#' + list + ' == 0'; return [code, Blockly.Lua.ORDER_RELATIONAL]; }; Blockly.Lua['lists_indexOf'] = function(block) { // Find an item in the list. - var argument0 = Blockly.Lua.valueToCode(block, 'FIND', + var item = Blockly.Lua.valueToCode(block, 'FIND', Blockly.Lua.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_NONE) || '({})'; - var functionName; + var list = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_NONE) || '{}'; if (block.getFieldValue('END') == 'FIRST') { - functionName = Blockly.Lua.provideFunction_( + var functionName = Blockly.Lua.provideFunction_( 'first_index', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, elem)', ' for k, v in ipairs(t) do', @@ -99,7 +97,7 @@ Blockly.Lua['lists_indexOf'] = function(block) { ' return 0', 'end']); } else { - functionName = Blockly.Lua.provideFunction_( + var functionName = Blockly.Lua.provideFunction_( 'last_index', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, elem)', ' for i = #t, 1, -1 do', @@ -110,64 +108,40 @@ Blockly.Lua['lists_indexOf'] = function(block) { ' return 0', 'end']); } - var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + var code = functionName + '(' + list + ', ' + item + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; /** * Returns an expression calculating the index into a list. * @private - * @param {string} listname Name of the list, used to calculate length. + * @param {string} listName Name of the list, used to calculate length. * @param {string} where The method of indexing, selected by dropdown in Blockly * @param {string=} opt_at The optional offset when indexing from start/end. * @return {string} Index expression. */ -Blockly.Lua.lists.getIndex_ = function(listname, where, opt_at) { +Blockly.Lua.lists.getIndex_ = function(listName, where, opt_at) { if (where == 'FIRST') { return '1'; } else if (where == 'FROM_END') { - return '#' + listname + ' + 1 - ' + opt_at; + return '#' + listName + ' + 1 - ' + opt_at; } else if (where == 'LAST') { - return '#' + listname; + return '#' + listName; } else if (where == 'RANDOM') { - return 'math.random(#' + listname + ')'; + return 'math.random(#' + listName + ')'; } else { return opt_at; } }; -/** - * Counter for generating unique symbols. - * @private - * @type {number} - */ -Blockly.Lua.lists.gensym_counter_ = 0; - -/** - * Generate a unique symbol. - * @private - * @return {string} unique symbol, eg 'G123' - */ -Blockly.Lua.lists.gensym_ = function() { - return 'G' + Blockly.Lua.lists.gensym_counter_++; -}; - Blockly.Lua['lists_getIndex'] = function(block) { // Get element at index. // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Lua.valueToCode(block, 'AT', - Blockly.Lua.ORDER_ADDITIVE) || '1'; - if (mode == 'GET') { - var order = Blockly.Lua.ORDER_HIGH; - } else { - // List will be an argument in a function call. - var order = Blockly.Lua.ORDER_NONE; - } - var list = Blockly.Lua.valueToCode(block, 'VALUE', order) || '({})'; + var list = Blockly.Lua.valueToCode(block, 'VALUE', Blockly.Lua.ORDER_HIGH) || + '{}'; var getIndex_ = Blockly.Lua.lists.getIndex_; - var gensym_ = Blockly.Lua.lists.gensym_; // If `list` would be evaluated more than once (which is the case for LAST, // FROM_END, and RANDOM) and is non-trivial, make sure to access it only once. @@ -176,46 +150,59 @@ Blockly.Lua['lists_getIndex'] = function(block) { // `list` is an expression, so we may not evaluate it more than once. if (mode == 'REMOVE') { // We can use multiple statements. + var atOrder = (where == 'FROM_END') ? Blockly.Lua.ORDER_ADDITIVE : + Blockly.Lua.ORDER_NONE; + var at = Blockly.Lua.valueToCode(block, 'AT', atOrder) || '1'; var listVar = Blockly.Lua.variableDB_.getDistinctName( 'tmp_list', Blockly.Variables.NAME_TYPE); + at = getIndex_(listVar, where, at); var code = listVar + ' = ' + list + '\n' + - 'table.remove(' + listVar + ', ' + getIndex_(listVar, where, at) + - ')\n'; + 'table.remove(' + listVar + ', ' + at + ')\n'; return code; } else { // We need to create a procedure to avoid reevaluating values. + var at = Blockly.Lua.valueToCode(block, 'AT', Blockly.Lua.ORDER_NONE) || + '1'; if (mode == 'GET') { - // Note that getIndex_() ignores `at` when `where` == 'LAST' or - // 'RANDOM', so we only need one procedure for each of those 'where' - // values. The value for 'FROM_END' depends on `at`, so we will - // generate a unique procedure (name) each time. var functionName = Blockly.Lua.provideFunction_( - 'list_get_' + where.toLowerCase() + - (where == 'FROM_END' ? '_' + gensym_() : ''), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' return t[' + getIndex_('t', where, at) + ']', + 'list_get_' + where.toLowerCase(), + ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where == 'FROM_END' || where == 'FROM_START') ? + ', at)' : ')'), + ' return t[' + getIndex_('t', where, 'at') + ']', 'end']); - } else { // mode == 'GET_REMOVE' - // We need to create a procedure. + } else { // mode == 'GET_REMOVE' var functionName = Blockly.Lua.provideFunction_( - 'list_remove_' + where.toLowerCase() + - (where == 'FROM_END' ? '_' + gensym_() : ''), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' return table.remove(t, ' + getIndex_('t', where, at) + ')', + 'list_remove_' + where.toLowerCase(), + ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where == 'FROM_END' || where == 'FROM_START') ? + ', at)' : ')'), + ' return table.remove(t, ' + getIndex_('t', where, 'at') + ')', 'end']); } - var code = functionName + '(' + list + ')'; + var code = functionName + '(' + list + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where == 'FROM_END' || where == 'FROM_START') ? ', ' + at : '') + + ')'; return [code, Blockly.Lua.ORDER_HIGH]; } } else { // Either `list` is a simple variable, or we only need to refer to `list` // once. + var atOrder = (mode == 'GET' && where == 'FROM_END') ? + Blockly.Lua.ORDER_ADDITIVE : Blockly.Lua.ORDER_NONE; + var at = Blockly.Lua.valueToCode(block, 'AT', atOrder) || '1'; + at = getIndex_(list, where, at); if (mode == 'GET') { - var code = list + '[' + getIndex_(list, where, at) + ']'; + var code = list + '[' + at + ']'; return [code, Blockly.Lua.ORDER_HIGH]; } else { - var code = 'table.remove(' + list + ', ' + getIndex_(list, where, at) + - ')'; + var code = 'table.remove(' + list + ', ' + at + ')'; if (mode == 'GET_REMOVE') { return [code, Blockly.Lua.ORDER_HIGH]; } else { // `mode` == 'REMOVE' @@ -229,7 +216,7 @@ Blockly.Lua['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. var list = Blockly.Lua.valueToCode(block, 'LIST', - Blockly.Lua.ORDER_HIGH) || '({})'; + Blockly.Lua.ORDER_HIGH) || '{}'; var mode = block.getFieldValue('MODE') || 'SET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; var at = Blockly.Lua.valueToCode(block, 'AT', @@ -238,97 +225,71 @@ Blockly.Lua['lists_setIndex'] = function(block) { Blockly.Lua.ORDER_NONE) || 'None'; var getIndex_ = Blockly.Lua.lists.getIndex_; + var code = ''; // If `list` would be evaluated more than once (which is the case for LAST, // FROM_END, and RANDOM) and is non-trivial, make sure to access it only once. if ((where == 'LAST' || where == 'FROM_END' || where == 'RANDOM') && !list.match(/^\w+$/)) { // `list` is an expression, so we may not evaluate it more than once. - if (where == 'RANDOM' || where == 'LAST') { - // In these cases, `at` is implicit. getIndex_() ignores its value. - if (mode == 'SET') { - var functionName = Blockly.Lua.provideFunction_( - 'list_set_' + where.toLowerCase(), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, val)', - ' t[' + getIndex_('t', where, at) + '] = val', - 'end']); - } else { // `mode` == 'INSERT' - var functionName = Blockly.Lua.provideFunction_( - 'list_insert_' + where.toLowerCase(), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, val)', - ' table.insert(t, ' + - // LAST is a special case, because we want to insert - // *after* not *before*, the existing last element. - getIndex_('t', where, at) + (where == 'LAST' ? ' + 1' : '') + - ', val)', - 'end']); - } - var code = functionName + '(' + list + ', ' + value + ')\n'; - return code; - } else { // `where` = 'FROM_END' - if (mode == 'SET') { - var functionName = Blockly.Lua.provideFunction_( - 'list_set_from_end', - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + - '(t, index, val)', - ' t[#t + 1 - index] = val', - 'end']); - } else { // `mode` == 'INSERT' - var functionName = Blockly.Lua.provideFunction_( - 'list_insert_from_end', - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + - '(t, index, val)', - ' table.insert(t, #t + 1 - index, val)', - 'end']); - } - var code = functionName + '(' + list + ', ' + at + ', ' + value + ')\n'; - return code; - } - } else { - // It's okay to have multiple references to the list. - if (mode == 'SET') { - var code = list + '[' + getIndex_(list, where, at) + '] = ' + value; - } else { // `mode` == 'INSERT' - // LAST is a special case, because we want to insert - // *after* not *before*, the existing last element. - var code = 'table.insert(' + list + ', ' + - (getIndex_(list, where, at) + (where == 'LAST' ? ' + 1' : '')) + - ', ' + value + ')'; - } - return code + '\n'; + // We can use multiple statements. + var listVar = Blockly.Lua.variableDB_.getDistinctName( + 'tmp_list', Blockly.Variables.NAME_TYPE); + code = listVar + ' = ' + list + '\n'; + list = listVar; } + if (mode == 'SET') { + code += list + '[' + getIndex_(list, where, at) + '] = ' + value; + } else { // `mode` == 'INSERT' + // LAST is a special case, because we want to insert + // *after* not *before*, the existing last element. + code += 'table.insert(' + list + ', ' + + (getIndex_(list, where, at) + (where == 'LAST' ? ' + 1' : '')) + + ', ' + value + ')'; + } + return code + '\n'; }; Blockly.Lua['lists_getSublist'] = function(block) { // Get sublist. var list = Blockly.Lua.valueToCode(block, 'LIST', - Blockly.Lua.ORDER_HIGH) || '({})'; + Blockly.Lua.ORDER_NONE) || '{}'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); var at1 = Blockly.Lua.valueToCode(block, 'AT1', - Blockly.Lua.ORDER_ADDITIVE) || '1'; + Blockly.Lua.ORDER_NONE) || '1'; var at2 = Blockly.Lua.valueToCode(block, 'AT2', - Blockly.Lua.ORDER_ADDITIVE) || '1'; + Blockly.Lua.ORDER_NONE) || '1'; var getIndex_ = Blockly.Lua.lists.getIndex_; var functionName = Blockly.Lua.provideFunction_( - 'list_sublist_' + Blockly.Lua.lists.gensym_(), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(source)', + 'list_sublist_' + where1.toLowerCase() + '_' + where2.toLowerCase(), + ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(source' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', at1' : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', at2' : '') + + ')', ' local t = {}', - ' local start = ' + getIndex_('source', where1, at1), - ' local finish = ' + getIndex_('source', where2, at2), + ' local start = ' + getIndex_('source', where1, 'at1'), + ' local finish = ' + getIndex_('source', where2, 'at2'), ' for i = start, finish do', ' table.insert(t, source[i])', ' end', ' return t', 'end']); - var code = functionName + '(' + list + ')'; + var code = functionName + '(' + list + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', ' + at1 : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', ' + at2 : '') + + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; Blockly.Lua['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = Blockly.Lua.valueToCode( - block, 'LIST', Blockly.Lua.ORDER_HIGH) || '({})'; + var list = Blockly.Lua.valueToCode( + block, 'LIST', Blockly.Lua.ORDER_NONE) || '{}'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); @@ -357,26 +318,26 @@ Blockly.Lua['lists_sort'] = function(block) { 'end']); var code = functionName + - '(' + listCode + ',"' + type + '", ' + direction + ')'; + '(' + list + ',"' + type + '", ' + direction + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; Blockly.Lua['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. - var value_input = Blockly.Lua.valueToCode(block, 'INPUT', + var input = Blockly.Lua.valueToCode(block, 'INPUT', Blockly.Lua.ORDER_NONE); - var value_delim = Blockly.Lua.valueToCode(block, 'DELIM', + var delimiter = Blockly.Lua.valueToCode(block, 'DELIM', Blockly.Lua.ORDER_NONE) || '\'\''; var mode = block.getFieldValue('MODE'); var functionName; if (mode == 'SPLIT') { - if (!value_input) { - value_input = '\'\''; + if (!input) { + input = '\'\''; } functionName = Blockly.Lua.provideFunction_( 'list_string_split', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + - '(input, delim)', + '(input, delim)', ' local t = {}', ' local pos = 1', ' while true do', @@ -392,13 +353,13 @@ Blockly.Lua['lists_split'] = function(block) { ' return t', 'end']); } else if (mode == 'JOIN') { - if (!value_input) { - value_input = '({})'; + if (!input) { + input = '{}'; } functionName = 'table.concat'; } else { throw 'Unknown mode: ' + mode; } - var code = functionName + '(' + value_input + ', ' + value_delim + ')'; + var code = functionName + '(' + input + ', ' + delimiter + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; diff --git a/generators/lua/procedures.js b/generators/lua/procedures.js index 9bdcc416b..b6d6cdda1 100644 --- a/generators/lua/procedures.js +++ b/generators/lua/procedures.js @@ -51,8 +51,8 @@ Blockly.Lua['procedures_defreturn'] = function(block) { branch = ''; } var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Lua.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Lua.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = 'function ' + funcName + '(' + args.join(', ') + ')\n' + @@ -73,8 +73,8 @@ Blockly.Lua['procedures_callreturn'] = function(block) { var funcName = Blockly.Lua.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Lua.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Lua.valueToCode(block, 'ARG' + i, Blockly.Lua.ORDER_NONE) || 'nil'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -86,8 +86,8 @@ Blockly.Lua['procedures_callnoreturn'] = function(block) { var funcName = Blockly.Lua.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Lua.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Lua.valueToCode(block, 'ARG' + i, Blockly.Lua.ORDER_NONE) || 'nil'; } var code = funcName + '(' + args.join(', ') + ')\n'; diff --git a/generators/lua/text.js b/generators/lua/text.js index 8a6a02702..74efba4a2 100644 --- a/generators/lua/text.js +++ b/generators/lua/text.js @@ -40,24 +40,24 @@ Blockly.Lua['text_join'] = function(block) { if (block.itemCount_ == 0) { return ['\'\'', Blockly.Lua.ORDER_ATOMIC]; } else if (block.itemCount_ == 1) { - var argument0 = Blockly.Lua.valueToCode(block, 'ADD0', + var element = Blockly.Lua.valueToCode(block, 'ADD0', Blockly.Lua.ORDER_NONE) || '\'\''; - var code = argument0; + var code = 'tostring(' + element + ')'; return [code, Blockly.Lua.ORDER_HIGH]; } else if (block.itemCount_ == 2) { - var argument0 = Blockly.Lua.valueToCode(block, 'ADD0', - Blockly.Lua.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Lua.valueToCode(block, 'ADD1', - Blockly.Lua.ORDER_NONE) || '\'\''; - var code = argument0 + ' .. ' + argument1; - return [code, Blockly.Lua.ORDER_UNARY]; + var element0 = Blockly.Lua.valueToCode(block, 'ADD0', + Blockly.Lua.ORDER_CONCATENATION) || '\'\''; + var element1 = Blockly.Lua.valueToCode(block, 'ADD1', + Blockly.Lua.ORDER_CONCATENATION) || '\'\''; + var code = element0 + ' .. ' + element1; + return [code, Blockly.Lua.ORDER_CONCATENATION]; } else { - var code = []; - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Lua.valueToCode(block, 'ADD' + n, + var elements = []; + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Lua.valueToCode(block, 'ADD' + i, Blockly.Lua.ORDER_NONE) || '\'\''; } - code = 'table.concat({' + code.join(', ') + '})'; + var code = 'table.concat({' + elements.join(', ') + '})'; return [code, Blockly.Lua.ORDER_HIGH]; } }; @@ -66,32 +66,30 @@ Blockly.Lua['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.Lua.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.Lua.valueToCode(block, 'TEXT', - Blockly.Lua.ORDER_NONE) || '\'\''; - return varName + ' = ' + varName + ' .. ' + argument0 + '\n'; + var value = Blockly.Lua.valueToCode(block, 'TEXT', + Blockly.Lua.ORDER_CONCATENATION) || '\'\''; + return varName + ' = ' + varName + ' .. ' + value + '\n'; }; Blockly.Lua['text_length'] = function(block) { // String or array length. - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '\'\''; - return ['#' + argument0, Blockly.Lua.ORDER_HIGH]; + var text = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '\'\''; + return ['#' + text, Blockly.Lua.ORDER_UNARY]; }; Blockly.Lua['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '\'\''; - return ['#' + argument0 + ' == 0', Blockly.Lua.ORDER_RELATIONAL]; + var text = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '\'\''; + return ['#' + text + ' == 0', Blockly.Lua.ORDER_RELATIONAL]; }; Blockly.Lua['text_indexOf'] = function(block) { // Search the text for a substring. - var operator = block.getFieldValue('END') == 'FIRST' ? - 'indexOf' : 'lastIndexOf'; - var substr = Blockly.Lua.valueToCode(block, 'FIND', + var substring = Blockly.Lua.valueToCode(block, 'FIND', Blockly.Lua.ORDER_NONE) || '\'\''; - var str = Blockly.Lua.valueToCode(block, 'VALUE', + var text = Blockly.Lua.valueToCode(block, 'VALUE', Blockly.Lua.ORDER_NONE) || '\'\''; if (block.getFieldValue('END') == 'FIRST') { var functionName = Blockly.Lua.provideFunction_( @@ -118,7 +116,7 @@ Blockly.Lua['text_indexOf'] = function(block) { ' return 0', 'end']); } - var code = functionName + '(' + str + ', ' + substr + ')'; + var code = functionName + '(' + text + ', ' + substring + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; @@ -126,8 +124,9 @@ Blockly.Lua['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Lua.valueToCode(block, 'AT', - Blockly.Lua.ORDER_UNARY) || '1'; + var atOrder = (where == 'FROM_END') ? Blockly.Lua.ORDER_UNARY : + Blockly.Lua.ORDER_NONE; + var at = Blockly.Lua.valueToCode(block, 'AT', atOrder) || '1'; var text = Blockly.Lua.valueToCode(block, 'VALUE', Blockly.Lua.ORDER_NONE) || '\'\''; var code; @@ -176,8 +175,9 @@ Blockly.Lua['text_getSubstring'] = function(block) { // Get start index. var where1 = block.getFieldValue('WHERE1'); - var at1 = Blockly.Lua.valueToCode(block, 'AT1', - Blockly.Lua.ORDER_UNARY) || '1'; + var at1Order = (where1 == 'FROM_END') ? Blockly.Lua.ORDER_UNARY : + Blockly.Lua.ORDER_NONE; + var at1 = Blockly.Lua.valueToCode(block, 'AT1', at1Order) || '1'; if (where1 == 'FIRST') { var start = 1; } else if (where1 == 'FROM_START') { @@ -190,8 +190,9 @@ Blockly.Lua['text_getSubstring'] = function(block) { // Get end index. var where2 = block.getFieldValue('WHERE2'); - var at2 = Blockly.Lua.valueToCode(block, 'AT2', - Blockly.Lua.ORDER_UNARY) || '1'; + var at2Order = (where2 == 'FROM_END') ? Blockly.Lua.ORDER_UNARY : + Blockly.Lua.ORDER_NONE; + var at2 = Blockly.Lua.valueToCode(block, 'AT2', at2Order) || '1'; if (where2 == 'LAST') { var end = -1; } else if (where2 == 'FROM_START') { @@ -208,7 +209,7 @@ Blockly.Lua['text_getSubstring'] = function(block) { Blockly.Lua['text_changeCase'] = function(block) { // Change capitalization. var operator = block.getFieldValue('CASE'); - var argument0 = Blockly.Lua.valueToCode(block, 'TEXT', + var text = Blockly.Lua.valueToCode(block, 'TEXT', Blockly.Lua.ORDER_NONE) || '\'\''; if (operator == 'UPPERCASE') { var functionName = 'string.upper'; @@ -238,7 +239,7 @@ Blockly.Lua['text_changeCase'] = function(block) { ' return table.concat(buf)', 'end']); } - var code = functionName + '(' + argument0 + ')'; + var code = functionName + '(' + text + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; @@ -258,9 +259,9 @@ Blockly.Lua['text_trim'] = function(block) { Blockly.Lua['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.Lua.valueToCode(block, 'TEXT', + var msg = Blockly.Lua.valueToCode(block, 'TEXT', Blockly.Lua.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ')\n'; + return 'print(' + msg + ')\n'; }; Blockly.Lua['text_prompt_ext'] = function(block) { diff --git a/generators/php.js b/generators/php.js index b836c7bc1..0543a439a 100644 --- a/generators/php.js +++ b/generators/php.js @@ -44,44 +44,98 @@ Blockly.PHP = new Blockly.Generator('PHP'); */ Blockly.PHP.addReservedWords( // http://php.net/manual/en/reserved.keywords.php - '__halt_compiler,abstract,and,array,as,break,callable,case,catch,class,clone,const,continue,declare,default,die,do,echo,else,elseif,empty,enddeclare,endfor,endforeach,endif,endswitch,endwhile,eval,exit,extends,final,for,foreach,function,global,goto,if,implements,include,include_once,instanceof,insteadof,interface,isset,list,namespace,new,or,print,private,protected,public,require,require_once,return,static,switch,throw,trait,try,unset,use,var,while,xor,' + + '__halt_compiler,abstract,and,array,as,break,callable,case,catch,class,' + + 'clone,const,continue,declare,default,die,do,echo,else,elseif,empty,' + + 'enddeclare,endfor,endforeach,endif,endswitch,endwhile,eval,exit,extends,' + + 'final,for,foreach,function,global,goto,if,implements,include,' + + 'include_once,instanceof,insteadof,interface,isset,list,namespace,new,or,' + + 'print,private,protected,public,require,require_once,return,static,' + + 'switch,throw,trait,try,unset,use,var,while,xor,' + // http://php.net/manual/en/reserved.constants.php - 'PHP_VERSION,PHP_MAJOR_VERSION,PHP_MINOR_VERSION,PHP_RELEASE_VERSION,PHP_VERSION_ID,PHP_EXTRA_VERSION,PHP_ZTS,PHP_DEBUG,PHP_MAXPATHLEN,PHP_OS,PHP_SAPI,PHP_EOL,PHP_INT_MAX,PHP_INT_SIZE,DEFAULT_INCLUDE_PATH,PEAR_INSTALL_DIR,PEAR_EXTENSION_DIR,PHP_EXTENSION_DIR,PHP_PREFIX,PHP_BINDIR,PHP_BINARY,PHP_MANDIR,PHP_LIBDIR,PHP_DATADIR,PHP_SYSCONFDIR,PHP_LOCALSTATEDIR,PHP_CONFIG_FILE_PATH,PHP_CONFIG_FILE_SCAN_DIR,PHP_SHLIB_SUFFIX,E_ERROR,E_WARNING,E_PARSE,E_NOTICE,E_CORE_ERROR,E_CORE_WARNING,E_COMPILE_ERROR,E_COMPILE_WARNING,E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE,E_DEPRECATED,E_USER_DEPRECATED,E_ALL,E_STRICT,__COMPILER_HALT_OFFSET__,TRUE,FALSE,NULL,__CLASS__,__DIR__,__FILE__,__FUNCTION__,__LINE__,__METHOD__,__NAMESPACE__,__TRAIT__'); + 'PHP_VERSION,PHP_MAJOR_VERSION,PHP_MINOR_VERSION,PHP_RELEASE_VERSION,' + + 'PHP_VERSION_ID,PHP_EXTRA_VERSION,PHP_ZTS,PHP_DEBUG,PHP_MAXPATHLEN,' + + 'PHP_OS,PHP_SAPI,PHP_EOL,PHP_INT_MAX,PHP_INT_SIZE,DEFAULT_INCLUDE_PATH,' + + 'PEAR_INSTALL_DIR,PEAR_EXTENSION_DIR,PHP_EXTENSION_DIR,PHP_PREFIX,' + + 'PHP_BINDIR,PHP_BINARY,PHP_MANDIR,PHP_LIBDIR,PHP_DATADIR,PHP_SYSCONFDIR,' + + 'PHP_LOCALSTATEDIR,PHP_CONFIG_FILE_PATH,PHP_CONFIG_FILE_SCAN_DIR,' + + 'PHP_SHLIB_SUFFIX,E_ERROR,E_WARNING,E_PARSE,E_NOTICE,E_CORE_ERROR,' + + 'E_CORE_WARNING,E_COMPILE_ERROR,E_COMPILE_WARNING,E_USER_ERROR,' + + 'E_USER_WARNING,E_USER_NOTICE,E_DEPRECATED,E_USER_DEPRECATED,E_ALL,' + + 'E_STRICT,__COMPILER_HALT_OFFSET__,TRUE,FALSE,NULL,__CLASS__,__DIR__,' + + '__FILE__,__FUNCTION__,__LINE__,__METHOD__,__NAMESPACE__,__TRAIT__' +); /** * Order of operation ENUMs. * http://php.net/manual/en/language.operators.precedence.php */ -Blockly.PHP.ORDER_ATOMIC = 0; // 0 "" ... -Blockly.PHP.ORDER_CLONE = 1; // clone -Blockly.PHP.ORDER_NEW = 1; // new -Blockly.PHP.ORDER_MEMBER = 2; // () -Blockly.PHP.ORDER_FUNCTION_CALL = 2; // () -Blockly.PHP.ORDER_INCREMENT = 3; // ++ -Blockly.PHP.ORDER_DECREMENT = 3; // -- -Blockly.PHP.ORDER_LOGICAL_NOT = 4; // ! -Blockly.PHP.ORDER_BITWISE_NOT = 4; // ~ -Blockly.PHP.ORDER_UNARY_PLUS = 4; // + -Blockly.PHP.ORDER_UNARY_NEGATION = 4; // - -Blockly.PHP.ORDER_MULTIPLICATION = 5; // * -Blockly.PHP.ORDER_DIVISION = 5; // / -Blockly.PHP.ORDER_MODULUS = 5; // % -Blockly.PHP.ORDER_ADDITION = 6; // + -Blockly.PHP.ORDER_SUBTRACTION = 6; // - -Blockly.PHP.ORDER_BITWISE_SHIFT = 7; // << >> >>> -Blockly.PHP.ORDER_RELATIONAL = 8; // < <= > >= -Blockly.PHP.ORDER_IN = 8; // in -Blockly.PHP.ORDER_INSTANCEOF = 8; // instanceof -Blockly.PHP.ORDER_EQUALITY = 9; // == != === !== -Blockly.PHP.ORDER_BITWISE_AND = 10; // & -Blockly.PHP.ORDER_BITWISE_XOR = 11; // ^ -Blockly.PHP.ORDER_BITWISE_OR = 12; // | -Blockly.PHP.ORDER_CONDITIONAL = 13; // ?: -Blockly.PHP.ORDER_ASSIGNMENT = 14; // = += -= *= /= %= <<= >>= ... -Blockly.PHP.ORDER_LOGICAL_AND = 15; // && -Blockly.PHP.ORDER_LOGICAL_OR = 16; // || -Blockly.PHP.ORDER_COMMA = 17; // , -Blockly.PHP.ORDER_NONE = 99; // (...) +Blockly.PHP.ORDER_ATOMIC = 0; // 0 "" ... +Blockly.PHP.ORDER_CLONE = 1; // clone +Blockly.PHP.ORDER_NEW = 1; // new +Blockly.PHP.ORDER_MEMBER = 2.1; // [] +Blockly.PHP.ORDER_FUNCTION_CALL = 2.2; // () +Blockly.PHP.ORDER_POWER = 3; // ** +Blockly.PHP.ORDER_INCREMENT = 4; // ++ +Blockly.PHP.ORDER_DECREMENT = 4; // -- +Blockly.PHP.ORDER_BITWISE_NOT = 4; // ~ +Blockly.PHP.ORDER_CAST = 4; // (int) (float) (string) (array) ... +Blockly.PHP.ORDER_SUPPRESS_ERROR = 4; // @ +Blockly.PHP.ORDER_INSTANCEOF = 5; // instanceof +Blockly.PHP.ORDER_LOGICAL_NOT = 6; // ! +Blockly.PHP.ORDER_UNARY_PLUS = 7.1; // + +Blockly.PHP.ORDER_UNARY_NEGATION = 7.2; // - +Blockly.PHP.ORDER_MULTIPLICATION = 8.1; // * +Blockly.PHP.ORDER_DIVISION = 8.2; // / +Blockly.PHP.ORDER_MODULUS = 8.3; // % +Blockly.PHP.ORDER_ADDITION = 9.1; // + +Blockly.PHP.ORDER_SUBTRACTION = 9.2; // - +Blockly.PHP.ORDER_STRING_CONCAT = 9.3; // . +Blockly.PHP.ORDER_BITWISE_SHIFT = 10; // << >> +Blockly.PHP.ORDER_RELATIONAL = 11; // < <= > >= +Blockly.PHP.ORDER_EQUALITY = 12; // == != === !== <> <=> +Blockly.PHP.ORDER_REFERENCE = 13; // & +Blockly.PHP.ORDER_BITWISE_AND = 13; // & +Blockly.PHP.ORDER_BITWISE_XOR = 14; // ^ +Blockly.PHP.ORDER_BITWISE_OR = 15; // | +Blockly.PHP.ORDER_LOGICAL_AND = 16; // && +Blockly.PHP.ORDER_LOGICAL_OR = 17; // || +Blockly.PHP.ORDER_IF_NULL = 18; // ?? +Blockly.PHP.ORDER_CONDITIONAL = 19; // ?: +Blockly.PHP.ORDER_ASSIGNMENT = 20; // = += -= *= /= %= <<= >>= ... +Blockly.PHP.ORDER_LOGICAL_AND_WEAK = 21; // and +Blockly.PHP.ORDER_LOGICAL_XOR = 22; // xor +Blockly.PHP.ORDER_LOGICAL_OR_WEAK = 23; // or +Blockly.PHP.ORDER_COMMA = 24; // , +Blockly.PHP.ORDER_NONE = 99; // (...) + +/** + * List of outer-inner pairings that do NOT require parentheses. + * @type {!Array.>} + */ +Blockly.PHP.ORDER_OVERRIDES = [ + // (foo()).bar() -> foo().bar() + // (foo())[0] -> foo()[0] + [Blockly.PHP.ORDER_MEMBER, Blockly.PHP.ORDER_FUNCTION_CALL], + // (foo[0])[1] -> foo[0][1] + // (foo.bar).baz -> foo.bar.baz + [Blockly.PHP.ORDER_MEMBER, Blockly.PHP.ORDER_MEMBER], + // !(!foo) -> !!foo + [Blockly.PHP.ORDER_LOGICAL_NOT, Blockly.PHP.ORDER_LOGICAL_NOT], + // a * (b * c) -> a * b * c + [Blockly.PHP.ORDER_MULTIPLICATION, Blockly.PHP.ORDER_MULTIPLICATION], + // a + (b + c) -> a + b + c + [Blockly.PHP.ORDER_ADDITION, Blockly.PHP.ORDER_ADDITION], + // a && (b && c) -> a && b && c + [Blockly.PHP.ORDER_LOGICAL_AND, Blockly.PHP.ORDER_LOGICAL_AND], + // a || (b || c) -> a || b || c + [Blockly.PHP.ORDER_LOGICAL_OR, Blockly.PHP.ORDER_LOGICAL_OR] +]; + +/** + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. + */ +Blockly.PHP.ONE_BASED_INDEXING = true; /** * Initialise the database of variable names. @@ -173,9 +227,9 @@ Blockly.PHP.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.PHP.allNestedComments(childBlock); if (comment) { @@ -189,3 +243,66 @@ Blockly.PHP.scrub_ = function(block, code) { var nextCode = Blockly.PHP.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value while taking into account indexing. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @param {number=} opt_order The highest order acting on this value. + * @return {string|number} + */ +Blockly.PHP.getAdjusted = function(block, atId, opt_delta, opt_negate, + opt_order) { + var delta = opt_delta || 0; + var order = opt_order || Blockly.PHP.ORDER_NONE; + if (Blockly.PHP.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.PHP.ONE_BASED_INDEXING ? '1' : '0'; + if (delta > 0) { + var at = Blockly.PHP.valueToCode(block, atId, + Blockly.PHP.ORDER_ADDITION) || defaultAtIndex; + } else if (delta < 0) { + var at = Blockly.PHP.valueToCode(block, atId, + Blockly.PHP.ORDER_SUBTRACTION) || defaultAtIndex; + } else if (opt_negate) { + var at = Blockly.PHP.valueToCode(block, atId, + Blockly.PHP.ORDER_UNARY_NEGATION) || defaultAtIndex; + } else { + var at = Blockly.PHP.valueToCode(block, atId, order) || + defaultAtIndex; + } + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseFloat(at) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = at + ' + ' + delta; + var innerOrder = Blockly.PHP.ORDER_ADDITION; + } else if (delta < 0) { + at = at + ' - ' + -delta; + var innerOrder = Blockly.PHP.ORDER_SUBTRACTION; + } + if (opt_negate) { + if (delta) { + at = '-(' + at + ')'; + } else { + at = '-' + at; + } + var innerOrder = Blockly.PHP.ORDER_UNARY_NEGATION; + } + innerOrder = Math.floor(innerOrder); + order = Math.floor(order); + if (innerOrder && order >= innerOrder) { + at = '(' + at + ')'; + } + } + return at; +}; diff --git a/generators/php/colour.js b/generators/php/colour.js index bc1789c35..e73c17a95 100644 --- a/generators/php/colour.js +++ b/generators/php/colour.js @@ -39,10 +39,10 @@ Blockly.PHP['colour_random'] = function(block) { // Generate a random colour. var functionName = Blockly.PHP.provideFunction_( 'colour_random', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', - ' return \'#\' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), ' + - '6, \'0\', STR_PAD_LEFT);', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', + ' return \'#\' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), ' + + '6, \'0\', STR_PAD_LEFT);', + '}']); var code = functionName + '()'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -57,17 +57,17 @@ Blockly.PHP['colour_rgb'] = function(block) { Blockly.PHP.ORDER_COMMA) || 0; var functionName = Blockly.PHP.provideFunction_( 'colour_rgb', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($r, $g, $b) {', - ' $r = round(max(min($r, 100), 0) * 2.55);', - ' $g = round(max(min($g, 100), 0) * 2.55);', - ' $b = round(max(min($b, 100), 0) * 2.55);', - ' $hex = "#";', - ' $hex .= str_pad(dechex($r), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($g), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($b), 2, "0", STR_PAD_LEFT);', - ' return $hex;', - '}']); + ' $r = round(max(min($r, 100), 0) * 2.55);', + ' $g = round(max(min($g, 100), 0) * 2.55);', + ' $b = round(max(min($b, 100), 0) * 2.55);', + ' $hex = \'#\';', + ' $hex .= str_pad(dechex($r), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($g), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($b), 2, \'0\', STR_PAD_LEFT);', + ' return $hex;', + '}']); var code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -82,24 +82,24 @@ Blockly.PHP['colour_blend'] = function(block) { Blockly.PHP.ORDER_COMMA) || 0.5; var functionName = Blockly.PHP.provideFunction_( 'colour_blend', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($c1, $c2, $ratio) {', - ' $ratio = max(min($ratio, 1), 0);', - ' $r1 = hexdec(substr($c1, 1, 2));', - ' $g1 = hexdec(substr($c1, 3, 2));', - ' $b1 = hexdec(substr($c1, 5, 2));', - ' $r2 = hexdec(substr($c2, 1, 2));', - ' $g2 = hexdec(substr($c2, 3, 2));', - ' $b2 = hexdec(substr($c2, 5, 2));', - ' $r = round($r1 * (1 - $ratio) + $r2 * $ratio);', - ' $g = round($g1 * (1 - $ratio) + $g2 * $ratio);', - ' $b = round($b1 * (1 - $ratio) + $b2 * $ratio);', - ' $hex = "#";', - ' $hex .= str_pad(dechex($r), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($g), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($b), 2, "0", STR_PAD_LEFT);', - ' return $hex;', - '}']); + ' $ratio = max(min($ratio, 1), 0);', + ' $r1 = hexdec(substr($c1, 1, 2));', + ' $g1 = hexdec(substr($c1, 3, 2));', + ' $b1 = hexdec(substr($c1, 5, 2));', + ' $r2 = hexdec(substr($c2, 1, 2));', + ' $g2 = hexdec(substr($c2, 3, 2));', + ' $b2 = hexdec(substr($c2, 5, 2));', + ' $r = round($r1 * (1 - $ratio) + $r2 * $ratio);', + ' $g = round($g1 * (1 - $ratio) + $g2 * $ratio);', + ' $b = round($b1 * (1 - $ratio) + $b2 * $ratio);', + ' $hex = \'#\';', + ' $hex .= str_pad(dechex($r), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($g), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($b), 2, \'0\', STR_PAD_LEFT);', + ' return $hex;', + '}']); var code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/lists.js b/generators/php/lists.js index a406aa6d2..449c31a5e 100644 --- a/generators/php/lists.js +++ b/generators/php/lists.js @@ -22,6 +22,17 @@ * @fileoverview Generating PHP for list blocks. * @author daarond@gmail.com (Daaron Dwyer) */ + +/* + * Lists in PHP are known to break when non-variables are passed into blocks + * that require a list. PHP, unlike other languages, passes arrays as reference + * value instead of value so we are unable to support it to the extent we can + * for the other languages. + * For example, a ternary operator with two arrays will return the array by + * value and that cannot be passed into any of the built-in array functions for + * PHP (because only variables can be passed by reference). + * ex: end(true ? list1 : list2) + */ 'use strict'; goog.provide('Blockly.PHP.lists'); @@ -31,37 +42,37 @@ goog.require('Blockly.PHP'); Blockly.PHP['lists_create_empty'] = function(block) { // Create an empty list. - return ['array()', Blockly.PHP.ORDER_ATOMIC]; + return ['array()', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.PHP.valueToCode(block, 'ADD' + n, + for (var i = 0; i < block.itemCount_; i++) { + code[i] = Blockly.PHP.valueToCode(block, 'ADD' + i, Blockly.PHP.ORDER_COMMA) || 'null'; } code = 'array(' + code.join(', ') + ')'; - return [code, Blockly.PHP.ORDER_ATOMIC]; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_repeat'] = function(block) { // Create a list with one element repeated. var functionName = Blockly.PHP.provideFunction_( 'lists_repeat', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value, $count) {', - ' $array = array();', - ' for ($index = 0; $index < $count; $index++) {', - ' $array[] = $value;', - ' }', - ' return $array;', - '}']); - var argument0 = Blockly.PHP.valueToCode(block, 'ITEM', + ' $array = array();', + ' for ($index = 0; $index < $count; $index++) {', + ' $array[] = $value;', + ' }', + ' return $array;', + '}']); + var element = Blockly.PHP.valueToCode(block, 'ITEM', Blockly.PHP.ORDER_COMMA) || 'null'; - var argument1 = Blockly.PHP.valueToCode(block, 'NUM', + var repeatCount = Blockly.PHP.valueToCode(block, 'NUM', Blockly.PHP.ORDER_COMMA) || '0'; - var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; + var code = functionName + '(' + element + ', ' + repeatCount + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -69,16 +80,16 @@ Blockly.PHP['lists_length'] = function(block) { // String or array length. var functionName = Blockly.PHP.provideFunction_( 'length', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', - ' if (is_string($value)) {', - ' return strlen($value);', - ' } else {', - ' return count($value);', - ' }', - '}']); - var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - return [functionName + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', + ' if (is_string($value)) {', + ' return strlen($value);', + ' } else {', + ' return count($value);', + ' }', + '}']); + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + return [functionName + '(' + list + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_isEmpty'] = function(block) { @@ -90,36 +101,42 @@ Blockly.PHP['lists_isEmpty'] = function(block) { Blockly.PHP['lists_indexOf'] = function(block) { // Find an item in the list. - var operator = block.getFieldValue('END') == 'FIRST' ? - 'indexOf' : 'lastIndexOf'; var argument0 = Blockly.PHP.valueToCode(block, 'FIND', Blockly.PHP.ORDER_NONE) || '\'\''; var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', Blockly.PHP.ORDER_MEMBER) || '[]'; - var functionName; - if (block.getFieldValue('END') == 'FIRST'){ + if (Blockly.PHP.ONE_BASED_INDEXING) { + var errorIndex = ' 0'; + var indexAdjustment = ' + 1'; + } else { + var errorIndex = ' -1'; + var indexAdjustment = ''; + } + if (block.getFieldValue('END') == 'FIRST') { // indexOf - functionName = Blockly.PHP.provideFunction_( + var functionName = Blockly.PHP.provideFunction_( 'indexOf', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($haystack, $needle) {', - ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) return $index + 1;', - ' }', - ' return 0;', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($haystack, $needle) {', + ' for ($index = 0; $index < count($haystack); $index++) {', + ' if ($haystack[$index] == $needle) return $index' + + indexAdjustment + ';', + ' }', + ' return ' + errorIndex + ';', + '}']); } else { // lastIndexOf - functionName = Blockly.PHP.provideFunction_( + var functionName = Blockly.PHP.provideFunction_( 'lastIndexOf', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($haystack, $needle) {', - ' $last = 0;', - ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) $last = $index + 1;', - ' }', - ' return $last;', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($haystack, $needle) {', + ' $last = ' + errorIndex + ';', + ' for ($index = 0; $index < count($haystack); $index++) {', + ' if ($haystack[$index] == $needle) $last = $index' + + indexAdjustment + ';', + ' }', + ' return $last;', + '}']); } var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; @@ -130,97 +147,113 @@ Blockly.PHP['lists_getIndex'] = function(block) { // Get element at index. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.PHP.valueToCode(block, 'AT', - Blockly.PHP.ORDER_UNARY_NEGATION) || '1'; - if (mode == 'GET') { - var order = Blockly.PHP.ORDER_FUNCTION_CALL; - } else { - // List will be an argument in a function call. - var order = Blockly.PHP.ORDER_COMMA; - } - var list = Blockly.PHP.valueToCode(block, 'VALUE', order) || 'array()'; - - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '[0]'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE') { - var code = 'array_shift(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return 'array_shift(' + list + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = 'end(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE') { - var code = 'array_pop(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return 'array_pop(' + list + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE') { - var code = 'array_splice(' + list + ', ' + at + ', 1)[0]'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return 'array_splice(' + list + ', ' + at + ', 1);\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var code = 'array_slice(' + list + ', -' + at + ', 1)[0]'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { - code = 'array_splice(' + list + - ', count(' + list + ') - ' + at + ', 1)[0]'; - if (mode == 'GET_REMOVE') { + switch (where) { + case 'FIRST': + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + var code = list + '[0]'; + return [code, Blockly.PHP.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var code = 'array_shift(' + list + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + ';\n'; + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + return 'array_shift(' + list + ');\n'; } - } - } else if (where == 'RANDOM') { - if (mode == 'GET'){ - var functionName = Blockly.PHP.provideFunction_( - 'lists_get_random_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($list) {', - ' return $list[rand(0,count($list)-1)];', - '}']); - code = functionName + '(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE'){ - var functionName = Blockly.PHP.provideFunction_( - 'lists_get_remove_random_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list) {', - ' $x = rand(0,count($list)-1);', - ' unset($list[$x]);', - ' return array_values($list);', - '}']); - code = functionName + '(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_remove_random_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list) {', - ' unset($list[rand(0,count($list)-1)]);', - '}']); - return functionName + '(' + list + ');\n'; - } + break; + case 'LAST': + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var code = 'end(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var code = 'array_pop(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + return 'array_pop(' + list + ');\n'; + } + break; + case 'FROM_START': + var at = Blockly.PHP.getAdjusted(block, 'AT'); + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + var code = list + '[' + at + ']'; + return [code, Blockly.PHP.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_COMMA) || 'array()'; + var code = 'array_splice(' + list + ', ' + at + ', 1)[0]'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_COMMA) || 'array()'; + return 'array_splice(' + list + ', ' + at + ', 1);\n'; + } + break; + case 'FROM_END': + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_COMMA) || 'array()'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1, true); + var code = 'array_slice(' + list + ', ' + at + ', 1)[0]'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1, false, + Blockly.PHP.ORDER_SUBTRACTION); + code = 'array_splice(' + list + + ', count(' + list + ') - ' + at + ', 1)[0]'; + if (mode == 'GET_REMOVE') { + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return code + ';\n'; + } + } + break; + case 'RANDOM': + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + if (mode == 'GET') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_get_random_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list) {', + ' return $list[rand(0,count($list)-1)];', + '}']); + code = functionName + '(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_get_remove_random_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list) {', + ' $x = rand(0,count($list)-1);', + ' unset($list[$x]);', + ' return array_values($list);', + '}']); + code = functionName + '(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_remove_random_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list) {', + ' unset($list[rand(0,count($list)-1)]);', + '}']); + return functionName + '(' + list + ');\n'; + } + break; } throw 'Unhandled combination (lists_getIndex).'; }; @@ -228,18 +261,14 @@ Blockly.PHP['lists_getIndex'] = function(block) { Blockly.PHP['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. - var list = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_MEMBER) || 'array()'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.PHP.valueToCode(block, 'AT', - Blockly.PHP.ORDER_NONE) || '1'; var value = Blockly.PHP.valueToCode(block, 'TO', Blockly.PHP.ORDER_ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. // Closure, which accesses and modifies 'list'. function cacheList() { - if (list.match(/^\w+$/)) { + if (list.match(/^\$\w+$/)) { return ''; } var listVar = Blockly.PHP.variableDB_.getDistinctName( @@ -248,68 +277,83 @@ Blockly.PHP['lists_setIndex'] = function(block) { list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return 'array_unshift(' + list + ', ' + value + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_set_last_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $value) {', - ' $list[count($list) - 1] = $value;', - '}']); - return functionName + '(' + list + ', ' + value + ');\n'; - } else if (mode == 'INSERT') { - return 'array_push(' + list + ', ' + value + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return 'array_splice(' + list + ', ' + at + ', 0, ' + value + ');\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'SET') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_set_from_end', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $at, $value) {', - ' $list[count($list) - $at] = $value;', - '}']); - return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; - } else if (mode == 'INSERT') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_insert_from_end', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $at, $value) {', - ' return array_splice($list, count($list) - $at, 0, $value);', - '}']); - return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; - } - } else if (where == 'RANDOM') { - var code = cacheList(); - var xVar = Blockly.PHP.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += xVar + ' = rand(0, count(' + list + ')-1);\n'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += 'array_splice(' + list + ', ' + xVar + ', 0, ' + value + ');\n'; - return code; - } + switch (where) { + case 'FIRST': + if (mode == 'SET') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + return list + '[0] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + return 'array_unshift(' + list + ', ' + value + ');\n'; + } + break; + case 'LAST': + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + if (mode == 'SET') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_set_last_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list, $value) {', + ' $list[count($list) - 1] = $value;', + '}']); + return functionName + '(' + list + ', ' + value + ');\n'; + } else if (mode == 'INSERT') { + return 'array_push(' + list + ', ' + value + ');\n'; + } + break; + case 'FROM_START': + var at = Blockly.PHP.getAdjusted(block, 'AT'); + if (mode == 'SET') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + return list + '[' + at + '] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + return 'array_splice(' + list + ', ' + at + ', 0, ' + value + ');\n'; + } + break; + case 'FROM_END': + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1); + if (mode == 'SET') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_set_from_end', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list, $at, $value) {', + ' $list[count($list) - $at] = $value;', + '}']); + return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; + } else if (mode == 'INSERT') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_insert_from_end', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list, $at, $value) {', + ' return array_splice($list, count($list) - $at, 0, $value);', + '}']); + return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; + } + break; + case 'RANDOM': + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_REFERENCE) || 'array()'; + var code = cacheList(); + var xVar = Blockly.PHP.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += xVar + ' = rand(0, count(' + list + ')-1);\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += 'array_splice(' + list + ', ' + xVar + ', 0, ' + value + + ');\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; @@ -317,44 +361,90 @@ Blockly.PHP['lists_setIndex'] = function(block) { Blockly.PHP['lists_getSublist'] = function(block) { // Get sublist. var list = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_MEMBER) || 'array()'; + Blockly.PHP.ORDER_COMMA) || 'array()'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.PHP.valueToCode(block, 'AT1', - Blockly.PHP.ORDER_NONE) || '1'; - var at2 = Blockly.PHP.valueToCode(block, 'AT2', - Blockly.PHP.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = list; + } else if (list.match(/^\$\w+$/) || + (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the list is a simple value or doesn't require a call for length, don't + // generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.PHP.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.PHP.getAdjusted(block, 'AT1', 1, false, + Blockly.PHP.ORDER_SUBTRACTION); + at1 = 'count(' + list + ') - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.PHP.getAdjusted(block, 'AT2', 0, false, + Blockly.PHP.ORDER_SUBTRACTION); + var length = at2 + ' - '; + if (Blockly.isNumber(String(at1)) || String(at1).match(/^\(.+\)$/)) { + length += at1; + } else { + length += '(' + at1 + ')'; + } + length += ' + 1'; + break; + case 'FROM_END': + var at2 = Blockly.PHP.getAdjusted(block, 'AT2', 0, false, + Blockly.PHP.ORDER_SUBTRACTION); + var length = 'count(' + list + ') - ' + at2 + ' - '; + if (Blockly.isNumber(String(at1)) || String(at1).match(/^\(.+\)$/)) { + length += at1; + } else { + length += '(' + at1 + ')'; + } + break; + case 'LAST': + var length = 'count(' + list + ') - '; + if (Blockly.isNumber(String(at1)) || String(at1).match(/^\(.+\)$/)) { + length += at1; + } else { + length += '(' + at1 + ')'; + } + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + code = 'array_slice(' + list + ', ' + at1 + ', ' + length + ')'; } else { + var at1 = Blockly.PHP.getAdjusted(block, 'AT1'); + var at2 = Blockly.PHP.getAdjusted(block, 'AT2'); var functionName = Blockly.PHP.provideFunction_( 'lists_get_sublist', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list, $where1, $at1, $where2, $at2) {', - ' if ($where2 == \'FROM_START\') {', - ' $at2--;', - ' } else if ($where2 == \'FROM_END\') {', - ' $at2 = $at2 - $at1;', - ' } else if ($where2 == \'FIRST\') {', - ' $at2 = 0;', - ' } else if ($where2 == \'LAST\') {', - ' $at2 = count($list);', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' if ($where1 == \'FROM_START\') {', - ' $at1--;', - ' } else if ($where1 == \'FROM_END\') {', - ' $at1 = count($list) - $at1;', - ' } else if ($where1 == \'FIRST\') {', - ' $at1 = 0;', - ' } else if ($where1 == \'LAST\') {', - ' $at1 = count($list) - 1;', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' return array_slice($list, $at1, $at2);', - '}']); + ' if ($where1 == \'FROM_END\') {', + ' $at1 = count($list) - 1 - $at1;', + ' } else if ($where1 == \'FIRST\') {', + ' $at1 = 0;', + ' } else if ($where1 != \'FROM_START\'){', + ' throw new Exception(\'Unhandled option (lists_get_sublist).\');', + ' }', + ' $length = 0;', + ' if ($where2 == \'FROM_START\') {', + ' $length = $at2 - $at1 + 1;', + ' } else if ($where2 == \'FROM_END\') {', + ' $length = count($list) - $at1 - $at2;', + ' } else if ($where2 == \'LAST\') {', + ' $length = count($list) - $at1;', + ' } else {', + ' throw new Exception(\'Unhandled option (lists_get_sublist).\');', + ' }', + ' return array_slice($list, $at1, $length);', + '}']); var code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -364,26 +454,26 @@ Blockly.PHP['lists_getSublist'] = function(block) { Blockly.PHP['lists_sort'] = function(block) { // Block for sorting a list. var listCode = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; + Blockly.PHP.ORDER_COMMA) || 'array()'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); var functionName = Blockly.PHP.provideFunction_( - 'lists_sort', [ - 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($list, $type, $direction) {', - ' $sortCmpFuncs = array(', - ' "NUMERIC" => "strnatcasecmp",', - ' "TEXT" => "strcmp",', - ' "IGNORE_CASE" => "strcasecmp"', - ' );', - ' $sortCmp = $sortCmpFuncs[$type];', - ' $list2 = $list;', // Clone list. - ' usort($list2, $sortCmp);', - ' if ($direction == -1) {', - ' $list2 = array_reverse($list2);', - ' }', - ' return $list2;', - '}']); + 'lists_sort', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list, $type, $direction) {', + ' $sortCmpFuncs = array(', + ' "NUMERIC" => "strnatcasecmp",', + ' "TEXT" => "strcmp",', + ' "IGNORE_CASE" => "strcasecmp"', + ' );', + ' $sortCmp = $sortCmpFuncs[$type];', + ' $list2 = $list;', // Clone list. + ' usort($list2, $sortCmp);', + ' if ($direction == -1) {', + ' $list2 = array_reverse($list2);', + ' }', + ' return $list2;', + '}']); var sortCode = functionName + '(' + listCode + ', "' + type + '", ' + direction + ')'; return [sortCode, Blockly.PHP.ORDER_FUNCTION_CALL]; @@ -392,9 +482,9 @@ Blockly.PHP['lists_sort'] = function(block) { Blockly.PHP['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. var value_input = Blockly.PHP.valueToCode(block, 'INPUT', - Blockly.PHP.ORDER_MEMBER); + Blockly.PHP.ORDER_COMMA); var value_delim = Blockly.PHP.valueToCode(block, 'DELIM', - Blockly.PHP.ORDER_NONE) || '\'\''; + Blockly.PHP.ORDER_COMMA) || '\'\''; var mode = block.getFieldValue('MODE'); if (mode == 'SPLIT') { if (!value_input) { diff --git a/generators/php/math.js b/generators/php/math.js index f0bb9fa79..7789ba8fe 100644 --- a/generators/php/math.js +++ b/generators/php/math.js @@ -47,20 +47,14 @@ Blockly.PHP['math_arithmetic'] = function(block) { 'MINUS': [' - ', Blockly.PHP.ORDER_SUBTRACTION], 'MULTIPLY': [' * ', Blockly.PHP.ORDER_MULTIPLICATION], 'DIVIDE': [' / ', Blockly.PHP.ORDER_DIVISION], - 'POWER': [null, Blockly.PHP.ORDER_COMMA] // Handle power separately. + 'POWER': [' ** ', Blockly.PHP.ORDER_POWER] }; var tuple = OPERATORS[block.getFieldValue('OP')]; var operator = tuple[0]; var order = tuple[1]; var argument0 = Blockly.PHP.valueToCode(block, 'A', order) || '0'; var argument1 = Blockly.PHP.valueToCode(block, 'B', order) || '0'; - var code; - // Power in PHP requires a special case since it has no operator. - if (!operator) { - code = 'pow(' + argument0 + ', ' + argument1 + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } - code = argument0 + operator + argument1; + var code = argument0 + operator + argument1; return [code, order]; }; @@ -172,25 +166,25 @@ Blockly.PHP['math_number_property'] = function(block) { // Prime is a special case as it is not a one-liner test. var functionName = Blockly.PHP.provideFunction_( 'math_isPrime', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if ($n == 2 || $n == 3) {', - ' return true;', - ' }', - ' // False if n is NaN, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + - ' $n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', - ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if ($n == 2 || $n == 3) {', + ' return true;', + ' }', + ' // False if n is NaN, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + + ' $n % 3 == 0) {', + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', + ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); code = functionName + '(' + number_to_check + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } @@ -256,10 +250,10 @@ Blockly.PHP['math_on_list'] = function(block) { case 'AVERAGE': var functionName = Blockly.PHP.provideFunction_( 'math_mean', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($myList) {', - ' return array_sum($myList) / count($myList);', - '}']); + ' return array_sum($myList) / count($myList);', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || 'array()'; code = functionName + '(' + list + ')'; @@ -267,12 +261,13 @@ Blockly.PHP['math_on_list'] = function(block) { case 'MEDIAN': var functionName = Blockly.PHP.provideFunction_( 'math_median', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($arr) {', - ' sort($arr,SORT_NUMERIC);', - ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ', - ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2) - 1]) / 2;', - '}']); + ' sort($arr,SORT_NUMERIC);', + ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ', + ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2)' + + ' - 1]) / 2;', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -283,13 +278,14 @@ Blockly.PHP['math_on_list'] = function(block) { // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Blockly.PHP.provideFunction_( 'math_modes', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($values) {', - ' $v = array_count_values($values);', - ' arsort($v);', - ' foreach($v as $k => $v){$total = $k; break;}', - ' return array($total);', - '}']); + ' if (empty($values)) return array();', + ' $counts = array_count_values($values);', + ' arsort($counts); // Sort counts in descending order', + ' $modes = array_keys($counts, current($counts), true);', + ' return $modes;', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -297,14 +293,15 @@ Blockly.PHP['math_on_list'] = function(block) { case 'STD_DEV': var functionName = Blockly.PHP.provideFunction_( 'math_standard_deviation', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($numbers) {', - ' $n = count($numbers);', - ' if (!$n) return null;', - ' $mean = array_sum($numbers) / count($numbers);', - ' foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2);', - ' return sqrt(array_sum($devs) / (count($devs) - 1));', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($numbers) {', + ' $n = count($numbers);', + ' if (!$n) return null;', + ' $mean = array_sum($numbers) / count($numbers);', + ' foreach($numbers as $key => $num) $devs[$key] = ' + + 'pow($num - $mean, 2);', + ' return sqrt(array_sum($devs) / (count($devs) - 1));', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -312,11 +309,11 @@ Blockly.PHP['math_on_list'] = function(block) { case 'RANDOM': var functionName = Blockly.PHP.provideFunction_( 'math_random_list', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list) {', - ' $x = rand(0, count($list)-1);', - ' return $list[$x];', - '}']); + ' $x = rand(0, count($list)-1);', + ' return $list[$x];', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -358,13 +355,13 @@ Blockly.PHP['math_random_int'] = function(block) { Blockly.PHP.ORDER_COMMA) || '0'; var functionName = Blockly.PHP.provideFunction_( 'math_random_int', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($a, $b) {', - ' if ($a > $b) {', - ' return rand($b, $a);', - ' }', - ' return rand($a, $b);', - '}']); + ' if ($a > $b) {', + ' return rand($b, $a);', + ' }', + ' return rand($a, $b);', + '}']); var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/procedures.js b/generators/php/procedures.js index 1fd4cc872..0dc56d7cb 100644 --- a/generators/php/procedures.js +++ b/generators/php/procedures.js @@ -63,8 +63,8 @@ Blockly.PHP['procedures_defreturn'] = function(block) { returnValue = ' return ' + returnValue + ';\n'; } var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.PHP.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.PHP.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + @@ -85,8 +85,8 @@ Blockly.PHP['procedures_callreturn'] = function(block) { var funcName = Blockly.PHP.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.PHP.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.PHP.valueToCode(block, 'ARG' + i, Blockly.PHP.ORDER_COMMA) || 'null'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -98,8 +98,8 @@ Blockly.PHP['procedures_callnoreturn'] = function(block) { var funcName = Blockly.PHP.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.PHP.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.PHP.valueToCode(block, 'ARG' + i, Blockly.PHP.ORDER_COMMA) || 'null'; } var code = funcName + '(' + args.join(', ') + ');\n'; diff --git a/generators/php/text.js b/generators/php/text.js index 3960ab011..efc0f2de3 100644 --- a/generators/php/text.js +++ b/generators/php/text.js @@ -37,28 +37,27 @@ Blockly.PHP['text'] = function(block) { Blockly.PHP['text_join'] = function(block) { // Create a string made up of any number of elements of any type. - var code; if (block.itemCount_ == 0) { return ['\'\'', Blockly.PHP.ORDER_ATOMIC]; } else if (block.itemCount_ == 1) { - var argument0 = Blockly.PHP.valueToCode(block, 'ADD0', + var element = Blockly.PHP.valueToCode(block, 'ADD0', Blockly.PHP.ORDER_NONE) || '\'\''; - code = argument0; + var code = element; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } else if (block.itemCount_ == 2) { - var argument0 = Blockly.PHP.valueToCode(block, 'ADD0', + var element0 = Blockly.PHP.valueToCode(block, 'ADD0', Blockly.PHP.ORDER_NONE) || '\'\''; - var argument1 = Blockly.PHP.valueToCode(block, 'ADD1', + var element1 = Blockly.PHP.valueToCode(block, 'ADD1', Blockly.PHP.ORDER_NONE) || '\'\''; - code = argument0 + ' . ' + argument1; + var code = element0 + ' . ' + element1; return [code, Blockly.PHP.ORDER_ADDITION]; } else { - code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.PHP.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.PHP.valueToCode(block, 'ADD' + i, Blockly.PHP.ORDER_COMMA) || '\'\''; } - code = 'implode(\'\', array(' + code.join(',') + '))'; + var code = 'implode(\'\', array(' + elements.join(',') + '))'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } }; @@ -67,89 +66,89 @@ Blockly.PHP['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_NONE) || '\'\''; - return varName + ' .= ' + argument0 + ';\n'; + var value = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_ASSIGNMENT) || '\'\''; + return varName + ' .= ' + value + ';\n'; }; Blockly.PHP['text_length'] = function(block) { // String or array length. var functionName = Blockly.PHP.provideFunction_( 'length', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', - ' if (is_string($value)) {', - ' return strlen($value);', - ' } else {', - ' return count($value);', - ' }', - '}']); - var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - return [functionName + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', + ' if (is_string($value)) {', + ' return strlen($value);', + ' } else {', + ' return count($value);', + ' }', + '}']); + var text = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + return [functionName + '(' + text + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - return ['empty(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + var text = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + return ['empty(' + text + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_indexOf'] = function(block) { // Search the text for a substring. var operator = block.getFieldValue('END') == 'FIRST' ? 'strpos' : 'strrpos'; - var argument0 = Blockly.PHP.valueToCode(block, 'FIND', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - + var substring = Blockly.PHP.valueToCode(block, 'FIND', + Blockly.PHP.ORDER_NONE) || '\'\''; + var text = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + if (Blockly.PHP.ONE_BASED_INDEXING) { + var errorIndex = ' 0'; + var indexAdjustment = ' + 1'; + } else { + var errorIndex = ' -1'; + var indexAdjustment = ''; + } var functionName = Blockly.PHP.provideFunction_( block.getFieldValue('END') == 'FIRST' ? 'text_indexOf' : 'text_lastIndexOf', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text, $search) {', - ' $pos = ' + operator + '($text, $search);', - ' return $pos === false ? 0 : $pos + 1;', - '}']); - var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + ' $pos = ' + operator + '($text, $search);', + ' return $pos === false ? ' + errorIndex + ' : $pos' + + indexAdjustment + ';', + '}']); + var code = functionName + '(' + text + ', ' + substring + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_charAt'] = function(block) { // Get letter at index. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.PHP.valueToCode(block, 'AT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '0'; - var text = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; + var textOrder = (where == 'RANDOM') ? Blockly.PHP.ORDER_NONE : + Blockly.PHP.ORDER_COMMA; + var text = Blockly.PHP.valueToCode(block, 'VALUE', textOrder) || '\'\''; switch (where) { case 'FIRST': - var code = text + '[0]'; + var code = 'substr(' + text + ', 0, 1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'LAST': - var code = 'substr(' + text + ', -1, 1)'; + var code = 'substr(' + text + ', -1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'FROM_START': - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } + var at = Blockly.PHP.getAdjusted(block, 'AT'); var code = 'substr(' + text + ', ' + at + ', 1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'FROM_END': - var code = 'substr(' + text + ', -' + at + ', 1)'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1, true); + var code = 'substr(' + text + ', ' + at + ', 1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'RANDOM': var functionName = Blockly.PHP.provideFunction_( 'text_random_letter', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text) {', - ' return $text[rand(0, strlen($text) - 1)];', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text) {', + ' return $text[rand(0, strlen($text) - 1)];', + '}']); code = functionName + '(' + text + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } @@ -162,37 +161,34 @@ Blockly.PHP['text_getSubstring'] = function(block) { Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.PHP.valueToCode(block, 'AT1', - Blockly.PHP.ORDER_FUNCTION_CALL) || '0'; - var at2 = Blockly.PHP.valueToCode(block, 'AT2', - Blockly.PHP.ORDER_FUNCTION_CALL) || '0'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = text; } else { + var at1 = Blockly.PHP.getAdjusted(block, 'AT1'); + var at2 = Blockly.PHP.getAdjusted(block, 'AT2'); var functionName = Blockly.PHP.provideFunction_( 'text_get_substring', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text, $where1, $at1, $where2, $at2) {', - ' if ($where2 == \'FROM_START\') {', - ' $at2--;', - ' } else if ($where2 == \'FROM_END\') {', - ' $at2 = $at2 - $at1;', - ' } else if ($where2 == \'FIRST\') {', - ' $at2 = 0;', - ' } else if ($where2 == \'LAST\') {', - ' $at2 = strlen($text);', - ' } else { $at2 = 0; }', - ' if ($where1 == \'FROM_START\') {', - ' $at1--;', - ' } else if ($where1 == \'FROM_END\') {', - ' $at1 = strlen($text) - $at1;', - ' } else if ($where1 == \'FIRST\') {', - ' $at1 = 0;', - ' } else if ($where1 == \'LAST\') {', - ' $at1 = strlen($text) - 1;', - ' } else { $at1 = 0; }', - ' return substr($text, $at1, $at2);', - '}']); + ' if ($where1 == \'FROM_END\') {', + ' $at1 = strlen($text) - 1 - $at1;', + ' } else if ($where1 == \'FIRST\') {', + ' $at1 = 0;', + ' } else if ($where1 != \'FROM_START\'){', + ' throw new Exception(\'Unhandled option (text_get_substring).\');', + ' }', + ' $length = 0;', + ' if ($where2 == \'FROM_START\') {', + ' $length = $at2 - $at1 + 1;', + ' } else if ($where2 == \'FROM_END\') {', + ' $length = strlen($text) - $at1 - $at2;', + ' } else if ($where2 == \'LAST\') {', + ' $length = strlen($text) - $at1;', + ' } else {', + ' throw new Exception(\'Unhandled option (text_get_substring).\');', + ' }', + ' return substr($text, $at1, $length);', + '}']); var code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -201,19 +197,14 @@ Blockly.PHP['text_getSubstring'] = function(block) { Blockly.PHP['text_changeCase'] = function(block) { // Change capitalization. - var code; + var text = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_NONE) || '\'\''; if (block.getFieldValue('CASE') == 'UPPERCASE') { - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - code = 'strtoupper(' + argument0 + ')'; + var code = 'strtoupper(' + text + ')'; } else if (block.getFieldValue('CASE') == 'LOWERCASE') { - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - code = 'strtolower(' + argument0 + ')'; + var code = 'strtolower(' + text + ')'; } else if (block.getFieldValue('CASE') == 'TITLECASE') { - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - code = 'ucwords(strtolower(' + argument0 + '))'; + var code = 'ucwords(strtolower(' + text + '))'; } return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -226,16 +217,16 @@ Blockly.PHP['text_trim'] = function(block) { 'BOTH': 'trim' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + var text = Blockly.PHP.valueToCode(block, 'TEXT', Blockly.PHP.ORDER_NONE) || '\'\''; - return [ operator + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + return [operator + '(' + text + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + var msg = Blockly.PHP.valueToCode(block, 'TEXT', Blockly.PHP.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ');\n'; + return 'print(' + msg + ');\n'; }; Blockly.PHP['text_prompt_ext'] = function(block) { diff --git a/generators/php/variables.js b/generators/php/variables.js index dd68f4ead..466774b3f 100644 --- a/generators/php/variables.js +++ b/generators/php/variables.js @@ -43,4 +43,4 @@ Blockly.PHP['variables_set'] = function(block) { var varName = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); return varName + ' = ' + argument0 + ';\n'; -}; \ No newline at end of file +}; diff --git a/generators/python.js b/generators/python.js index 082480685..3534aec27 100644 --- a/generators/python.js +++ b/generators/python.js @@ -46,11 +46,22 @@ Blockly.Python.addReservedWords( // import keyword // print ','.join(keyword.kwlist) // http://docs.python.org/reference/lexical_analysis.html#keywords - 'and,as,assert,break,class,continue,def,del,elif,else,except,exec,finally,for,from,global,if,import,in,is,lambda,not,or,pass,print,raise,return,try,while,with,yield,' + + 'and,as,assert,break,class,continue,def,del,elif,else,except,exec,' + + 'finally,for,from,global,if,import,in,is,lambda,not,or,pass,print,raise,' + + 'return,try,while,with,yield,' + //http://docs.python.org/library/constants.html - 'True,False,None,NotImplemented,Ellipsis,__debug__,quit,exit,copyright,license,credits,' + + 'True,False,None,NotImplemented,Ellipsis,__debug__,quit,exit,copyright,' + + 'license,credits,' + // http://docs.python.org/library/functions.html - 'abs,divmod,input,open,staticmethod,all,enumerate,int,ord,str,any,eval,isinstance,pow,sum,basestring,execfile,issubclass,print,super,bin,file,iter,property,tuple,bool,filter,len,range,type,bytearray,float,list,raw_input,unichr,callable,format,locals,reduce,unicode,chr,frozenset,long,reload,vars,classmethod,getattr,map,repr,xrange,cmp,globals,max,reversed,zip,compile,hasattr,memoryview,round,__import__,complex,hash,min,set,apply,delattr,help,next,setattr,buffer,dict,hex,object,slice,coerce,dir,id,oct,sorted,intern'); + 'abs,divmod,input,open,staticmethod,all,enumerate,int,ord,str,any,eval,' + + 'isinstance,pow,sum,basestring,execfile,issubclass,print,super,bin,file,' + + 'iter,property,tuple,bool,filter,len,range,type,bytearray,float,list,' + + 'raw_input,unichr,callable,format,locals,reduce,unicode,chr,frozenset,' + + 'long,reload,vars,classmethod,getattr,map,repr,xrange,cmp,globals,max,' + + 'reversed,zip,compile,hasattr,memoryview,round,__import__,complex,hash,' + + 'min,set,apply,delattr,help,next,setattr,buffer,dict,hex,object,slice,' + + 'coerce,dir,id,oct,sorted,intern' +); /** * Order of operation ENUMs. @@ -79,6 +90,12 @@ Blockly.Python.ORDER_CONDITIONAL = 15; // if else Blockly.Python.ORDER_LAMBDA = 16; // lambda Blockly.Python.ORDER_NONE = 99; // (...) +/** + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. + */ +Blockly.Python.ONE_BASED_INDEXING = true; + /** * List of outer-inner pairings that do NOT require parentheses. * @type {!Array.>} @@ -213,9 +230,9 @@ Blockly.Python.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.Python.allNestedComments(childBlock); if (comment) { @@ -229,3 +246,45 @@ Blockly.Python.scrub_ = function(block, code) { var nextCode = Blockly.Python.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value, taking into account indexing, and + * casts to an integer. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @return {string|number} + */ +Blockly.Python.getAdjustedInt = function(block, atId, opt_delta, opt_negate) { + var delta = opt_delta || 0; + if (Blockly.Python.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.Python.ONE_BASED_INDEXING ? '1' : '0'; + var atOrder = delta ? Blockly.Python.ORDER_ADDITIVE : + Blockly.Python.ORDER_NONE; + var at = Blockly.Python.valueToCode(block, atId, atOrder) || defaultAtIndex; + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseInt(at, 10) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = 'int(' + at + ' + ' + delta + ')'; + } else if (delta < 0) { + at = 'int(' + at + ' - ' + -delta + ')'; + } else { + at = 'int(' + at + ')'; + } + if (opt_negate) { + at = '-' + at; + } + } + return at; +}; + diff --git a/generators/python/colour.js b/generators/python/colour.js index 49505ac86..68666a89b 100644 --- a/generators/python/colour.js +++ b/generators/python/colour.js @@ -46,11 +46,11 @@ Blockly.Python['colour_rgb'] = function(block) { // Compose a colour from RGB components expressed as percentages. var functionName = Blockly.Python.provideFunction_( 'colour_rgb', - [ 'def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b):', - ' r = round(min(100, max(0, r)) * 2.55)', - ' g = round(min(100, max(0, g)) * 2.55)', - ' b = round(min(100, max(0, b)) * 2.55)', - ' return \'#%02x%02x%02x\' % (r, g, b)']); + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b):', + ' r = round(min(100, max(0, r)) * 2.55)', + ' g = round(min(100, max(0, g)) * 2.55)', + ' b = round(min(100, max(0, b)) * 2.55)', + ' return \'#%02x%02x%02x\' % (r, g, b)']); var r = Blockly.Python.valueToCode(block, 'RED', Blockly.Python.ORDER_NONE) || 0; var g = Blockly.Python.valueToCode(block, 'GREEN', diff --git a/generators/python/lists.js b/generators/python/lists.js index e43e31d5e..b831d4d2f 100644 --- a/generators/python/lists.js +++ b/generators/python/lists.js @@ -36,66 +36,75 @@ Blockly.Python['lists_create_empty'] = function(block) { Blockly.Python['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Python.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Python.valueToCode(block, 'ADD' + i, Blockly.Python.ORDER_NONE) || 'None'; } - code = '[' + code.join(', ') + ']'; + var code = '[' + elements.join(', ') + ']'; return [code, Blockly.Python.ORDER_ATOMIC]; }; Blockly.Python['lists_repeat'] = function(block) { // Create a list with one element repeated. - var argument0 = Blockly.Python.valueToCode(block, 'ITEM', + var item = Blockly.Python.valueToCode(block, 'ITEM', Blockly.Python.ORDER_NONE) || 'None'; - var argument1 = Blockly.Python.valueToCode(block, 'NUM', + var times = Blockly.Python.valueToCode(block, 'NUM', Blockly.Python.ORDER_MULTIPLICATIVE) || '0'; - var code = '[' + argument0 + '] * ' + argument1; + var code = '[' + item + '] * ' + times; return [code, Blockly.Python.ORDER_MULTIPLICATIVE]; }; Blockly.Python['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var list = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '[]'; - return ['len(' + argument0 + ')', Blockly.Python.ORDER_FUNCTION_CALL]; + return ['len(' + list + ')', Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var list = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '[]'; - var code = 'not len(' + argument0 + ')'; + var code = 'not len(' + list + ')'; return [code, Blockly.Python.ORDER_LOGICAL_NOT]; }; Blockly.Python['lists_indexOf'] = function(block) { // Find an item in the list. - var argument0 = Blockly.Python.valueToCode(block, 'FIND', + var item = Blockly.Python.valueToCode(block, 'FIND', Blockly.Python.ORDER_NONE) || '[]'; - var argument1 = Blockly.Python.valueToCode(block, 'VALUE', - Blockly.Python.ORDER_MEMBER) || '\'\''; - var code; + var list = Blockly.Python.valueToCode(block, 'VALUE', + Blockly.Python.ORDER_NONE) || '\'\''; + if (Blockly.Python.ONE_BASED_INDEXING) { + var errorIndex = ' 0'; + var firstIndexAdjustment = ' + 1'; + var lastIndexAdjustment = ''; + } else { + var errorIndex = ' -1'; + var firstIndexAdjustment = ''; + var lastIndexAdjustment = ' - 1'; + } if (block.getFieldValue('END') == 'FIRST') { var functionName = Blockly.Python.provideFunction_( 'first_index', - ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList, elem):', - ' try: theIndex = myList.index(elem) + 1', - ' except: theIndex = 0', - ' return theIndex']); - code = functionName + '(' + argument1 + ', ' + argument0 + ')'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else { - var functionName = Blockly.Python.provideFunction_( - 'last_index', - ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList, elem):', - ' try: theIndex = len(myList) - myList[::-1].index(elem)', - ' except: theIndex = 0', - ' return theIndex']); - code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + + '(my_list, elem):', + ' try: index = my_list.index(elem)' + firstIndexAdjustment, + ' except: index =' + errorIndex, + ' return index']); + var code = functionName + '(' + list + ', ' + item + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } + var functionName = Blockly.Python.provideFunction_( + 'last_index', + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(my_list, elem):', + ' try: index = len(my_list) - my_list[::-1].index(elem)' + + lastIndexAdjustment, + ' except: index =' + errorIndex, + ' return index']); + var code = functionName + '(' + list + ', ' + item + ')'; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['lists_getIndex'] = function(block) { @@ -103,85 +112,76 @@ Blockly.Python['lists_getIndex'] = function(block) { // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Python.valueToCode(block, 'AT', - Blockly.Python.ORDER_UNARY_SIGN) || '1'; - var list = Blockly.Python.valueToCode(block, 'VALUE', - Blockly.Python.ORDER_MEMBER) || '[]'; + var listOrder = (where == 'RANDOM') ? Blockly.Python.ORDER_NONE : + Blockly.Python.ORDER_MEMBER; + var list = Blockly.Python.valueToCode(block, 'VALUE', listOrder) || '[]'; - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '[0]'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop(0)'; - if (mode == 'GET_REMOVE') { + switch (where) { + case 'FIRST': + if (mode == 'GET') { + var code = list + '[0]'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop(0)'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop(0)\n'; } - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = list + '[-1]'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop()'; - if (mode == 'GET_REMOVE') { + break; + case 'LAST': + if (mode == 'GET') { + var code = list + '[-1]'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop()'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop()\n'; } - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at = 'int(' + at + ' - 1)'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop(' + at + ')'; - if (mode == 'GET_REMOVE') { + break; + case 'FROM_START': + var at = Blockly.Python.getAdjustedInt(block, 'AT'); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop(' + at + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop(' + at + ')\n'; } - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var code = list + '[-' + at + ']'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop(-' + at + ')'; - if (mode == 'GET_REMOVE') { + break; + case'FROM_END': + var at = Blockly.Python.getAdjustedInt(block, 'AT', 1, true); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop(' + at + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop(' + at + ')\n'; } - } - } else if (where == 'RANDOM') { - Blockly.Python.definitions_['import_random'] = 'import random'; - if (mode == 'GET') { - code = 'random.choice(' + list + ')'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else { - var functionName = Blockly.Python.provideFunction_( - 'lists_remove_random_item', - ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList):', - ' x = int(random.random() * len(myList))', - ' return myList.pop(x)']); - code = functionName + '(' + list + ')'; - if (mode == 'GET_REMOVE') { + break; + case 'RANDOM': + Blockly.Python.definitions_['import_random'] = 'import random'; + if (mode == 'GET') { + code = 'random.choice(' + list + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return code + '\n'; + } else { + var functionName = Blockly.Python.provideFunction_( + 'lists_remove_random_item', + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList):', + ' x = int(random.random() * len(myList))', + ' return myList.pop(x)']); + code = functionName + '(' + list + ')'; + if (mode == 'GET_REMOVE') { + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return code + '\n'; + } } - } + break; } throw 'Unhandled combination (lists_getIndex).'; }; @@ -193,8 +193,6 @@ Blockly.Python['lists_setIndex'] = function(block) { Blockly.Python.ORDER_MEMBER) || '[]'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Python.valueToCode(block, 'AT', - Blockly.Python.ORDER_NONE) || '1'; var value = Blockly.Python.valueToCode(block, 'TO', Blockly.Python.ORDER_NONE) || 'None'; // Cache non-trivial values to variables to prevent repeated look-ups. @@ -209,51 +207,52 @@ Blockly.Python['lists_setIndex'] = function(block) { list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.insert(0, ' + value + ')\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { - return list + '[-1] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.append(' + value + ')\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at = 'int(' + at + ' - 1)'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.insert(' + at + ', ' + value + ')\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'SET') { - return list + '[-' + at + '] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.insert(-' + at + ', ' + value + ')\n'; - } - } else if (where == 'RANDOM') { - Blockly.Python.definitions_['import_random'] = 'import random'; - var code = cacheList(); - var xVar = Blockly.Python.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += xVar + ' = int(random.random() * len(' + list + '))\n'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + '\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.insert(' + xVar + ', ' + value + ')\n'; - return code; - } + + switch (where) { + case 'FIRST': + if (mode == 'SET') { + return list + '[0] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.insert(0, ' + value + ')\n'; + } + break; + case 'LAST': + if (mode == 'SET') { + return list + '[-1] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.append(' + value + ')\n'; + } + break; + case 'FROM_START': + var at = Blockly.Python.getAdjustedInt(block, 'AT'); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.insert(' + at + ', ' + value + ')\n'; + } + break; + case 'FROM_END': + var at = Blockly.Python.getAdjustedInt(block, 'AT', 1, true); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.insert(' + at + ', ' + value + ')\n'; + } + break; + case 'RANDOM': + Blockly.Python.definitions_['import_random'] = 'import random'; + var code = cacheList(); + var xVar = Blockly.Python.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += xVar + ' = int(random.random() * len(' + list + '))\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + '\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.insert(' + xVar + ', ' + value + ')\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; @@ -264,49 +263,42 @@ Blockly.Python['lists_getSublist'] = function(block) { Blockly.Python.ORDER_MEMBER) || '[]'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Python.valueToCode(block, 'AT1', - Blockly.Python.ORDER_ADDITIVE) || '1'; - var at2 = Blockly.Python.valueToCode(block, 'AT2', - Blockly.Python.ORDER_ADDITIVE) || '1'; - if (where1 == 'FIRST' || (where1 == 'FROM_START' && at1 == '1')) { - at1 = ''; - } else if (where1 == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at1)) { - // If the index is a naked number, decrement it right now. - at1 = parseInt(at1, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at1 = 'int(' + at1 + ' - 1)'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at1)) { - at1 = -parseInt(at1, 10); - } else { - at1 = '-int(' + at1 + ')'; - } + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1'); + if (at1 == '0') { + at1 = ''; + } + break; + case 'FROM_END': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1', 1, true); + break; + case 'FIRST': + var at1 = ''; + break; + default: + throw 'Unhandled option (lists_getSublist)'; } - if (where2 == 'LAST' || (where2 == 'FROM_END' && at2 == '1')) { - at2 = ''; - } else if (where1 == 'FROM_START') { - if (Blockly.isNumber(at2)) { - at2 = parseInt(at2, 10); - } else { - at2 = 'int(' + at2 + ')'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at2)) { - // If the index is a naked number, increment it right now. - // Add special case for -0. - at2 = 1 - parseInt(at2, 10); - if (at2 == 0) { + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 0, true); + // Ensure that if the result calculated is 0 that sub-sequence will + // include all elements as expected. + if (!Blockly.isNumber(String(at2))) { + Blockly.Python.definitions_['import_sys'] = 'import sys'; + at2 += ' or sys.maxsize'; + } else if (at2 == '0') { at2 = ''; } - } else { - // If the index is dynamic, increment it in code. - Blockly.Python.definitions_['import_sys'] = 'import sys'; - at2 = 'int(1 - ' + at2 + ') or sys.maxsize'; - } + break; + case 'LAST': + var at2 = ''; + break; + default: + throw 'Unhandled option (lists_getSublist)'; } var code = list + '[' + at1 + ' : ' + at2 + ']'; return [code, Blockly.Python.ORDER_MEMBER]; @@ -314,30 +306,30 @@ Blockly.Python['lists_getSublist'] = function(block) { Blockly.Python['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = (Blockly.Python.valueToCode(block, 'LIST', - Blockly.Python.ORDER_MEMBER) || '[]'); + var list = (Blockly.Python.valueToCode(block, 'LIST', + Blockly.Python.ORDER_NONE) || '[]'); var type = block.getFieldValue('TYPE'); var reverse = block.getFieldValue('DIRECTION') === '1' ? 'False' : 'True'; var sortFunctionName = Blockly.Python.provideFunction_('lists_sort', ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + - '(listv, type, reversev):', - ' def tryfloat(s):', + '(my_list, type, reverse):', + ' def try_float(s):', ' try:', ' return float(s)', ' except:', ' return 0', - ' keyFuncts = {', - ' "NUMERIC": tryfloat,', + ' key_funcs = {', + ' "NUMERIC": try_float,', ' "TEXT": str,', ' "IGNORE_CASE": lambda s: str(s).lower()', ' }', - ' keyv = keyFuncts[type]', - ' tmp_list = list(listv)', // Clone the list. - ' return sorted(tmp_list, key=keyv, reverse=reversev)' + ' key_func = key_funcs[type]', + ' list_cpy = list(my_list)', // Clone the list. + ' return sorted(list_cpy, key=key_func, reverse=reverse)' ]); var code = sortFunctionName + - '(' + listCode + ', "' + type + '", ' + reverse + ')'; + '(' + list + ', "' + type + '", ' + reverse + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; diff --git a/generators/python/procedures.js b/generators/python/procedures.js index daa7386aa..b90f38d5b 100644 --- a/generators/python/procedures.js +++ b/generators/python/procedures.js @@ -65,8 +65,8 @@ Blockly.Python['procedures_defreturn'] = function(block) { branch = Blockly.Python.PASS; } var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Python.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Python.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = 'def ' + funcName + '(' + args.join(', ') + '):\n' + @@ -87,8 +87,8 @@ Blockly.Python['procedures_callreturn'] = function(block) { var funcName = Blockly.Python.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Python.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Python.valueToCode(block, 'ARG' + i, Blockly.Python.ORDER_NONE) || 'None'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -100,8 +100,8 @@ Blockly.Python['procedures_callnoreturn'] = function(block) { var funcName = Blockly.Python.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Python.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Python.valueToCode(block, 'ARG' + i, Blockly.Python.ORDER_NONE) || 'None'; } var code = funcName + '(' + args.join(', ') + ')\n'; diff --git a/generators/python/text.js b/generators/python/text.js index 3ae0ffc8e..84e19872a 100644 --- a/generators/python/text.js +++ b/generators/python/text.js @@ -38,32 +38,35 @@ Blockly.Python['text'] = function(block) { Blockly.Python['text_join'] = function(block) { // Create a string made up of any number of elements of any type. //Should we allow joining by '-' or ',' or any other characters? - var code; - if (block.itemCount_ == 0) { - return ['\'\'', Blockly.Python.ORDER_ATOMIC]; - } else if (block.itemCount_ == 1) { - var argument0 = Blockly.Python.valueToCode(block, 'ADD0', - Blockly.Python.ORDER_NONE) || '\'\''; - code = 'str(' + argument0 + ')'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else if (block.itemCount_ == 2) { - var argument0 = Blockly.Python.valueToCode(block, 'ADD0', - Blockly.Python.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Python.valueToCode(block, 'ADD1', - Blockly.Python.ORDER_NONE) || '\'\''; - var code = 'str(' + argument0 + ') + str(' + argument1 + ')'; - return [code, Blockly.Python.ORDER_UNARY_SIGN]; - } else { - var code = []; - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Python.valueToCode(block, 'ADD' + n, - Blockly.Python.ORDER_NONE) || '\'\''; - } - var tempVar = Blockly.Python.variableDB_.getDistinctName('temp_value', - Blockly.Variables.NAME_TYPE); - code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' + - code.join(', ') + ']])'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; + switch (block.itemCount_) { + case 0: + return ['\'\'', Blockly.Python.ORDER_ATOMIC]; + break; + case 1: + var element = Blockly.Python.valueToCode(block, 'ADD0', + Blockly.Python.ORDER_NONE) || '\'\''; + var code = 'str(' + element + ')'; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; + break; + case 2: + var element0 = Blockly.Python.valueToCode(block, 'ADD0', + Blockly.Python.ORDER_NONE) || '\'\''; + var element1 = Blockly.Python.valueToCode(block, 'ADD1', + Blockly.Python.ORDER_NONE) || '\'\''; + var code = 'str(' + element0 + ') + str(' + element1 + ')'; + return [code, Blockly.Python.ORDER_ADDITIVE]; + break; + default: + var elements = []; + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Python.valueToCode(block, 'ADD' + i, + Blockly.Python.ORDER_NONE) || '\'\''; + } + var tempVar = Blockly.Python.variableDB_.getDistinctName('x', + Blockly.Variables.NAME_TYPE); + var code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' + + elements.join(', ') + ']])'; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } }; @@ -71,23 +74,23 @@ Blockly.Python['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.Python.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var value = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_NONE) || '\'\''; - return varName + ' = str(' + varName + ') + str(' + argument0 + ')\n'; + return varName + ' = str(' + varName + ') + str(' + value + ')\n'; }; Blockly.Python['text_length'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '\'\''; - return ['len(' + argument0 + ')', Blockly.Python.ORDER_FUNCTION_CALL]; + return ['len(' + text + ')', Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '\'\''; - var code = 'not len(' + argument0 + ')'; + var code = 'not len(' + text + ')'; return [code, Blockly.Python.ORDER_LOGICAL_NOT]; }; @@ -95,20 +98,21 @@ Blockly.Python['text_indexOf'] = function(block) { // Search the text for a substring. // Should we allow for non-case sensitive??? var operator = block.getFieldValue('END') == 'FIRST' ? 'find' : 'rfind'; - var argument0 = Blockly.Python.valueToCode(block, 'FIND', + var substring = Blockly.Python.valueToCode(block, 'FIND', Blockly.Python.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Python.valueToCode(block, 'VALUE', + var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_MEMBER) || '\'\''; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Python.ORDER_ADDITIVE]; + var code = text + '.' + operator + '(' + substring + ')'; + if (Blockly.Python.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.Python.ORDER_ADDITIVE]; + } + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Python.valueToCode(block, 'AT', - Blockly.Python.ORDER_UNARY_SIGN) || '1'; var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_MEMBER) || '\'\''; switch (where) { @@ -119,18 +123,12 @@ Blockly.Python['text_charAt'] = function(block) { var code = text + '[-1]'; return [code, Blockly.Python.ORDER_MEMBER]; case 'FROM_START': - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at = 'int(' + at + ' - 1)'; - } + var at = Blockly.Python.getAdjustedInt(block, 'AT'); var code = text + '[' + at + ']'; return [code, Blockly.Python.ORDER_MEMBER]; case 'FROM_END': - var code = text + '[-' + at + ']'; + var at = Blockly.Python.getAdjustedInt(block, 'AT', 1, true); + var code = text + '[' + at + ']'; return [code, Blockly.Python.ORDER_MEMBER]; case 'RANDOM': Blockly.Python.definitions_['import_random'] = 'import random'; @@ -147,53 +145,46 @@ Blockly.Python['text_charAt'] = function(block) { Blockly.Python['text_getSubstring'] = function(block) { // Get substring. - var text = Blockly.Python.valueToCode(block, 'STRING', - Blockly.Python.ORDER_MEMBER) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Python.valueToCode(block, 'AT1', - Blockly.Python.ORDER_ADDITIVE) || '1'; - var at2 = Blockly.Python.valueToCode(block, 'AT2', - Blockly.Python.ORDER_ADDITIVE) || '1'; - if (where1 == 'FIRST' || (where1 == 'FROM_START' && at1 == '1')) { - at1 = ''; - } else if (where1 == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at1)) { - // If the index is a naked number, decrement it right now. - at1 = parseInt(at1, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at1 = 'int(' + at1 + ' - 1)'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at1)) { - at1 = -parseInt(at1, 10); - } else { - at1 = '-int(' + at1 + ')'; - } + var text = Blockly.Python.valueToCode(block, 'STRING', + Blockly.Python.ORDER_MEMBER) || '\'\''; + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1'); + if (at1 == '0') { + at1 = ''; + } + break; + case 'FROM_END': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1', 1, true); + break; + case 'FIRST': + var at1 = ''; + break; + default: + throw 'Unhandled option (text_getSubstring)'; } - if (where2 == 'LAST' || (where2 == 'FROM_END' && at2 == '1')) { - at2 = ''; - } else if (where1 == 'FROM_START') { - if (Blockly.isNumber(at2)) { - at2 = parseInt(at2, 10); - } else { - at2 = 'int(' + at2 + ')'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at2)) { - // If the index is a naked number, increment it right now. - at2 = 1 - parseInt(at2, 10); - if (at2 == 0) { + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 0, true); + // Ensure that if the result calculated is 0 that sub-sequence will + // include all elements as expected. + if (!Blockly.isNumber(String(at2))) { + Blockly.Python.definitions_['import_sys'] = 'import sys'; + at2 += ' or sys.maxsize'; + } else if (at2 == '0') { at2 = ''; } - } else { - // If the index is dynamic, increment it in code. - // Add special case for -0. - Blockly.Python.definitions_['import_sys'] = 'import sys'; - at2 = 'int(1 - ' + at2 + ') or sys.maxsize'; - } + break; + case 'LAST': + var at2 = ''; + break; + default: + throw 'Unhandled option (text_getSubstring)'; } var code = text + '[' + at1 + ' : ' + at2 + ']'; return [code, Blockly.Python.ORDER_MEMBER]; @@ -207,10 +198,10 @@ Blockly.Python['text_changeCase'] = function(block) { 'TITLECASE': '.title()' }; var operator = OPERATORS[block.getFieldValue('CASE')]; - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var text = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_MEMBER) || '\'\''; - var code = argument0 + operator; - return [code, Blockly.Python.ORDER_MEMBER]; + var code = text + operator; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_trim'] = function(block) { @@ -221,17 +212,17 @@ Blockly.Python['text_trim'] = function(block) { 'BOTH': '.strip()' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var text = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_MEMBER) || '\'\''; - var code = argument0 + operator; - return [code, Blockly.Python.ORDER_MEMBER]; + var code = text + operator; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var msg = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ')\n'; + return 'print(' + msg + ')\n'; }; Blockly.Python['text_prompt_ext'] = function(block) { diff --git a/javascript_compressed.js b/javascript_compressed.js index ad00ad4be..aaf4a1560 100644 --- a/javascript_compressed.js +++ b/javascript_compressed.js @@ -11,28 +11,32 @@ Blockly.JavaScript.ORDER_OVERRIDES=[[Blockly.JavaScript.ORDER_FUNCTION_CALL,Bloc Blockly.JavaScript.ORDER_ADDITION],[Blockly.JavaScript.ORDER_LOGICAL_AND,Blockly.JavaScript.ORDER_LOGICAL_AND],[Blockly.JavaScript.ORDER_LOGICAL_OR,Blockly.JavaScript.ORDER_LOGICAL_OR]];Blockly.JavaScript.ONE_BASED_INDEXING=!0; Blockly.JavaScript.init=function(a){Blockly.JavaScript.definitions_=Object.create(null);Blockly.JavaScript.functionNames_=Object.create(null);Blockly.JavaScript.variableDB_?Blockly.JavaScript.variableDB_.reset():Blockly.JavaScript.variableDB_=new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);var b=[];a=Blockly.Variables.allVariables(a);if(a.length){for(var c=0;cc?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_SUBTRACTION)||f:d?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_UNARY_NEGATION)||f:Blockly.JavaScript.valueToCode(a,b,e)||f;if(Blockly.isNumber(a))a=parseFloat(a)+ +c,d&&(a=-a);else{if(0c&&(a=a+" - "+-c,g=Blockly.JavaScript.ORDER_SUBTRACTION);d&&(a=c?"-("+a+")":"-"+a,g=Blockly.JavaScript.ORDER_UNARY_NEGATION);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.JavaScript.colour={};Blockly.JavaScript.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.colour_random=function(a){return[Blockly.JavaScript.provideFunction_("colourRandom",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"() {"," var num = Math.floor(Math.random() * Math.pow(2, 24));"," return '#' + ('00000' + num.toString(16)).substr(-6);","}"])+"()",Blockly.JavaScript.ORDER_FUNCTION_CALL]}; Blockly.JavaScript.colour_rgb=function(a){var b=Blockly.JavaScript.valueToCode(a,"RED",Blockly.JavaScript.ORDER_COMMA)||0,c=Blockly.JavaScript.valueToCode(a,"GREEN",Blockly.JavaScript.ORDER_COMMA)||0;a=Blockly.JavaScript.valueToCode(a,"BLUE",Blockly.JavaScript.ORDER_COMMA)||0;return[Blockly.JavaScript.provideFunction_("colourRgb",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(r, g, b) {"," r = Math.max(Math.min(Number(r), 100), 0) * 2.55;"," g = Math.max(Math.min(Number(g), 100), 0) * 2.55;", " b = Math.max(Math.min(Number(b), 100), 0) * 2.55;"," r = ('0' + (Math.round(r) || 0).toString(16)).slice(-2);"," g = ('0' + (Math.round(g) || 0).toString(16)).slice(-2);"," b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2);"," return '#' + r + g + b;","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]}; Blockly.JavaScript.colour_blend=function(a){var b=Blockly.JavaScript.valueToCode(a,"COLOUR1",Blockly.JavaScript.ORDER_COMMA)||"'#000000'",c=Blockly.JavaScript.valueToCode(a,"COLOUR2",Blockly.JavaScript.ORDER_COMMA)||"'#000000'";a=Blockly.JavaScript.valueToCode(a,"RATIO",Blockly.JavaScript.ORDER_COMMA)||.5;return[Blockly.JavaScript.provideFunction_("colourBlend",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(c1, c2, ratio) {"," ratio = Math.max(Math.min(Number(ratio), 1), 0);"," var r1 = parseInt(c1.substring(1, 3), 16);", " var g1 = parseInt(c1.substring(3, 5), 16);"," var b1 = parseInt(c1.substring(5, 7), 16);"," var r2 = parseInt(c2.substring(1, 3), 16);"," var g2 = parseInt(c2.substring(3, 5), 16);"," var b2 = parseInt(c2.substring(5, 7), 16);"," var r = Math.round(r1 * (1 - ratio) + r2 * ratio);"," var g = Math.round(g1 * (1 - ratio) + g2 * ratio);"," var b = Math.round(b1 * (1 - ratio) + b2 * ratio);"," r = ('0' + (r || 0).toString(16)).slice(-2);"," g = ('0' + (g || 0).toString(16)).slice(-2);"," b = ('0' + (b || 0).toString(16)).slice(-2);", -" return '#' + r + g + b;","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.lists={};Blockly.JavaScript.lists_create_empty=function(a){return["[]",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c b.toString() ? 1 : -1; },", -' "IGNORE_CASE": function(a, b) {'," return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; },"," };"," var compare = compareFuncs[type];"," return function(a, b) { return compare(a, b) * direction; }","}"]);return["("+b+").slice().sort("+d+'("'+a+'", '+c+"))",Blockly.JavaScript.ORDER_FUNCTION_CALL]}; +' "IGNORE_CASE": function(a, b) {'," return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; },"," };"," var compare = compareFuncs[type];"," return function(a, b) { return compare(a, b) * direction; }","}"]);return[b+".slice().sort("+d+'("'+a+'", '+c+"))",Blockly.JavaScript.ORDER_FUNCTION_CALL]}; Blockly.JavaScript.lists_split=function(a){var b=Blockly.JavaScript.valueToCode(a,"INPUT",Blockly.JavaScript.ORDER_MEMBER),c=Blockly.JavaScript.valueToCode(a,"DELIM",Blockly.JavaScript.ORDER_NONE)||"''";a=a.getFieldValue("MODE");if("SPLIT"==a)b||(b="''"),a="split";else if("JOIN"==a)b||(b="[]"),a="join";else throw"Unknown mode: "+a;return[b+"."+a+"("+c+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.logic={}; Blockly.JavaScript.controls_if=function(a){for(var b=0,c=Blockly.JavaScript.valueToCode(a,"IF"+b,Blockly.JavaScript.ORDER_NONE)||"false",d=Blockly.JavaScript.statementToCode(a,"DO"+b),e="if ("+c+") {\n"+d+"}",b=1;b<=a.elseifCount_;b++)c=Blockly.JavaScript.valueToCode(a,"IF"+b,Blockly.JavaScript.ORDER_NONE)||"false",d=Blockly.JavaScript.statementToCode(a,"DO"+b),e+=" else if ("+c+") {\n"+d+"}";a.elseCount_&&(d=Blockly.JavaScript.statementToCode(a,"ELSE"),e+=" else {\n"+d+"}");return e+"\n"}; Blockly.JavaScript.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.JavaScript.ORDER_EQUALITY:Blockly.JavaScript.ORDER_RELATIONAL,d=Blockly.JavaScript.valueToCode(a,"A",c)||"0";a=Blockly.JavaScript.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]}; @@ -71,16 +75,17 @@ Blockly.JavaScript.procedures_defreturn=function(a){var b=Blockly.JavaScript.var Blockly.JavaScript.procedures_callreturn=function(a){for(var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE),c=[],d=0;d",GTE:">="}[a.getFieldValue("OP")],c=Blockly.Lua.valueToCode(a,"A",Blockly.Lua.ORDER_RELATIONAL)||"0";a=Blockly.Lua.valueToCode(a,"B",Blockly.Lua.ORDER_RELATIONAL)||"0";return[c+" "+b+" "+a,Blockly.Lua.ORDER_RELATIONAL]}; Blockly.Lua.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"and":"or",c="and"==b?Blockly.Lua.ORDER_AND:Blockly.Lua.ORDER_OR,d=Blockly.Lua.valueToCode(a,"A",c);a=Blockly.Lua.valueToCode(a,"B",c);if(d||a){var e="and"==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+" "+b+" "+a,c]};Blockly.Lua.logic_negate=function(a){return["not "+(Blockly.Lua.valueToCode(a,"BOOL",Blockly.Lua.ORDER_UNARY)||"true"),Blockly.Lua.ORDER_UNARY]}; Blockly.Lua.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"true":"false",Blockly.Lua.ORDER_ATOMIC]};Blockly.Lua.logic_null=function(a){return["nil",Blockly.Lua.ORDER_ATOMIC]};Blockly.Lua.logic_ternary=function(a){var b=Blockly.Lua.valueToCode(a,"IF",Blockly.Lua.ORDER_AND)||"false",c=Blockly.Lua.valueToCode(a,"THEN",Blockly.Lua.ORDER_AND)||"nil";a=Blockly.Lua.valueToCode(a,"ELSE",Blockly.Lua.ORDER_OR)||"nil";return[b+" and "+c+" or "+a,Blockly.Lua.ORDER_OR]};Blockly.Lua.loops={};Blockly.Lua.CONTINUE_STATEMENT="goto continue\n";Blockly.Lua.addContinueLabel=function(a){return-1c?Blockly.PHP.valueToCode(a,b,Blockly.PHP.ORDER_SUBTRACTION)||g:d?Blockly.PHP.valueToCode(a,b,Blockly.PHP.ORDER_UNARY_NEGATION)||g:Blockly.PHP.valueToCode(a,b,e)||g;if(Blockly.isNumber(a))a=parseFloat(a)+c,d&&(a=-a);else{if(0c&& +(a=a+" - "+-c,f=Blockly.PHP.ORDER_SUBTRACTION);d&&(a=c?"-("+a+")":"-"+a,f=Blockly.PHP.ORDER_UNARY_NEGATION);f=Math.floor(f);e=Math.floor(e);f&&e>=f&&(a="("+a+")")}return a};Blockly.PHP.colour={};Blockly.PHP.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.PHP.ORDER_ATOMIC]};Blockly.PHP.colour_random=function(a){return[Blockly.PHP.provideFunction_("colour_random",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"() {"," return '#' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT);","}"])+"()",Blockly.PHP.ORDER_FUNCTION_CALL]}; +Blockly.PHP.colour_rgb=function(a){var b=Blockly.PHP.valueToCode(a,"RED",Blockly.PHP.ORDER_COMMA)||0,c=Blockly.PHP.valueToCode(a,"GREEN",Blockly.PHP.ORDER_COMMA)||0;a=Blockly.PHP.valueToCode(a,"BLUE",Blockly.PHP.ORDER_COMMA)||0;return[Blockly.PHP.provideFunction_("colour_rgb",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($r, $g, $b) {"," $r = round(max(min($r, 100), 0) * 2.55);"," $g = round(max(min($g, 100), 0) * 2.55);"," $b = round(max(min($b, 100), 0) * 2.55);"," $hex = '#';"," $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT);", +" $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT);"," $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT);"," return $hex;","}"])+"("+b+", "+c+", "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; Blockly.PHP.colour_blend=function(a){var b=Blockly.PHP.valueToCode(a,"COLOUR1",Blockly.PHP.ORDER_COMMA)||"'#000000'",c=Blockly.PHP.valueToCode(a,"COLOUR2",Blockly.PHP.ORDER_COMMA)||"'#000000'";a=Blockly.PHP.valueToCode(a,"RATIO",Blockly.PHP.ORDER_COMMA)||.5;return[Blockly.PHP.provideFunction_("colour_blend",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($c1, $c2, $ratio) {"," $ratio = max(min($ratio, 1), 0);"," $r1 = hexdec(substr($c1, 1, 2));"," $g1 = hexdec(substr($c1, 3, 2));"," $b1 = hexdec(substr($c1, 5, 2));", -" $r2 = hexdec(substr($c2, 1, 2));"," $g2 = hexdec(substr($c2, 3, 2));"," $b2 = hexdec(substr($c2, 5, 2));"," $r = round($r1 * (1 - $ratio) + $r2 * $ratio);"," $g = round($g1 * (1 - $ratio) + $g2 * $ratio);"," $b = round($b1 * (1 - $ratio) + $b2 * $ratio);",' $hex = "#";',' $hex .= str_pad(dechex($r), 2, "0", STR_PAD_LEFT);',' $hex .= str_pad(dechex($g), 2, "0", STR_PAD_LEFT);',' $hex .= str_pad(dechex($b), 2, "0", STR_PAD_LEFT);'," return $hex;","}"])+"("+b+", "+c+", "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.lists={};Blockly.PHP.lists_create_empty=function(a){return["array()",Blockly.PHP.ORDER_ATOMIC]};Blockly.PHP.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c "strnatcasecmp",',' "TEXT" => "strcmp",',' "IGNORE_CASE" => "strcasecmp"'," );"," $sortCmp = $sortCmpFuncs[$type];"," $list2 = $list;", -" usort($list2, $sortCmp);"," if ($direction == -1) {"," $list2 = array_reverse($list2);"," }"," return $list2;","}"])+"("+b+', "'+a+'", '+c+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; -Blockly.PHP.lists_split=function(a){var b=Blockly.PHP.valueToCode(a,"INPUT",Blockly.PHP.ORDER_MEMBER),c=Blockly.PHP.valueToCode(a,"DELIM",Blockly.PHP.ORDER_NONE)||"''";a=a.getFieldValue("MODE");if("SPLIT"==a)b||(b="''"),a="explode";else if("JOIN"==a)b||(b="array()"),a="implode";else throw"Unknown mode: "+a;return[a+"("+c+", "+b+")",Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.logic={};Blockly.PHP.controls_if=function(a){for(var b=0,c=Blockly.PHP.valueToCode(a,"IF"+b,Blockly.PHP.ORDER_NONE)||"false",d=Blockly.PHP.statementToCode(a,"DO"+b),e="if ("+c+") {\n"+d+"}",b=1;b<=a.elseifCount_;b++)c=Blockly.PHP.valueToCode(a,"IF"+b,Blockly.PHP.ORDER_NONE)||"false",d=Blockly.PHP.statementToCode(a,"DO"+b),e+=" else if ("+c+") {\n"+d+"}";a.elseCount_&&(d=Blockly.PHP.statementToCode(a,"ELSE"),e+=" else {\n"+d+"}");return e+"\n"}; +Blockly.PHP.lists_indexOf=function(a){var b=Blockly.PHP.valueToCode(a,"FIND",Blockly.PHP.ORDER_NONE)||"''",c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_MEMBER)||"[]";if(Blockly.PHP.ONE_BASED_INDEXING)var d=" 0",e=" + 1";else d=" -1",e="";return[("FIRST"==a.getFieldValue("END")?Blockly.PHP.provideFunction_("indexOf",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($haystack, $needle) {"," for ($index = 0; $index < count($haystack); $index++) {"," if ($haystack[$index] == $needle) return $index"+ +e+";"," }"," return "+d+";","}"]):Blockly.PHP.provideFunction_("lastIndexOf",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($haystack, $needle) {"," $last = "+d+";"," for ($index = 0; $index < count($haystack); $index++) {"," if ($haystack[$index] == $needle) $last = $index"+e+";"," }"," return $last;","}"]))+"("+c+", "+b+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; +Blockly.PHP.lists_getIndex=function(a){var b=a.getFieldValue("MODE")||"GET";switch(a.getFieldValue("WHERE")||"FROM_START"){case "FIRST":if("GET"==b){var c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_MEMBER)||"array()";return[c+"[0]",Blockly.PHP.ORDER_MEMBER]}if("GET_REMOVE"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_NONE)||"array()",["array_shift("+c+")",Blockly.PHP.ORDER_FUNCTION_CALL];if("REMOVE"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_NONE)|| +"array()","array_shift("+c+");\n";break;case "LAST":if("GET"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_NONE)||"array()",["end("+c+")",Blockly.PHP.ORDER_FUNCTION_CALL];if("GET_REMOVE"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_NONE)||"array()",["array_pop("+c+")",Blockly.PHP.ORDER_FUNCTION_CALL];if("REMOVE"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_NONE)||"array()","array_pop("+c+");\n";break;case "FROM_START":var d=Blockly.PHP.getAdjusted(a, +"AT");if("GET"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_MEMBER)||"array()",[c+"["+d+"]",Blockly.PHP.ORDER_MEMBER];if("GET_REMOVE"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_COMMA)||"array()",["array_splice("+c+", "+d+", 1)[0]",Blockly.PHP.ORDER_FUNCTION_CALL];if("REMOVE"==b)return c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_COMMA)||"array()","array_splice("+c+", "+d+", 1);\n";break;case "FROM_END":if("GET"==b)return c=Blockly.PHP.valueToCode(a, +"VALUE",Blockly.PHP.ORDER_COMMA)||"array()",d=Blockly.PHP.getAdjusted(a,"AT",1,!0),["array_slice("+c+", "+d+", 1)[0]",Blockly.PHP.ORDER_FUNCTION_CALL];if("GET_REMOVE"==b||"REMOVE"==b){c=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_NONE)||"array()";d=Blockly.PHP.getAdjusted(a,"AT",1,!1,Blockly.PHP.ORDER_SUBTRACTION);c="array_splice("+c+", count("+c+") - "+d+", 1)[0]";if("GET_REMOVE"==b)return[c,Blockly.PHP.ORDER_FUNCTION_CALL];if("REMOVE"==b)return c+";\n"}break;case "RANDOM":c=Blockly.PHP.valueToCode(a, +"VALUE",Blockly.PHP.ORDER_NONE)||"array()";if("GET"==b)return b=Blockly.PHP.provideFunction_("lists_get_random_item",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($list) {"," return $list[rand(0,count($list)-1)];","}"]),[b+"("+c+")",Blockly.PHP.ORDER_FUNCTION_CALL];if("GET_REMOVE"==b)return b=Blockly.PHP.provideFunction_("lists_get_remove_random_item",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"(&$list) {"," $x = rand(0,count($list)-1);"," unset($list[$x]);"," return array_values($list);", +"}"]),[b+"("+c+")",Blockly.PHP.ORDER_FUNCTION_CALL];if("REMOVE"==b)return b=Blockly.PHP.provideFunction_("lists_remove_random_item",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"(&$list) {"," unset($list[rand(0,count($list)-1)]);","}"]),b+"("+c+");\n"}throw"Unhandled combination (lists_getIndex).";}; +Blockly.PHP.lists_setIndex=function(a){var b=a.getFieldValue("MODE")||"GET",c=a.getFieldValue("WHERE")||"FROM_START",d=Blockly.PHP.valueToCode(a,"TO",Blockly.PHP.ORDER_ASSIGNMENT)||"null";switch(c){case "FIRST":if("SET"==b)return c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_MEMBER)||"array()",c+"[0] = "+d+";\n";if("INSERT"==b)return c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_COMMA)||"array()","array_unshift("+c+", "+d+");\n";break;case "LAST":c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_COMMA)|| +"array()";if("SET"==b)return b=Blockly.PHP.provideFunction_("lists_set_last_item",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"(&$list, $value) {"," $list[count($list) - 1] = $value;","}"]),b+"("+c+", "+d+");\n";if("INSERT"==b)return"array_push("+c+", "+d+");\n";break;case "FROM_START":var e=Blockly.PHP.getAdjusted(a,"AT");if("SET"==b)return c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_MEMBER)||"array()",c+"["+e+"] = "+d+";\n";if("INSERT"==b)return c=Blockly.PHP.valueToCode(a,"LIST", +Blockly.PHP.ORDER_COMMA)||"array()","array_splice("+c+", "+e+", 0, "+d+");\n";break;case "FROM_END":c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_COMMA)||"array()";e=Blockly.PHP.getAdjusted(a,"AT",1);if("SET"==b)return b=Blockly.PHP.provideFunction_("lists_set_from_end",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"(&$list, $at, $value) {"," $list[count($list) - $at] = $value;","}"]),b+"("+c+", "+e+", "+d+");\n";if("INSERT"==b)return b=Blockly.PHP.provideFunction_("lists_insert_from_end", +["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"(&$list, $at, $value) {"," return array_splice($list, count($list) - $at, 0, $value);","}"]),b+"("+c+", "+e+", "+d+");\n";break;case "RANDOM":c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_REFERENCE)||"array()";c.match(/^\$\w+$/)?a="":(a=Blockly.PHP.variableDB_.getDistinctName("tmp_list",Blockly.Variables.NAME_TYPE),e=a+" = &"+c+";\n",c=a,a=e);e=Blockly.PHP.variableDB_.getDistinctName("tmp_x",Blockly.Variables.NAME_TYPE);a+=e+" = rand(0, count("+ +c+")-1);\n";if("SET"==b)return a+(c+"["+e+"] = "+d+";\n");if("INSERT"==b)return a+("array_splice("+c+", "+e+", 0, "+d+");\n")}throw"Unhandled combination (lists_setIndex).";}; +Blockly.PHP.lists_getSublist=function(a){var b=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_COMMA)||"array()",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"!=c||"LAST"!=d)if(b.match(/^\$\w+$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":var e=Blockly.PHP.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.PHP.getAdjusted(a,"AT1",1,!1,Blockly.PHP.ORDER_SUBTRACTION);e="count("+b+") - "+e;break;case "FIRST":e="0";break;default:throw"Unhandled option (lists_getSublist)."; +}switch(d){case "FROM_START":a=Blockly.PHP.getAdjusted(a,"AT2",0,!1,Blockly.PHP.ORDER_SUBTRACTION);c=a+" - ";c=Blockly.isNumber(String(e))||String(e).match(/^\(.+\)$/)?c+e:c+("("+e+")");c+=" + 1";break;case "FROM_END":a=Blockly.PHP.getAdjusted(a,"AT2",0,!1,Blockly.PHP.ORDER_SUBTRACTION);c="count("+b+") - "+a+" - ";c=Blockly.isNumber(String(e))||String(e).match(/^\(.+\)$/)?c+e:c+("("+e+")");break;case "LAST":c="count("+b+") - ";c=Blockly.isNumber(String(e))||String(e).match(/^\(.+\)$/)?c+e:c+("("+ +e+")");break;default:throw"Unhandled option (lists_getSublist).";}b="array_slice("+b+", "+e+", "+c+")"}else e=Blockly.PHP.getAdjusted(a,"AT1"),a=Blockly.PHP.getAdjusted(a,"AT2"),b=Blockly.PHP.provideFunction_("lists_get_sublist",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($list, $where1, $at1, $where2, $at2) {"," if ($where1 == 'FROM_END') {"," $at1 = count($list) - 1 - $at1;"," } else if ($where1 == 'FIRST') {"," $at1 = 0;"," } else if ($where1 != 'FROM_START'){"," throw new Exception('Unhandled option (lists_get_sublist).');", +" }"," $length = 0;"," if ($where2 == 'FROM_START') {"," $length = $at2 - $at1 + 1;"," } else if ($where2 == 'FROM_END') {"," $length = count($list) - $at1 - $at2;"," } else if ($where2 == 'LAST') {"," $length = count($list) - $at1;"," } else {"," throw new Exception('Unhandled option (lists_get_sublist).');"," }"," return array_slice($list, $at1, $length);","}"])+"("+b+", '"+c+"', "+e+", '"+d+"', "+a+")";return[b,Blockly.PHP.ORDER_FUNCTION_CALL]}; +Blockly.PHP.lists_sort=function(a){var b=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_COMMA)||"array()",c="1"===a.getFieldValue("DIRECTION")?1:-1;a=a.getFieldValue("TYPE");return[Blockly.PHP.provideFunction_("lists_sort",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($list, $type, $direction) {"," $sortCmpFuncs = array(",' "NUMERIC" => "strnatcasecmp",',' "TEXT" => "strcmp",',' "IGNORE_CASE" => "strcasecmp"'," );"," $sortCmp = $sortCmpFuncs[$type];"," $list2 = $list;"," usort($list2, $sortCmp);", +" if ($direction == -1) {"," $list2 = array_reverse($list2);"," }"," return $list2;","}"])+"("+b+', "'+a+'", '+c+")",Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.lists_split=function(a){var b=Blockly.PHP.valueToCode(a,"INPUT",Blockly.PHP.ORDER_COMMA),c=Blockly.PHP.valueToCode(a,"DELIM",Blockly.PHP.ORDER_COMMA)||"''";a=a.getFieldValue("MODE");if("SPLIT"==a)b||(b="''"),a="explode";else if("JOIN"==a)b||(b="array()"),a="implode";else throw"Unknown mode: "+a;return[a+"("+c+", "+b+")",Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.logic={};Blockly.PHP.controls_if=function(a){for(var b=0,c=Blockly.PHP.valueToCode(a,"IF"+b,Blockly.PHP.ORDER_NONE)||"false",d=Blockly.PHP.statementToCode(a,"DO"+b),e="if ("+c+") {\n"+d+"}",b=1;b<=a.elseifCount_;b++)c=Blockly.PHP.valueToCode(a,"IF"+b,Blockly.PHP.ORDER_NONE)||"false",d=Blockly.PHP.statementToCode(a,"DO"+b),e+=" else if ("+c+") {\n"+d+"}";a.elseCount_&&(d=Blockly.PHP.statementToCode(a,"ELSE"),e+=" else {\n"+d+"}");return e+"\n"}; Blockly.PHP.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.PHP.ORDER_EQUALITY:Blockly.PHP.ORDER_RELATIONAL,d=Blockly.PHP.valueToCode(a,"A",c)||"0";a=Blockly.PHP.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]}; Blockly.PHP.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"&&":"||",c="&&"==b?Blockly.PHP.ORDER_LOGICAL_AND:Blockly.PHP.ORDER_LOGICAL_OR,d=Blockly.PHP.valueToCode(a,"A",c);a=Blockly.PHP.valueToCode(a,"B",c);if(d||a){var e="&&"==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+" "+b+" "+a,c]};Blockly.PHP.logic_negate=function(a){var b=Blockly.PHP.ORDER_LOGICAL_NOT;return["!"+(Blockly.PHP.valueToCode(a,"BOOL",b)||"true"),b]}; Blockly.PHP.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"true":"false",Blockly.PHP.ORDER_ATOMIC]};Blockly.PHP.logic_null=function(a){return["null",Blockly.PHP.ORDER_ATOMIC]};Blockly.PHP.logic_ternary=function(a){var b=Blockly.PHP.valueToCode(a,"IF",Blockly.PHP.ORDER_CONDITIONAL)||"false",c=Blockly.PHP.valueToCode(a,"THEN",Blockly.PHP.ORDER_CONDITIONAL)||"null";a=Blockly.PHP.valueToCode(a,"ELSE",Blockly.PHP.ORDER_CONDITIONAL)||"null";return[b+" ? "+c+" : "+a,Blockly.PHP.ORDER_CONDITIONAL]};Blockly.PHP.loops={}; @@ -44,7 +50,7 @@ a="for ("+b+" = "+c+"; "+b+(f?" <= ":" >= ")+d+"; "+b;b=Math.abs(parseFloat(e)); a+=d+" = ",a=Blockly.isNumber(e)?a+(Math.abs(e)+";\n"):a+("abs("+e+");\n"),a=a+("if ("+f+" > "+c+") {\n")+(Blockly.PHP.INDENT+d+" = -"+d+";\n"),a+="}\n",a+="for ("+b+" = "+f+"; "+d+" >= 0 ? "+b+" <= "+c+" : "+b+" >= "+c+"; "+b+" += "+d+") {\n"+g+"}\n";return a}; Blockly.PHP.controls_forEach=function(a){var b=Blockly.PHP.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_ASSIGNMENT)||"[]",d=Blockly.PHP.statementToCode(a,"DO"),d=Blockly.PHP.addLoopTrap(d,a.id);return""+("foreach ("+c+" as "+b+") {\n"+d+"}\n")}; Blockly.PHP.controls_flow_statements=function(a){switch(a.getFieldValue("FLOW")){case "BREAK":return"break;\n";case "CONTINUE":return"continue;\n"}throw"Unknown flow statement.";};Blockly.PHP.math={};Blockly.PHP.math_number=function(a){a=parseFloat(a.getFieldValue("NUM"));Infinity==a?a="INF":-Infinity==a&&(a="-INF");return[a,Blockly.PHP.ORDER_ATOMIC]}; -Blockly.PHP.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.PHP.ORDER_ADDITION],MINUS:[" - ",Blockly.PHP.ORDER_SUBTRACTION],MULTIPLY:[" * ",Blockly.PHP.ORDER_MULTIPLICATION],DIVIDE:[" / ",Blockly.PHP.ORDER_DIVISION],POWER:[null,Blockly.PHP.ORDER_COMMA]}[a.getFieldValue("OP")],c=b[0],b=b[1],d=Blockly.PHP.valueToCode(a,"A",b)||"0";a=Blockly.PHP.valueToCode(a,"B",b)||"0";return c?[d+c+a,b]:["pow("+d+", "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; +Blockly.PHP.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.PHP.ORDER_ADDITION],MINUS:[" - ",Blockly.PHP.ORDER_SUBTRACTION],MULTIPLY:[" * ",Blockly.PHP.ORDER_MULTIPLICATION],DIVIDE:[" / ",Blockly.PHP.ORDER_DIVISION],POWER:[" ** ",Blockly.PHP.ORDER_POWER]}[a.getFieldValue("OP")],c=b[0],b=b[1],d=Blockly.PHP.valueToCode(a,"A",b)||"0";a=Blockly.PHP.valueToCode(a,"B",b)||"0";return[d+c+a,b]}; Blockly.PHP.math_single=function(a){var b=a.getFieldValue("OP"),c;if("NEG"==b)return a=Blockly.PHP.valueToCode(a,"NUM",Blockly.PHP.ORDER_UNARY_NEGATION)||"0","-"==a[0]&&(a=" "+a),["-"+a,Blockly.PHP.ORDER_UNARY_NEGATION];a="SIN"==b||"COS"==b||"TAN"==b?Blockly.PHP.valueToCode(a,"NUM",Blockly.PHP.ORDER_DIVISION)||"0":Blockly.PHP.valueToCode(a,"NUM",Blockly.PHP.ORDER_NONE)||"0";switch(b){case "ABS":c="abs("+a+")";break;case "ROOT":c="sqrt("+a+")";break;case "LN":c="log("+a+")";break;case "EXP":c="exp("+ a+")";break;case "POW10":c="pow(10,"+a+")";break;case "ROUND":c="round("+a+")";break;case "ROUNDUP":c="ceil("+a+")";break;case "ROUNDDOWN":c="floor("+a+")";break;case "SIN":c="sin("+a+" / 180 * pi())";break;case "COS":c="cos("+a+" / 180 * pi())";break;case "TAN":c="tan("+a+" / 180 * pi())"}if(c)return[c,Blockly.PHP.ORDER_FUNCTION_CALL];switch(b){case "LOG10":c="log("+a+") / log(10)";break;case "ASIN":c="asin("+a+") / pi() * 180";break;case "ACOS":c="acos("+a+") / pi() * 180";break;case "ATAN":c="atan("+ a+") / pi() * 180";break;default:throw"Unknown math operator: "+b;}return[c,Blockly.PHP.ORDER_DIVISION]};Blockly.PHP.math_constant=function(a){return{PI:["M_PI",Blockly.PHP.ORDER_ATOMIC],E:["M_E",Blockly.PHP.ORDER_ATOMIC],GOLDEN_RATIO:["(1 + sqrt(5)) / 2",Blockly.PHP.ORDER_DIVISION],SQRT2:["M_SQRT2",Blockly.PHP.ORDER_ATOMIC],SQRT1_2:["M_SQRT1_2",Blockly.PHP.ORDER_ATOMIC],INFINITY:["INF",Blockly.PHP.ORDER_ATOMIC]}[a.getFieldValue("CONSTANT")]}; @@ -53,28 +59,26 @@ Blockly.PHP.math_number_property=function(a){var b=Blockly.PHP.valueToCode(a,"NU b+" > 0";break;case "NEGATIVE":d=b+" < 0";break;case "DIVISIBLE_BY":a=Blockly.PHP.valueToCode(a,"DIVISOR",Blockly.PHP.ORDER_MODULUS)||"0",d=b+" % "+a+" == 0"}return[d,Blockly.PHP.ORDER_EQUALITY]};Blockly.PHP.math_change=function(a){var b=Blockly.PHP.valueToCode(a,"DELTA",Blockly.PHP.ORDER_ADDITION)||"0";return Blockly.PHP.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE)+" += "+b+";\n"};Blockly.PHP.math_round=Blockly.PHP.math_single;Blockly.PHP.math_trig=Blockly.PHP.math_single; Blockly.PHP.math_on_list=function(a){var b=a.getFieldValue("OP");switch(b){case "SUM":a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_FUNCTION_CALL)||"array()";a="array_sum("+a+")";break;case "MIN":a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_FUNCTION_CALL)||"array()";a="min("+a+")";break;case "MAX":a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_FUNCTION_CALL)||"array()";a="max("+a+")";break;case "AVERAGE":b=Blockly.PHP.provideFunction_("math_mean",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+ "($myList) {"," return array_sum($myList) / count($myList);","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"array()";a=b+"("+a+")";break;case "MEDIAN":b=Blockly.PHP.provideFunction_("math_median",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($arr) {"," sort($arr,SORT_NUMERIC);"," return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : "," ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2) - 1]) / 2;","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)|| -"[]";a=b+"("+a+")";break;case "MODE":b=Blockly.PHP.provideFunction_("math_modes",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($values) {"," $v = array_count_values($values);"," arsort($v);"," foreach($v as $k => $v){$total = $k; break;}"," return array($total);","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "STD_DEV":b=Blockly.PHP.provideFunction_("math_standard_deviation",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($numbers) {", -" $n = count($numbers);"," if (!$n) return null;"," $mean = array_sum($numbers) / count($numbers);"," foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2);"," return sqrt(array_sum($devs) / (count($devs) - 1));","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "RANDOM":b=Blockly.PHP.provideFunction_("math_random_list",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($list) {"," $x = rand(0, count($list)-1);"," return $list[$x];", -"}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;default:throw"Unknown operator: "+b;}return[a,Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.math_modulo=function(a){var b=Blockly.PHP.valueToCode(a,"DIVIDEND",Blockly.PHP.ORDER_MODULUS)||"0";a=Blockly.PHP.valueToCode(a,"DIVISOR",Blockly.PHP.ORDER_MODULUS)||"0";return[b+" % "+a,Blockly.PHP.ORDER_MODULUS]}; +"[]";a=b+"("+a+")";break;case "MODE":b=Blockly.PHP.provideFunction_("math_modes",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($values) {"," if (empty($values)) return array();"," $counts = array_count_values($values);"," arsort($counts); // Sort counts in descending order"," $modes = array_keys($counts, current($counts), true);"," return $modes;","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "STD_DEV":b=Blockly.PHP.provideFunction_("math_standard_deviation", +["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($numbers) {"," $n = count($numbers);"," if (!$n) return null;"," $mean = array_sum($numbers) / count($numbers);"," foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2);"," return sqrt(array_sum($devs) / (count($devs) - 1));","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "RANDOM":b=Blockly.PHP.provideFunction_("math_random_list",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+ +"($list) {"," $x = rand(0, count($list)-1);"," return $list[$x];","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;default:throw"Unknown operator: "+b;}return[a,Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.math_modulo=function(a){var b=Blockly.PHP.valueToCode(a,"DIVIDEND",Blockly.PHP.ORDER_MODULUS)||"0";a=Blockly.PHP.valueToCode(a,"DIVISOR",Blockly.PHP.ORDER_MODULUS)||"0";return[b+" % "+a,Blockly.PHP.ORDER_MODULUS]}; Blockly.PHP.math_constrain=function(a){var b=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_COMMA)||"0",c=Blockly.PHP.valueToCode(a,"LOW",Blockly.PHP.ORDER_COMMA)||"0";a=Blockly.PHP.valueToCode(a,"HIGH",Blockly.PHP.ORDER_COMMA)||"Infinity";return["min(max("+b+", "+c+"), "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; Blockly.PHP.math_random_int=function(a){var b=Blockly.PHP.valueToCode(a,"FROM",Blockly.PHP.ORDER_COMMA)||"0";a=Blockly.PHP.valueToCode(a,"TO",Blockly.PHP.ORDER_COMMA)||"0";return[Blockly.PHP.provideFunction_("math_random_int",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($a, $b) {"," if ($a > $b) {"," return rand($b, $a);"," }"," return rand($a, $b);","}"])+"("+b+", "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; Blockly.PHP.math_random_float=function(a){return["(float)rand()/(float)getrandmax()",Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.procedures={}; -Blockly.PHP.procedures_defreturn=function(a){for(var b=Blockly.Variables.allVariables(a),c=b.length-1;0<=c;c--){var d=b[c];-1==a.arguments_.indexOf(d)?b[c]=Blockly.PHP.variableDB_.getName(d,Blockly.Variables.NAME_TYPE):b.splice(c,1)}b=b.length?" global "+b.join(", ")+";\n":"";c=Blockly.PHP.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE);d=Blockly.PHP.statementToCode(a,"STACK");Blockly.PHP.STATEMENT_PREFIX&&(d=Blockly.PHP.prefixLines(Blockly.PHP.STATEMENT_PREFIX.replace(/%1/g,"'"+ -a.id+"'"),Blockly.PHP.INDENT)+d);Blockly.PHP.INFINITE_LOOP_TRAP&&(d=Blockly.PHP.INFINITE_LOOP_TRAP.replace(/%1/g,"'"+a.id+"'")+d);var e=Blockly.PHP.valueToCode(a,"RETURN",Blockly.PHP.ORDER_NONE)||"";e&&(e=" return "+e+";\n");for(var g=[],f=0;fc?"int("+a+" - "+-c+")":"int("+a+")",d&&(a="-"+a));return a};Blockly.Python.colour={};Blockly.Python.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.colour_random=function(a){Blockly.Python.definitions_.import_random="import random";return["'#%06x' % random.randint(0, 2**24 - 1)",Blockly.Python.ORDER_FUNCTION_CALL]}; Blockly.Python.colour_rgb=function(a){var b=Blockly.Python.provideFunction_("colour_rgb",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(r, g, b):"," r = round(min(100, max(0, r)) * 2.55)"," g = round(min(100, max(0, g)) * 2.55)"," b = round(min(100, max(0, b)) * 2.55)"," return '#%02x%02x%02x' % (r, g, b)"]),c=Blockly.Python.valueToCode(a,"RED",Blockly.Python.ORDER_NONE)||0,d=Blockly.Python.valueToCode(a,"GREEN",Blockly.Python.ORDER_NONE)||0;a=Blockly.Python.valueToCode(a,"BLUE",Blockly.Python.ORDER_NONE)|| 0;return[b+"("+c+", "+d+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]}; Blockly.Python.colour_blend=function(a){var b=Blockly.Python.provideFunction_("colour_blend",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(colour1, colour2, ratio):"," r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16)"," g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16)"," b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16)"," ratio = min(1, max(0, ratio))"," r = round(r1 * (1 - ratio) + r2 * ratio)"," g = round(g1 * (1 - ratio) + g2 * ratio)"," b = round(b1 * (1 - ratio) + b2 * ratio)", -" return '#%02x%02x%02x' % (r, g, b)"]),c=Blockly.Python.valueToCode(a,"COLOUR1",Blockly.Python.ORDER_NONE)||"'#000000'",d=Blockly.Python.valueToCode(a,"COLOUR2",Blockly.Python.ORDER_NONE)||"'#000000'";a=Blockly.Python.valueToCode(a,"RATIO",Blockly.Python.ORDER_NONE)||0;return[b+"("+c+", "+d+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.lists={};Blockly.Python.lists_create_empty=function(a){return["[]",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c",GTE:">="}[a.getFieldValue("OP")],c=Blockly.Python.ORDER_RELATIONAL,d=Blockly.Python.valueToCode(a,"A",c)||"0";a=Blockly.Python.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]}; @@ -38,10 +38,10 @@ Blockly.Python.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"a Blockly.Python.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"True":"False",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.logic_null=function(a){return["None",Blockly.Python.ORDER_ATOMIC]}; Blockly.Python.logic_ternary=function(a){var b=Blockly.Python.valueToCode(a,"IF",Blockly.Python.ORDER_CONDITIONAL)||"False",c=Blockly.Python.valueToCode(a,"THEN",Blockly.Python.ORDER_CONDITIONAL)||"None";a=Blockly.Python.valueToCode(a,"ELSE",Blockly.Python.ORDER_CONDITIONAL)||"None";return[c+" if "+b+" else "+a,Blockly.Python.ORDER_CONDITIONAL]};Blockly.Python.loops={};Blockly.Python.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(parseInt(a.getFieldValue("TIMES"),10)):Blockly.Python.valueToCode(a,"TIMES",Blockly.Python.ORDER_NONE)||"0",b=Blockly.isNumber(b)?parseInt(b,10):"int("+b+")",c=Blockly.Python.statementToCode(a,"DO"),c=Blockly.Python.addLoopTrap(c,a.id)||Blockly.Python.PASS;return"for "+Blockly.Python.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE)+" in range("+b+"):\n"+c}; Blockly.Python.controls_repeat=Blockly.Python.controls_repeat_ext;Blockly.Python.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.Python.valueToCode(a,"BOOL",b?Blockly.Python.ORDER_LOGICAL_NOT:Blockly.Python.ORDER_NONE)||"False",d=Blockly.Python.statementToCode(a,"DO"),d=Blockly.Python.addLoopTrap(d,a.id)||Blockly.Python.PASS;b&&(c="not "+c);return"while "+c+":\n"+d}; -Blockly.Python.controls_for=function(a){var b=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0",d=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0",e=Blockly.Python.valueToCode(a,"BY",Blockly.Python.ORDER_NONE)||"1",g=Blockly.Python.statementToCode(a,"DO"),g=Blockly.Python.addLoopTrap(g,a.id)||Blockly.Python.PASS,f="",h=function(){return Blockly.Python.provideFunction_("upRange", +Blockly.Python.controls_for=function(a){var b=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0",d=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0",e=Blockly.Python.valueToCode(a,"BY",Blockly.Python.ORDER_NONE)||"1",f=Blockly.Python.statementToCode(a,"DO"),f=Blockly.Python.addLoopTrap(f,a.id)||Blockly.Python.PASS,g="",h=function(){return Blockly.Python.provideFunction_("upRange", ["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(start, stop, step):"," while start <= stop:"," yield start"," start += abs(step)"])},k=function(){return Blockly.Python.provideFunction_("downRange",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(start, stop, step):"," while start >= stop:"," yield start"," start -= abs(step)"])};a=function(a,b,c){return"("+a+" <= "+b+") and "+h()+"("+a+", "+b+", "+c+") or "+k()+"("+a+", "+b+", "+c+")"};if(Blockly.isNumber(c)&&Blockly.isNumber(d)&& -Blockly.isNumber(e))c=parseFloat(c),d=parseFloat(d),e=Math.abs(parseFloat(e)),0===c%1&&0===d%1&&0===e%1?(c<=d?(d++,a=0==c&&1==e?d:c+", "+d,1!=e&&(a+=", "+e)):(d--,a=c+", "+d+", -"+e),a="range("+a+")"):(a=ca?Blockly.Python.ORDER_UNARY_SIGN:Blockly.Python.ORDER_ATOMIC;return[a,b]}; Blockly.Python.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.Python.ORDER_ADDITIVE],MINUS:[" - ",Blockly.Python.ORDER_ADDITIVE],MULTIPLY:[" * ",Blockly.Python.ORDER_MULTIPLICATIVE],DIVIDE:[" / ",Blockly.Python.ORDER_MULTIPLICATIVE],POWER:[" ** ",Blockly.Python.ORDER_EXPONENTIATION]}[a.getFieldValue("OP")],c=b[0],b=b[1],d=Blockly.Python.valueToCode(a,"A",b)||"0";a=Blockly.Python.valueToCode(a,"B",b)||"0";return[d+c+a,b]}; Blockly.Python.math_single=function(a){var b=a.getFieldValue("OP"),c;if("NEG"==b)return c=Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_UNARY_SIGN)||"0",["-"+c,Blockly.Python.ORDER_UNARY_SIGN];Blockly.Python.definitions_.import_math="import math";a="SIN"==b||"COS"==b||"TAN"==b?Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_MULTIPLICATIVE)||"0":Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_NONE)||"0";switch(b){case "ABS":c="math.fabs("+a+")";break;case "ROOT":c="math.sqrt("+ @@ -60,20 +60,20 @@ Blockly.Python.math_on_list=function(a){var b=a.getFieldValue("OP");a=Blockly.Py Blockly.Python.math_modulo=function(a){var b=Blockly.Python.valueToCode(a,"DIVIDEND",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";a=Blockly.Python.valueToCode(a,"DIVISOR",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";return[b+" % "+a,Blockly.Python.ORDER_MULTIPLICATIVE]}; Blockly.Python.math_constrain=function(a){var b=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"0",c=Blockly.Python.valueToCode(a,"LOW",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"HIGH",Blockly.Python.ORDER_NONE)||"float('inf')";return["min(max("+b+", "+c+"), "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]}; Blockly.Python.math_random_int=function(a){Blockly.Python.definitions_.import_random="import random";var b=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0";return["random.randint("+b+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.math_random_float=function(a){Blockly.Python.definitions_.import_random="import random";return["random.random()",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.procedures={}; -Blockly.Python.procedures_defreturn=function(a){for(var b=Blockly.Variables.allVariables(a),c=b.length-1;0<=c;c--){var d=b[c];-1==a.arguments_.indexOf(d)?b[c]=Blockly.Python.variableDB_.getName(d,Blockly.Variables.NAME_TYPE):b.splice(c,1)}b=b.length?" global "+b.join(", ")+"\n":"";c=Blockly.Python.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE);d=Blockly.Python.statementToCode(a,"STACK");Blockly.Python.STATEMENT_PREFIX&&(d=Blockly.Python.prefixLines(Blockly.Python.STATEMENT_PREFIX.replace(/%1/g,"'"+ -a.id+"'"),Blockly.Python.INDENT)+d);Blockly.Python.INFINITE_LOOP_TRAP&&(d=Blockly.Python.INFINITE_LOOP_TRAP.replace(/%1/g,'"'+a.id+'"')+d);var e=Blockly.Python.valueToCode(a,"RETURN",Blockly.Python.ORDER_NONE)||"";e?e=" return "+e+"\n":d||(d=Blockly.Python.PASS);for(var g=[],f=0;ftest colour picker - static colour + + + static colour + + #ff6600 @@ -21,7 +25,11 @@ test rgb - from rgb + + + from rgb + + @@ -88,7 +96,11 @@ - test name + + + test name + + @@ -105,7 +117,11 @@ - test name + + + test name + + @@ -137,7 +153,11 @@ - test name + + + test name + + TRUE @@ -192,7 +212,11 @@ test blend - blend + + + blend + + diff --git a/tests/generators/functions.xml b/tests/generators/functions.xml index ce450b11a..b189ed355 100644 --- a/tests/generators/functions.xml +++ b/tests/generators/functions.xml @@ -8,7 +8,11 @@ - test recurse + + + test recurse + + @@ -53,7 +57,11 @@ - procedure with global + + + procedure with global + + proc z @@ -84,7 +92,11 @@ - procedure no return + + + procedure no return + + TRUE @@ -111,7 +123,11 @@ - procedure return + + + procedure return + + FALSE @@ -192,7 +208,11 @@ test function - function with arguments + + + function with arguments + + @@ -218,7 +238,11 @@ - function with side effect + + + function with side effect + + func z @@ -247,7 +271,11 @@ - function with global + + + function with global + + @@ -267,7 +295,11 @@ - function with scope + + + function with scope + + func a @@ -280,7 +312,11 @@ - function return + + + function return + + TRUE @@ -296,7 +332,11 @@ - function no return + + + function no return + + FALSE diff --git a/tests/generators/index.html b/tests/generators/index.html index 5ae75e553..a5003ca51 100644 --- a/tests/generators/index.html +++ b/tests/generators/index.html @@ -153,17 +153,23 @@ function toXml() { } function toJavaScript() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.JavaScript.ONE_BASED_INDEXING = oneBasedIndexing; var code = '\'use strict\';\n\n' code += Blockly.JavaScript.workspaceToCode(workspace); setOutput(code); } function toPython() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.Python.ONE_BASED_INDEXING = oneBasedIndexing; var code = Blockly.Python.workspaceToCode(workspace); setOutput(code); } function toPhp() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.PHP.ONE_BASED_INDEXING = oneBasedIndexing; var code = Blockly.PHP.workspaceToCode(workspace); setOutput(code); } @@ -174,6 +180,8 @@ function toLua() { } function toDart() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.Dart.ONE_BASED_INDEXING = oneBasedIndexing; var code = Blockly.Dart.workspaceToCode(workspace); setOutput(code); } @@ -212,9 +220,22 @@ h1 {