From c8290c24070d5c4df2cb2bb842b29292a5922dd5 Mon Sep 17 00:00:00 2001 From: Ken Date: Fri, 25 Aug 2017 15:16:44 +0800 Subject: [PATCH 0001/2135] Add pen transparency blocks change pen transparency by (num) set pen transparency to (num) --- blocks_vertical/pen.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/blocks_vertical/pen.js b/blocks_vertical/pen.js index 1a6e5cf135..97e44b1d0c 100644 --- a/blocks_vertical/pen.js +++ b/blocks_vertical/pen.js @@ -223,3 +223,43 @@ Blockly.Blocks['pen_setpensizeto'] = { }); } }; + +Blockly.Blocks['pen_changepentransparencyby'] = { + /** + * Block to change the pen's transparency by the value. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": "change pen transparency by %1", + "args0": [ + { + "type": "input_value", + "name": "TRANSPARENCY" + } + ], + "category": Blockly.Categories.pen, + "extensions": ["colours_pen", "shape_statement"] + }); + } +}; + +Blockly.Blocks['pen_setpentransparencyto'] = { + /** + * Block to set the pen's transparency to the value. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": "set pen transparency to %1", + "args0": [ + { + "type": "input_value", + "name": "TRANSPARENCY" + } + ], + "category": Blockly.Categories.pen, + "extensions": ["colours_pen", "shape_statement"] + }); + } +}; From 708ed5d2cd4686b0c98ea4d9067d02b6bb8b3004 Mon Sep 17 00:00:00 2001 From: Ken Date: Fri, 25 Aug 2017 15:21:12 +0800 Subject: [PATCH 0002/2135] Add pen transparency blocks to palette change pen transparency by (num=10) set pen transparency to (num=50) --- blocks_vertical/default_toolbox.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/blocks_vertical/default_toolbox.js b/blocks_vertical/default_toolbox.js index a9499e9381..d1bdbb8d79 100644 --- a/blocks_vertical/default_toolbox.js +++ b/blocks_vertical/default_toolbox.js @@ -351,6 +351,20 @@ Blockly.Blocks.defaultToolbox = ''+ + ''+ + ''+ + '10'+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + '50'+ + ''+ + ''+ + ''+ ''+ '' + '' + From 3e18337d26422ced77960187855f99f307ffd61e Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Wed, 23 Aug 2017 13:23:20 -0400 Subject: [PATCH 0003/2135] Concatenate all the blocks --- core/toolbox.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/toolbox.js b/core/toolbox.js index 56c1bd9d94..95de6ae2c7 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -179,9 +179,22 @@ Blockly.Toolbox.prototype.createFlyout_ = function() { */ Blockly.Toolbox.prototype.populate_ = function(newTree) { this.categoryMenu_.populate(newTree); + this.showAll(); this.setSelectedItem(this.categoryMenu_.categories_[0]); }; +/** + * Show all blocks for all categories in the flyout + */ +Blockly.Toolbox.prototype.showAll = function() { + var allContents = []; + for (var i=0; i Date: Wed, 23 Aug 2017 16:58:05 -0400 Subject: [PATCH 0004/2135] Auto-scroll the flyout to the selected category --- core/flyout_vertical.js | 31 +++++++++++++++++++++++++++++++ core/toolbox.js | 16 ++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index 097e416ac9..3eb537fd28 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -37,6 +37,7 @@ goog.require('goog.dom'); goog.require('goog.events'); goog.require('goog.math.Rect'); goog.require('goog.userAgent'); +goog.require('goog.dom.animationFrame.polyfill'); /** @@ -331,12 +332,42 @@ Blockly.VerticalFlyout.prototype.scrollToStart = function() { this.scrollbar_.set(0); }; +/** + * Scroll the flyout to a position. + * @param {Number} pos The targeted scroll position. + */ +Blockly.VerticalFlyout.prototype.scrollTo = function(pos) { + this.scrollTarget = pos * this.workspace_.scale; + this.step(); +}; + +/** + * Step the scrolling animation by scrolling a fraction of the way to + * a scroll target, and request the next frame if necessary. + */ +Blockly.VerticalFlyout.prototype.step = function() { + if (!this.scrollTarget) return; + var scrollYPos = -this.workspace_.scrollY; + var diff = this.scrollTarget - scrollYPos; + if (Math.abs(diff) < 1) { + this.scrollbar_.set(this.scrollTarget); + return; + } + this.scrollbar_.set(scrollYPos + diff * 0.3); + + // Polyfilled by goog.dom.animationFrame.polyfill + requestAnimationFrame(this.step.bind(this)); +}; + /** * Scroll the flyout. * @param {!Event} e Mouse wheel scroll event. * @private */ Blockly.VerticalFlyout.prototype.wheel_ = function(e) { + // remove scrollTarget to stop auto scrolling in step() + this.scrollTarget = null; + var delta = e.deltaY; if (delta) { diff --git a/core/toolbox.js b/core/toolbox.js index 95de6ae2c7..664fdb8cfc 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -325,18 +325,22 @@ Blockly.Toolbox.prototype.getSelectedItem = function() { */ Blockly.Toolbox.prototype.setSelectedItem = function(item) { if (this.selectedItem_) { - // Don't do anything if they selected the already-open category. - if (this.selectedItem_ == item) { - return; - } // They selected a different category but one was already open. Close it. this.selectedItem_.setSelected(false); } this.selectedItem_ = item; if (this.selectedItem_ != null) { this.selectedItem_.setSelected(true); - this.flyout_.show(item.getContents()); - this.flyout_.scrollToStart(); + // Scroll flyout to the top of the selected category + var categoryName = item.name_; + for (var i=0; i Date: Wed, 23 Aug 2017 17:00:14 -0400 Subject: [PATCH 0005/2135] Add category labels with underlines to flyout --- blocks_vertical/default_toolbox.js | 9 +++++++++ core/flyout_button.js | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/blocks_vertical/default_toolbox.js b/blocks_vertical/default_toolbox.js index a9499e9381..b348adb8b7 100644 --- a/blocks_vertical/default_toolbox.js +++ b/blocks_vertical/default_toolbox.js @@ -30,6 +30,7 @@ goog.require('Blockly.Blocks'); Blockly.Blocks.defaultToolbox = ''; + try { + Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); + workspace.getBlockById('test_1').procCode_ = 'test_procedure'; + var rootBlock = workspace.getBlockById('test_1'); + assertTrue(Blockly.Procedures.deleteProcedureDefCallback('test_procedure', + rootBlock)); + // The other two blocks should stick around. + assertEquals(2, workspace.getTopBlocks().length); + } + finally { + procedureTest_tearDown(); + } +} + +function test_deleteProcedure_recursiveCaller() { + // If there is a caller but it's a part of stack starting with definitionRoot, + // the stack should be deleted. + + procedureTest_setUp(); + + var xml = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + try { + Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); + workspace.getBlockById('test_3').procCode_ = 'test_procedure'; + var rootBlock = workspace.getBlockById('test_1'); + assertTrue(Blockly.Procedures.deleteProcedureDefCallback('test_procedure', + rootBlock)); + assertEquals(0, workspace.getTopBlocks().length); + } + finally { + procedureTest_tearDown(); + } +} + +function test_deleteProcedure_nonRecursiveCaller() { + // If there is a caller and it's not part of the procedure definition, the + // stack should not be deleted. + + procedureTest_setUp(); + var xml = '' + + '' + + '' + + '' + + ''; + try { + Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace); + workspace.getBlockById('test_1').procCode_ = 'test_procedure'; + var rootBlock = workspace.getBlockById('test_2'); + assertFalse(Blockly.Procedures.deleteProcedureDefCallback('test_procedure', + rootBlock)); + // All blocks should stay on the workspace. + assertEquals(3, workspace.getTopBlocks().length); + } + finally { + procedureTest_tearDown(); + } +} From 1796a85bd838264612eaaac138f8afb357465af9 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 18 Oct 2017 17:24:02 -0700 Subject: [PATCH 0137/2135] Move connection to the correct location --- core/block_render_svg_vertical.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js index b3b342f030..b42cedee79 100644 --- a/core/block_render_svg_vertical.js +++ b/core/block_render_svg_vertical.js @@ -1534,10 +1534,11 @@ Blockly.BlockSvg.prototype.renderDefineBlock_ = function(steps, inputRows, // Draw the top and the right corner of the hat. steps.push('H', rightSide); steps.push(Blockly.BlockSvg.TOP_RIGHT_CORNER_DEFINE_HAT); - // row.height will be used to update the cursor in the calling function. row.height += 3 * Blockly.BlockSvg.GRID_UNIT; // Draw the right side of the block around the statement input. steps.push('v', row.height); + // row.height will be used to update the cursor in the calling function. + row.height += Blockly.BlockSvg.GRID_UNIT; }; From d9d3a6063141c7006299b5d9671dcbb8bedc4448 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Oct 2017 10:31:45 -0400 Subject: [PATCH 0138/2135] Revert "Only pick random colors with full saturation and brightness" --- blocks_common/colour.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/blocks_common/colour.js b/blocks_common/colour.js index f3542260f1..30be7523ac 100644 --- a/blocks_common/colour.js +++ b/blocks_common/colour.js @@ -26,18 +26,17 @@ goog.provide('Blockly.Blocks.colour'); -goog.require('goog.color'); - goog.require('Blockly.Blocks'); goog.require('Blockly.constants'); /** - * Pick a random colour that has full saturation and brightness. + * Pick a random colour. * @return {string} #RRGGBB for random colour. */ function randomColour() { - return goog.color.hsvToHex(360 * Math.random(), 1, 255); + var num = Math.floor(Math.random() * Math.pow(2, 24)); + return '#' + ('00000' + num.toString(16)).substr(-6); } Blockly.Blocks['colour_picker'] = { From a119cca83bf8f50e0102ba76fae111ca8b36233f Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Mon, 31 Jul 2017 17:07:41 +0100 Subject: [PATCH 0139/2135] Compatibility for Closure Compiler. (#1240) --- build.py | 25 ------------------------- core/block_svg.js | 6 ++---- core/extensions.js | 21 +++++++++++---------- core/trashcan.js | 5 +++-- core/utils.js | 2 +- core/workspace_svg.js | 1 - core/xml.js | 2 +- i18n/create_messages.py | 6 +++--- 8 files changed, 21 insertions(+), 47 deletions(-) diff --git a/build.py b/build.py index 770cbc78e3..e53c4cfb0f 100755 --- a/build.py +++ b/build.py @@ -337,31 +337,6 @@ def file_lookup(name): code = HEADER + "\n" + json_data["compiledCode"] code = code.replace(remove, "") - - # Trim down Google's Apache licences. - # The Closure Compiler used to preserve these until August 2015. - # Delete this in a few months if the licences don't return. - LICENSE = re.compile("""/\\* - - [\w ]+ - - (Copyright \\d+ Google Inc.) - https://developers.google.com/blockly/ - - Licensed under the Apache License, Version 2.0 \(the "License"\); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -\\*/""") - code = re.sub(LICENSE, r"\n// \1 Apache License 2.0", code) - stats = json_data["statistics"] original_b = stats["originalSize"] compressed_b = stats["compressedSize"] diff --git a/core/block_svg.js b/core/block_svg.js index 75ceaec352..3b1e040ac0 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -997,10 +997,8 @@ Blockly.BlockSvg.disposeUiStep_ = function(clone, rtl, start, workspaceScale) { var scale = (1 - percent) * workspaceScale; clone.setAttribute('transform', 'translate(' + x + ',' + y + ')' + ' scale(' + scale + ')'); - var closure = function() { - Blockly.BlockSvg.disposeUiStep_(clone, rtl, start, workspaceScale); - }; - setTimeout(closure, 10); + setTimeout(Blockly.BlockSvg.disposeUiStep_, 10, clone, rtl, start, + workspaceScale); } }; diff --git a/core/extensions.js b/core/extensions.js index f42640273b..1f88d6d507 100644 --- a/core/extensions.js +++ b/core/extensions.js @@ -60,7 +60,7 @@ Blockly.Extensions.MUTATOR_PROPERTIES_ = * handlers and mutators. These are applied using Block.applyExtension(), or * the JSON "extensions" array attribute. * @param {string} name The name of this extension. - * @param {function} initFn The function to initialize an extended block. + * @param {Function} initFn The function to initialize an extended block. * @throws {Error} if the extension name is empty, the extension is already * registered, or extensionFn is not a function. */ @@ -97,7 +97,7 @@ Blockly.Extensions.registerMixin = function(name, mixinObj) { * decompose are defined on the mixin. * @param {string} name The name of this mutator extension. * @param {!Object} mixinObj The values to mix in. - * @param {function()=} opt_helperFn An optional function to apply after mixing + * @param {(function())=} opt_helperFn An optional function to apply after mixing * in the object. * @param {Array.=} opt_blockList A list of blocks to appear in the * flyout of the mutator dialog. @@ -108,8 +108,10 @@ Blockly.Extensions.registerMutator = function(name, mixinObj, opt_helperFn, var errorPrefix = 'Error when registering mutator "' + name + '": '; // Sanity check the mixin object before registering it. - Blockly.Extensions.checkHasFunction_(errorPrefix, mixinObj, 'domToMutation'); - Blockly.Extensions.checkHasFunction_(errorPrefix, mixinObj, 'mutationToDom'); + Blockly.Extensions.checkHasFunction_(errorPrefix, mixinObj.domToMutation, + 'domToMutation'); + Blockly.Extensions.checkHasFunction_(errorPrefix, mixinObj.mutationToDom, + 'mutationToDom'); var hasMutatorDialog = Blockly.Extensions.checkMutatorDialog_(mixinObj, errorPrefix); @@ -167,20 +169,19 @@ Blockly.Extensions.apply = function(name, block, isMutator) { }; /** - * Check that the given object has a property with the given name, and that the - * property is a function. + * Check that the given value is a function. * @param {string} errorPrefix The string to prepend to any error message. - * @param {!Object} object The object to check. + * @param {*} func Function to check. * @param {string} propertyName Which property to check. * @throws {Error} if the property does not exist or is not a function. * @private */ -Blockly.Extensions.checkHasFunction_ = function(errorPrefix, object, +Blockly.Extensions.checkHasFunction_ = function(errorPrefix, func, propertyName) { - if (!object.hasOwnProperty(propertyName)) { + if (!func) { throw new Error(errorPrefix + 'missing required property "' + propertyName + '"'); - } else if (typeof object[propertyName] !== "function") { + } else if (typeof func != 'function') { throw new Error(errorPrefix + '" required property "' + propertyName + '" must be a function'); } diff --git a/core/trashcan.js b/core/trashcan.js index 33f9fa5e9c..c5aa411a8a 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -165,8 +165,9 @@ Blockly.Trashcan.prototype.createDom = function() { */ this.svgGroup_ = Blockly.utils.createSvgElement('g', {'class': 'blocklyTrash'}, null); + var clip; var rnd = String(Math.random()).substring(2); - var clip = Blockly.utils.createSvgElement('clipPath', + clip = Blockly.utils.createSvgElement('clipPath', {'id': 'blocklyTrashBodyClipPath' + rnd}, this.svgGroup_); Blockly.utils.createSvgElement('rect', @@ -181,7 +182,7 @@ Blockly.Trashcan.prototype.createDom = function() { body.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', this.workspace_.options.pathToMedia + Blockly.SPRITE.url); - var clip = Blockly.utils.createSvgElement('clipPath', + clip = Blockly.utils.createSvgElement('clipPath', {'id': 'blocklyTrashLidClipPath' + rnd}, this.svgGroup_); Blockly.utils.createSvgElement('rect', diff --git a/core/utils.js b/core/utils.js index 18ab7cd501..a32380acf0 100644 --- a/core/utils.js +++ b/core/utils.js @@ -197,7 +197,7 @@ Blockly.utils.getInjectionDivXY_ = function(element) { var scale = 1; while (element) { var xy = Blockly.utils.getRelativeXY(element); - var scale = Blockly.utils.getScale_(element); + scale = Blockly.utils.getScale_(element); x = (x * scale) + xy.x; y = (y * scale) + xy.y; var classes = element.getAttribute('class') || ''; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 08515e45ab..967998557c 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1957,7 +1957,6 @@ Blockly.WorkspaceSvg.prototype.startDragWithFakeEvent = function(fakeEvent, /** * Get the audio manager for this workspace. * @return {Blockly.WorkspaceAudio} The audio manager for this workspace. - * @package */ Blockly.WorkspaceSvg.prototype.getAudioManager = function() { return this.audioManager_; diff --git a/core/xml.js b/core/xml.js index 5c2ab0e8de..ff0b431c83 100644 --- a/core/xml.js +++ b/core/xml.js @@ -397,7 +397,7 @@ Blockly.Xml.appendDomToWorkspace = function(xml, workspace) { var savetab = Blockly.BlockSvg.TAB_WIDTH; try { Blockly.BlockSvg.TAB_WIDTH = 0; - var bbox = workspace.getBlocksBoundingBox(); + bbox = workspace.getBlocksBoundingBox(); } finally { Blockly.BlockSvg.TAB_WIDTH = savetab; } diff --git a/i18n/create_messages.py b/i18n/create_messages.py index 1010b05f10..dc2620a314 100755 --- a/i18n/create_messages.py +++ b/i18n/create_messages.py @@ -34,7 +34,7 @@ def string_is_ascii(s): return True except UnicodeEncodeError: return False - + def load_constants(filename): """Read in constants file, which must be output in every language.""" constant_defs = read_json_file(filename); @@ -42,7 +42,7 @@ def load_constants(filename): for key in constant_defs: value = constant_defs[key] value = value.replace('"', '\\"') - constants_text += '\nBlockly.Msg.{0} = \"{1}\";'.format(key, value) + constants_text += '\nBlockly.Msg["{0}"] = "{1}";'.format(key, value) return constants_text def main(): @@ -139,7 +139,7 @@ def main(): value = source_defs[key] comment = ' // untranslated' value = value.replace('"', '\\"') - outfile.write(u'Blockly.Msg.{0} = "{1}";{2}\n'.format( + outfile.write(u'Blockly.Msg["{0}"] = "{1}";{2}\n'.format( key, value, comment)) # Announce any keys defined only for target language. From 6a9617c77d908472859d4c6eb19b10aaa1007234 Mon Sep 17 00:00:00 2001 From: marisaleung Date: Thu, 3 Aug 2017 12:05:17 -0700 Subject: [PATCH 0140/2135] Fix type tags and todo placement. --- core/contextmenu.js | 3 ++- core/flyout_base.js | 2 ++ core/gesture.js | 7 ++++--- core/variable_map.js | 2 +- core/workspace_svg.js | 1 - 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/core/contextmenu.js b/core/contextmenu.js index 07a219a60a..c1c1d8c9e0 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -44,7 +44,8 @@ goog.require('goog.ui.MenuItem'); Blockly.ContextMenu.currentBlock = null; /** - * @type {Array.} Opaque data that can be passed to unbindEvent_. + * Opaque data that can be passed to unbindEvent_. + * @type {Array.} * @private */ Blockly.ContextMenu.eventWrapper_ = null; diff --git a/core/flyout_base.js b/core/flyout_base.js index 9ab3166e24..b179aba95c 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -162,6 +162,8 @@ Blockly.Flyout.prototype.CORNER_RADIUS = 0; */ Blockly.Flyout.prototype.MARGIN = 12; +// TODO: Move GAP_X and GAP_Y to their appropriate files. + /** * Gap between items in horizontal flyouts. Can be overridden with the "sep" * element. diff --git a/core/gesture.js b/core/gesture.js index a19ca582fe..d85557b3d0 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -39,11 +39,12 @@ goog.require('goog.asserts'); goog.require('goog.math.Coordinate'); -/** - * NB: In this file "start" refers to touchstart, mousedown, and pointerstart +/* + * Note: In this file "start" refers to touchstart, mousedown, and pointerstart * events. "End" refers to touchend, mouseup, and pointerend events. - * TODO: Consider touchcancel/pointercancel. */ +// TODO: Consider touchcancel/pointercancel. + /** * Class for one gesture. diff --git a/core/variable_map.js b/core/variable_map.js index ebe55f6912..c6ffe57486 100644 --- a/core/variable_map.js +++ b/core/variable_map.js @@ -37,10 +37,10 @@ goog.require('Blockly.VariableModel'); */ Blockly.VariableMap = function(workspace) { /** - * @type {!Object>} * A map from variable type to list of variable names. The lists contain all * of the named variables in the workspace, including variables * that are not currently in use. + * @type {!Object>} * @private */ this.variableMap_ = {}; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 967998557c..fb25afbed4 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1032,7 +1032,6 @@ Blockly.WorkspaceSvg.prototype.refreshToolboxSelection_ = function() { /** * Rename a variable by updating its name in the variable list. - * TODO: google/blockly:#468 * @param {string} oldName Variable to rename. * @param {string} newName New variable name. * @package From 88e404732d8caa100183cfb475ce79d49e7bea1b Mon Sep 17 00:00:00 2001 From: RoboErikG Date: Thu, 3 Aug 2017 15:18:41 -0700 Subject: [PATCH 0141/2135] Update extensions.js to be compatible with ADVANCED_OPTIMIZATIONS (#1253) * Update extensions.js to be compatible with ADVANCED_OPTIMIZATIONS We were using strings to check for the existence of properties on a mutator, which breaks if those properties were renamed by the closure compiler. This updates all of the uses to direct function references so that anyone building with advanced optimizations turned on will get the correct method references in their mutators. fixes #1251 * Update to extensions.js for advanced optimizations Some minor updates to making extensions.js work with advanced optimizations. * use === undefined instead of typeof == 'undefined' --- core/extensions.js | 54 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/core/extensions.js b/core/extensions.js index 1f88d6d507..315f5bef58 100644 --- a/core/extensions.js +++ b/core/extensions.js @@ -45,15 +45,6 @@ goog.require('goog.string'); */ Blockly.Extensions.ALL_ = {}; -/** - * The set of properties on a block that may only be set by a mutator. - * @type {!Array.} - * @private - * @constant - */ -Blockly.Extensions.MUTATOR_PROPERTIES_ = - ['domToMutation', 'mutationToDom', 'compose', 'decompose']; - /** * Registers a new extension function. Extensions are functions that help * initialize blocks, usually adding dynamic behavior such as onchange @@ -159,7 +150,7 @@ Blockly.Extensions.apply = function(name, block, isMutator) { if (isMutator) { var errorPrefix = 'Error after applying mutator "' + name + '": '; - Blockly.Extensions.checkBlockHasMutatorProperties_(name, block, errorPrefix); + Blockly.Extensions.checkBlockHasMutatorProperties_(errorPrefix, block); } else { if (!Blockly.Extensions.mutatorPropertiesMatch_(mutatorProperties, block)) { throw new Error('Error when applying extension "' + name + @@ -198,13 +189,11 @@ Blockly.Extensions.checkHasFunction_ = function(errorPrefix, func, * @private */ Blockly.Extensions.checkNoMutatorProperties_ = function(mutationName, block) { - for (var i = 0; i < Blockly.Extensions.MUTATOR_PROPERTIES_.length; i++) { - var propertyName = Blockly.Extensions.MUTATOR_PROPERTIES_[i]; - if (block.hasOwnProperty(propertyName)) { - throw new Error('Error: tried to apply mutation "' + mutationName + - '" to a block that already has a "' + propertyName + - '" function. Block id: ' + block.id); - } + var properties = Blockly.Extensions.getMutatorProperties_(block); + if (properties.length) { + throw new Error('Error: tried to apply mutation "' + mutationName + + '" to a block that already has mutator functions.' + + ' Block id: ' + block.id); } }; @@ -221,13 +210,13 @@ Blockly.Extensions.checkNoMutatorProperties_ = function(mutationName, block) { * @private */ Blockly.Extensions.checkMutatorDialog_ = function(object, errorPrefix) { - var hasCompose = object.hasOwnProperty('compose'); - var hasDecompose = object.hasOwnProperty('decompose'); + var hasCompose = object.compose !== undefined; + var hasDecompose = object.decompose !== undefined; if (hasCompose && hasDecompose) { - if (typeof object['compose'] !== "function") { + if (typeof object.compose !== 'function') { throw new Error(errorPrefix + 'compose must be a function.'); - } else if (typeof object['decompose'] !== "function") { + } else if (typeof object['decompose'] !== 'function') { throw new Error(errorPrefix + 'decompose must be a function.'); } return true; @@ -248,10 +237,10 @@ Blockly.Extensions.checkMutatorDialog_ = function(object, errorPrefix) { */ Blockly.Extensions.checkBlockHasMutatorProperties_ = function(errorPrefix, block) { - if (!block.hasOwnProperty('domToMutation')) { + if (typeof block.domToMutation !== 'function') { throw new Error(errorPrefix + 'Applying a mutator didn\'t add "domToMutation"'); } - if (!block.hasOwnProperty('mutationToDom')) { + if (typeof block.mutationToDom !== 'function') { throw new Error(errorPrefix + 'Applying a mutator didn\'t add "mutationToDom"'); } @@ -263,14 +252,25 @@ Blockly.Extensions.checkBlockHasMutatorProperties_ = function(errorPrefix, /** * Get a list of values of mutator properties on the given block. * @param {!Blockly.Block} block The block to inspect. - * @return {!Array.} a list with all of the properties, which should be - * functions or undefined, but are not guaranteed to be. + * @return {!Array.} a list with all of the defined properties, which + * should be functions, but may be anything other than undefined. * @private */ Blockly.Extensions.getMutatorProperties_ = function(block) { var result = []; - for (var i = 0; i < Blockly.Extensions.MUTATOR_PROPERTIES_.length; i++) { - result.push(block[Blockly.Extensions.MUTATOR_PROPERTIES_[i]]); + // List each function explicitly by reference to allow for renaming + // during compilation. + if (block.domToMutation !== undefined) { + result.push(block.domToMutation); + } + if (block.mutationToDom !== undefined) { + result.push(block.mutationToDom); + } + if (block.compose !== undefined) { + result.push(block.compose); + } + if (block.decompose !== undefined) { + result.push(block.decompose); } return result; }; From 348aba771e88024ce6a7d0dd0fb85c219e4a9d50 Mon Sep 17 00:00:00 2001 From: RoboErikG Date: Fri, 4 Aug 2017 11:34:33 -0700 Subject: [PATCH 0142/2135] Missed one use of string instead of .property in extensions.js (#1262) --- core/extensions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/extensions.js b/core/extensions.js index 315f5bef58..63b02fab4f 100644 --- a/core/extensions.js +++ b/core/extensions.js @@ -216,7 +216,7 @@ Blockly.Extensions.checkMutatorDialog_ = function(object, errorPrefix) { if (hasCompose && hasDecompose) { if (typeof object.compose !== 'function') { throw new Error(errorPrefix + 'compose must be a function.'); - } else if (typeof object['decompose'] !== 'function') { + } else if (typeof object.decompose !== 'function') { throw new Error(errorPrefix + 'decompose must be a function.'); } return true; From 92e6d339f4280f21d93cbdeac8813675918e67bd Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 14 Aug 2017 15:34:44 -0700 Subject: [PATCH 0143/2135] Code cleanup in BlockSvg.prototype.tab (#1277) --- core/block_svg.js | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 3b1e040ac0..260a584937 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -594,24 +594,8 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) { * @param {boolean} forward If true go forward, otherwise backward. */ Blockly.BlockSvg.prototype.tab = function(start, forward) { - // This function need not be efficient since it runs once on a keypress. - // Create an ordered list of all text fields and connected inputs. - var list = []; - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field instanceof Blockly.FieldTextInput) { - // TODO: Also support dropdown fields. - list.push(field); - } - } - if (input.connection) { - var block = input.connection.targetBlock(); - if (block) { - list.push(block); - } - } - } - i = list.indexOf(start); + var list = this.createTabList_(); + var i = list.indexOf(start); if (i == -1) { // No start location, start at the beginning or end. i = forward ? -1 : list.length; @@ -636,6 +620,31 @@ Blockly.BlockSvg.prototype.tab = function(start, forward) { } }; +/** + * Create an ordered list of all text fields and connected inputs. + * @return {!Array} The ordered list. + * @private + */ +Blockly.BlockSvg.prototype.createTabList_ = function() { + // This function need not be efficient since it runs once on a keypress. + var list = []; + for (var i = 0, input; input = this.inputList[i]; i++) { + for (var j = 0, field; field = input.fieldRow[j]; j++) { + if (field instanceof Blockly.FieldTextInput) { + // TODO(# 1276): Also support dropdown fields. + list.push(field); + } + } + if (input.connection) { + var block = input.connection.targetBlock(); + if (block) { + list.push(block); + } + } + } + return list; +}; + /** * Handle a mouse-down on an SVG block. * @param {!Event} e Mouse down event or touch start event. From 0c09d60ff792c7cafb88c60321c5dd9fb4415e63 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Tue, 15 Aug 2017 13:20:51 -0700 Subject: [PATCH 0144/2135] =?UTF-8?q?Fix=20#1275=20by=20adding=20a=20more?= =?UTF-8?q?=20specific=20rule=20for=20overflow:visible=20on=20the=20?= =?UTF-8?q?=E2=80=A6=20(#1280)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #1275 by adding a more specific rule for overflow:visible on the drag surface svg. This wins out over a common bootstrap rule that says: svg:not(:root) {overflow:hidden} and helps avoid a difficult problem for Blockly users to diagnose. * Update css.js --- core/css.js | 7 ++++++- core/workspace_drag_surface_svg.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/css.js b/core/css.js index 373594a689..bbae9e98d9 100644 --- a/core/css.js +++ b/core/css.js @@ -186,10 +186,15 @@ Blockly.Css.CONTENT = [ '.blocklyWsDragSurface {', 'display: none;', 'position: absolute;', - 'overflow: visible;', 'top: 0;', 'left: 0;', '}', + /* Added as a separate rule with multiple classes to make it more specific + than a bootstrap rule that selects svg:root. See issue #1275 for context. + */ + '.blocklyWsDragSurface.blocklyOverflowVisible {', + 'overflow: visible;', + '}', '.blocklyBlockDragSurface {', 'display: none;', diff --git a/core/workspace_drag_surface_svg.js b/core/workspace_drag_surface_svg.js index d642a1686a..4bf932a455 100644 --- a/core/workspace_drag_surface_svg.js +++ b/core/workspace_drag_surface_svg.js @@ -93,7 +93,7 @@ Blockly.WorkspaceDragSurfaceSvg.prototype.createDom = function() { 'xmlns:html': Blockly.HTML_NS, 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 'version': '1.1', - 'class': 'blocklyWsDragSurface' + 'class': 'blocklyWsDragSurface blocklyOverflowVisible', }, null); this.container_.appendChild(this.SVG_); }; From eed4e33f845f842dfcd001508ddb442ffe55abdb Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 18 Aug 2017 11:18:49 -0700 Subject: [PATCH 0145/2135] Escape variable names correctly when serializing to XML (#1279) --- core/variables.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/variables.js b/core/variables.js index 37916cdab1..38720c4986 100644 --- a/core/variables.js +++ b/core/variables.js @@ -375,10 +375,14 @@ Blockly.Variables.promptName = function(promptText, defaultText, callback) { * @private */ Blockly.Variables.generateVariableFieldXml_ = function(variableModel, opt_name) { - var name = opt_name || "VARIABLE"; - var xmlString = ''+ - variableModel.name + - ''; + // The variable name may be user input, so it may contain characters that need + // to be escaped to create valid XML. + var element = goog.dom.createDom('field'); + element.setAttribute('name', opt_name || 'VARIABLE'); + element.setAttribute('variableType', variableModel.type); + element.setAttribute('id', variableModel.getId()); + element.textContent = variableModel.name; + + var xmlString = Blockly.Xml.domToText(element); return xmlString; }; From 5ec9195ff1dadf3d39e44553fe994aff3251e2a3 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 18 Aug 2017 14:16:46 -0700 Subject: [PATCH 0146/2135] Bring the most recently edited stack to the front at the end of a drag. (#1283) --- core/dragged_connection_manager.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/dragged_connection_manager.js b/core/dragged_connection_manager.js index a84eaeb89e..41ee0dc5e2 100644 --- a/core/dragged_connection_manager.js +++ b/core/dragged_connection_manager.js @@ -139,6 +139,9 @@ Blockly.DraggedConnectionManager.prototype.applyConnections = function() { var inferiorConnection = this.localConnection_.isSuperior() ? this.closestConnection_ : this.localConnection_; inferiorConnection.getSourceBlock().connectionUiEffect(); + // Bring the just-edited stack to the front. + var rootBlock = this.topBlock_.getRootBlock(); + rootBlock.bringToFront(); } this.removeHighlighting_(); } From 910284856e8016980790f2cc807258ab3bfcace3 Mon Sep 17 00:00:00 2001 From: "Evan W. Patton" Date: Fri, 18 Aug 2017 17:18:47 -0400 Subject: [PATCH 0147/2135] Implement Blockly.Events.filter in linear time (#1205) * Implement Blockly.Events.filter in linear time For large App Inventor projects (order 1k+ blocks, 100+ top-level blocks), the O(n^2) behavior of Blockly.Event.filter was causing performance issues when rearranging blocks or pasting from the backpack. This commit provides a linear merge implementation using a key that uniquely identifies a block so that multiple events targeting the same block are merged. This change benefits from O(1) amortized lookup using an object as a key-value store. * Add event filter unit tests and fix logic bugs * Update Blockly.Events.filter unit tests --- core/events.js | 64 ++++++++---------- tests/jsunit/event_test.js | 133 +++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 36 deletions(-) diff --git a/core/events.js b/core/events.js index 80a2620fa8..064d17daab 100644 --- a/core/events.js +++ b/core/events.js @@ -174,45 +174,37 @@ Blockly.Events.filter = function(queueIn, forward) { // Undo is merged in reverse order. queue.reverse(); } - // Merge duplicates. O(n^2), but n should be very small. - for (var i = 0, event1; event1 = queue[i]; i++) { - for (var j = i + 1, event2; event2 = queue[j]; j++) { - if (event1.type == event2.type && - event1.blockId == event2.blockId && - event1.workspaceId == event2.workspaceId) { - if (event1.type == Blockly.Events.MOVE) { - // Merge move events. - event1.newParentId = event2.newParentId; - event1.newInputName = event2.newInputName; - event1.newCoordinate = event2.newCoordinate; - queue.splice(j, 1); - j--; - } else if (event1.type == Blockly.Events.CHANGE && - event1.element == event2.element && - event1.name == event2.name) { - // Merge change events. - event1.newValue = event2.newValue; - queue.splice(j, 1); - j--; - } else if (event1.type == Blockly.Events.UI && - event2.element == 'click' && - (event1.element == 'commentOpen' || - event1.element == 'mutatorOpen' || - event1.element == 'warningOpen')) { - // Merge change events. - event1.newValue = event2.newValue; - queue.splice(j, 1); - j--; - } + var mergedQueue = []; + var hash = Object.create(null); + // Merge duplicates. + for (var i = 0, event; event = queue[i]; i++) { + if (!event.isNull()) { + var key = [event.type, event.blockId, event.workspaceId].join(' '); + var lastEvent = hash[key]; + if (!lastEvent) { + hash[key] = event; + mergedQueue.push(event); + } else if (event.type == Blockly.Events.MOVE) { + // Merge move events. + lastEvent.newParentId = event.newParentId; + lastEvent.newInputName = event.newInputName; + lastEvent.newCoordinate = event.newCoordinate; + } else if (event.type == Blockly.Events.CHANGE && + event.element == lastEvent.element && + event.name == lastEvent.name) { + // Merge change events. + lastEvent.newValue = event.newValue; + } else if (event.type == Blockly.Events.UI && + event.element == 'click' && + (lastEvent.element == 'commentOpen' || + lastEvent.element == 'mutatorOpen' || + lastEvent.element == 'warningOpen')) { + // Merge click events. + lastEvent.newValue = event.newValue; } } } - // Remove null events. - for (var i = queue.length - 1; i >= 0; i--) { - if (queue[i].isNull()) { - queue.splice(i, 1); - } - } + queue = mergedQueue; if (!forward) { // Restore undo order. queue.reverse(); diff --git a/tests/jsunit/event_test.js b/tests/jsunit/event_test.js index c019515e94..d5583089e3 100644 --- a/tests/jsunit/event_test.js +++ b/tests/jsunit/event_test.js @@ -392,3 +392,136 @@ function test_varBackard_runForward() { checkVariableValues(workspace, 'name1', 'type1', 'id1'); eventTest_tearDown(); } + +function test_events_filter() { + eventTest_setUpWithMockBlocks(); + var block1 = workspace.newBlock('field_variable_test_block', '1'); + var events = [ + new Blockly.Events.BlockCreate(block1), + new Blockly.Events.BlockMove(block1), + new Blockly.Events.BlockChange(block1, 'field', 'VAR', 'item', 'item1'), + new Blockly.Events.Ui(block1, 'click') + ]; + var filteredEvents = Blockly.Events.filter(events, true); + assertEquals(4, filteredEvents.length); // no event should have been removed. + // test that the order hasn't changed + assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate); + assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove); + assertTrue(filteredEvents[2] instanceof Blockly.Events.BlockChange); + assertTrue(filteredEvents[3] instanceof Blockly.Events.Ui); +} + +function test_events_filterForward() { + eventTest_setUpWithMockBlocks(); + var block1 = workspace.newBlock('field_variable_test_block', '1'); + var events = [ + new Blockly.Events.BlockCreate(block1), + ]; + helper_addMoveEvent(events, block1, 1, 1); + helper_addMoveEvent(events, block1, 2, 2); + helper_addMoveEvent(events, block1, 3, 3); + var filteredEvents = Blockly.Events.filter(events, true); + assertEquals(2, filteredEvents.length); // duplicate moves should have been removed. + // test that the order hasn't changed + assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate); + assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove); + assertEquals(3, filteredEvents[1].newCoordinate.x); + assertEquals(3, filteredEvents[1].newCoordinate.y); + eventTest_tearDownWithMockBlocks(); +} + +function test_events_filterBackward() { + eventTest_setUpWithMockBlocks(); + var block1 = workspace.newBlock('field_variable_test_block', '1'); + var events = [ + new Blockly.Events.BlockCreate(block1), + ]; + helper_addMoveEvent(events, block1, 1, 1); + helper_addMoveEvent(events, block1, 2, 2); + helper_addMoveEvent(events, block1, 3, 3); + var filteredEvents = Blockly.Events.filter(events, false); + assertEquals(2, filteredEvents.length); // duplicate event should have been removed. + // test that the order hasn't changed + assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate); + assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove); + assertEquals(1, filteredEvents[1].newCoordinate.x); + assertEquals(1, filteredEvents[1].newCoordinate.y); + eventTest_tearDownWithMockBlocks(); +} + +function test_events_filterDifferentBlocks() { + eventTest_setUpWithMockBlocks(); + var block1 = workspace.newBlock('field_variable_test_block', '1'); + var block2 = workspace.newBlock('field_variable_test_block', '2'); + var events = [ + new Blockly.Events.BlockCreate(block1), + new Blockly.Events.BlockMove(block1), + new Blockly.Events.BlockCreate(block2), + new Blockly.Events.BlockMove(block2) + ]; + var filteredEvents = Blockly.Events.filter(events, true); + assertEquals(4, filteredEvents.length); // no event should have been removed. + eventTest_tearDownWithMockBlocks(); +} + +function test_events_mergeMove() { + eventTest_setUpWithMockBlocks(); + var block1 = workspace.newBlock('field_variable_test_block', '1'); + var events = []; + helper_addMoveEvent(events, block1, 0, 0); + helper_addMoveEvent(events, block1, 1, 1); + var filteredEvents = Blockly.Events.filter(events, true); + assertEquals(1, filteredEvents.length); // second move event merged into first + assertEquals(1, filteredEvents[0].newCoordinate.x); + assertEquals(1, filteredEvents[0].newCoordinate.y); + eventTest_tearDownWithMockBlocks(); +} + +function test_events_mergeChange() { + eventTest_setUpWithMockBlocks(); + var block1 = workspace.newBlock('field_variable_test_block', '1'); + var events = [ + new Blockly.Events.Change(block1, 'field', 'VAR', 'item', 'item1'), + new Blockly.Events.Change(block1, 'field', 'VAR', 'item1', 'item2') + ]; + var filteredEvents = Blockly.Events.filter(events, true); + assertEquals(1, filteredEvents.length); // second change event merged into first + assertEquals('item', filteredEvents[0].oldValue); + assertEquals('item2', filteredEvents[0].newValue); + eventTest_tearDownWithMockBlocks(); +} + +function test_events_mergeUi() { + eventTest_setUpWithMockBlocks(); + var block1 = workspace.newBlock('field_variable_test_block', '1'); + var block2 = workspace.newBlock('field_variable_test_block', '2'); + var block3 = workspace.newBlock('field_variable_test_block', '3'); + var events = [ + new Blockly.Events.Ui(block1, 'commentOpen', 'false', 'true'), + new Blockly.Events.Ui(block1, 'click', 'false', 'true'), + new Blockly.Events.Ui(block2, 'mutatorOpen', 'false', 'true'), + new Blockly.Events.Ui(block2, 'click', 'false', 'true'), + new Blockly.Events.Ui(block3, 'warningOpen', 'false', 'true'), + new Blockly.Events.Ui(block3, 'click', 'false', 'true') + ]; + var filteredEvents = Blockly.Events.filter(events, true); + assertEquals(3, filteredEvents.length); // click event merged into corresponding *Open event + assertEquals('commentOpen', filteredEvents[0].element); + assertEquals('mutatorOpen', filteredEvents[1].element); + assertEquals('warningOpen', filteredEvents[2].element); + eventTest_tearDownWithMockBlocks(); +} + +/** + * Helper function to simulate block move events. + * + * @param {!Array.} events a queue of events. + * @param {!Blockly.Block} block the block to be moved + * @param {number} newX new X coordinate of the block + * @param {number} newY new Y coordinate of the block + */ +function helper_addMoveEvent(events, block, newX, newY) { + events.push(new Blockly.Events.BlockMove(block)); + block.xy_ = new goog.math.Coordinate(newX, newY); + events[events.length-1].recordNew(); +} From e38411c07ff9c0fdb828ecf713e685404bb66861 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 18 Aug 2017 14:55:40 -0700 Subject: [PATCH 0148/2135] Clean up code in FieldTextInput (#1284) * Clean up code in FieldTextInput * Explicit annotations * Remove extra newline --- core/field_textinput.js | 65 +++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index 8ac28fc928..5e9e5314d8 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -68,6 +68,14 @@ Blockly.FieldTextInput.ANIMATION_TIME = 0.25; */ Blockly.FieldTextInput.TEXT_MEASURE_PADDING_MAGIC = 45; +/** + * The HTML input element for the user to type, or null if no FieldTextInput + * editor is currently open. + * @type {HTMLInputElement} + * @private + */ +Blockly.FieldTextInput.htmlInput_ = null; + /** * Mouse cursor style when over the hotspot that initiates the editor. */ @@ -240,6 +248,30 @@ Blockly.FieldTextInput.prototype.showEditor_ = function( htmlInput.setSelectionRange(0, 99999); } + this.bindEvents_(htmlInput); + + // Add animation transition properties + var transitionProperties = 'box-shadow ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; + if (Blockly.BlockSvg.FIELD_TEXTINPUT_ANIMATE_POSITIONING) { + div.style.transition += ',padding ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + + 'width ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + + 'height ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + + 'margin-left ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; + } + div.style.transition = transitionProperties; + htmlInput.style.transition = 'font-size ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; + // The animated properties themselves + htmlInput.style.fontSize = Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_FINAL + 'pt'; + div.style.boxShadow = '0px 0px 0px 4px ' + Blockly.Colours.fieldShadow; +}; + +/** + * Bind handlers for user input on this field and size changes on the workspace. + * @param {!HTMLInputElement} htmlInput The htmlInput created in showEditor, to + * which event handlers will be bound. + * @private + */ +Blockly.FieldTextInput.prototype.bindEvents_ = function(htmlInput) { // Bind to keydown -- trap Enter without IME and Esc to hide. htmlInput.onKeyDownWrapper_ = Blockly.bindEventWithChecks_(htmlInput, 'keydown', this, @@ -260,20 +292,20 @@ Blockly.FieldTextInput.prototype.showEditor_ = function( Blockly.bindEvent_(htmlInput, 'input', this, this.onHtmlInputChange_); htmlInput.onWorkspaceChangeWrapper_ = this.resizeEditor_.bind(this); this.workspace_.addChangeListener(htmlInput.onWorkspaceChangeWrapper_); +}; - // Add animation transition properties - var transitionProperties = 'box-shadow ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; - if (Blockly.BlockSvg.FIELD_TEXTINPUT_ANIMATE_POSITIONING) { - div.style.transition += ',padding ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + - 'width ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + - 'height ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + - 'margin-left ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; - } - div.style.transition = transitionProperties; - htmlInput.style.transition = 'font-size ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; - // The animated properties themselves - htmlInput.style.fontSize = Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_FINAL + 'pt'; - div.style.boxShadow = '0px 0px 0px 4px ' + Blockly.Colours.fieldShadow; +/** + * Unbind handlers for user input and workspace size changes. + * @param {!HTMLInputElement} htmlInput The html for this text input. + * @private + */ +Blockly.FieldTextInput.prototype.unbindEvents_ = function(htmlInput) { + Blockly.unbindEvent_(htmlInput.onKeyDownWrapper_); + Blockly.unbindEvent_(htmlInput.onKeyUpWrapper_); + Blockly.unbindEvent_(htmlInput.onKeyPressWrapper_); + Blockly.unbindEvent_(htmlInput.onInputWrapper_); + this.workspace_.removeChangeListener( + htmlInput.onWorkspaceChangeWrapper_); }; /** @@ -493,15 +525,10 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() { } thisField.setText(text); thisField.sourceBlock_.rendered && thisField.sourceBlock_.render(); - Blockly.unbindEvent_(htmlInput.onKeyDownWrapper_); - Blockly.unbindEvent_(htmlInput.onKeyUpWrapper_); - Blockly.unbindEvent_(htmlInput.onKeyPressWrapper_); - Blockly.unbindEvent_(htmlInput.onInputWrapper_); + thisField.unbindEvents_(htmlInput); if (htmlInput.dropDownArrowMouseWrapper_) { Blockly.unbindEvent_(htmlInput.dropDownArrowMouseWrapper_); } - thisField.workspace_.removeChangeListener( - htmlInput.onWorkspaceChangeWrapper_); Blockly.Events.setGroup(false); // Animation of disposal From 6fb59ccc78d95103c699c48a735bb95986068350 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 18 Aug 2017 15:19:05 -0700 Subject: [PATCH 0149/2135] Decompose the showEditor_ function in FieldTextInput (#1285) * Explicit annotations * Decompose the showEditor_ function in FieldTextInput * Remove extra newline --- core/field_textinput.js | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index 5e9e5314d8..1eb6f8fe5a 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -509,22 +509,8 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() { var div = Blockly.WidgetDiv.DIV; var htmlInput = Blockly.FieldTextInput.htmlInput_; // Save the edit (if it validates). - var text = htmlInput.value; - if (thisField.sourceBlock_) { - var text1 = thisField.callValidator(text); - if (text1 === null) { - // Invalid edit. - text = htmlInput.defaultValue; - } else { - // Validation function has changed the text. - text = text1; - if (thisField.onFinishEditing_) { - thisField.onFinishEditing_(text); - } - } - } - thisField.setText(text); - thisField.sourceBlock_.rendered && thisField.sourceBlock_.render(); + thisField.maybeSaveEdit_(); + thisField.unbindEvents_(htmlInput); if (htmlInput.dropDownArrowMouseWrapper_) { Blockly.unbindEvent_(htmlInput.dropDownArrowMouseWrapper_); @@ -565,6 +551,27 @@ Blockly.FieldTextInput.prototype.widgetDisposeAnimationFinished_ = function() { }; }; +Blockly.FieldTextInput.prototype.maybeSaveEdit_ = function() { + var htmlInput = Blockly.FieldTextInput.htmlInput_; + // Save the edit (if it validates). + var text = htmlInput.value; + if (this.sourceBlock_) { + var text1 = this.callValidator(text); + if (text1 === null) { + // Invalid edit. + text = htmlInput.defaultValue; + } else { + // Validation function has changed the text. + text = text1; + if (this.onFinishEditing_) { + this.onFinishEditing_(text); + } + } + } + this.setText(text); + this.sourceBlock_.rendered && this.sourceBlock_.render(); +}; + /** * Ensure that only a number may be entered. * @param {string} text The user's text. From 504c458003b26d0c9955b1d868109f61e0e4efb7 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 24 Aug 2017 10:58:16 -0700 Subject: [PATCH 0150/2135] Add annotations for units to scrollbar.js (#1290) * Add annotations for units to scrollbar.js * Update comments --- core/scrollbar.js | 78 ++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/core/scrollbar.js b/core/scrollbar.js index 92b15b763b..08b6585120 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -30,6 +30,10 @@ goog.provide('Blockly.ScrollbarPair'); goog.require('goog.dom'); goog.require('goog.events'); +/** + * A note on units: most of the numbers that are in CSS pixels are scaled if the + * scrollbar is in a mutator. + */ /** * Class for a pair of scrollbars. Horizontal and vertical. @@ -133,7 +137,8 @@ Blockly.ScrollbarPair.prototype.resize = function() { }; /** - * Set the sliders of both scrollbars to be at a certain position. + * Set the handles of both scrollbars to be at a certain position in CSS pixels + * relative to their parents. * @param {number} x Horizontal scroll value. * @param {number} y Vertical scroll value. */ @@ -196,7 +201,9 @@ Blockly.Scrollbar = function(workspace, horizontal, opt_pair, opt_class) { this.createDom_(opt_class); /** - * The upper left corner of the scrollbar's svg group. + * The upper left corner of the scrollbar's svg group in CSS pixels relative + * to the scrollbar's origin. This is usually relative to the injection div + * origin. * @type {goog.math.Coordinate} * @private */ @@ -231,11 +238,14 @@ Blockly.Scrollbar = function(workspace, horizontal, opt_pair, opt_class) { this.onMouseDownHandleWrapper_ = Blockly.bindEventWithChecks_(this.svgHandle_, 'mousedown', scrollbar, scrollbar.onMouseDownHandle_); }; + /** - * The coordinate of the upper left corner of the scrollbar SVG. - * @type {goog.math.Coordinate} - * @private - */ + * The location of the origin of the workspace that the scrollbar is in, + * measured in CSS pixels relative to the injection div origin. This is usually + * (0, 0). When the scrollbar is in a flyout it may have a different origin. + * @type {goog.math.Coordinate} + * @private + */ Blockly.Scrollbar.prototype.origin_ = new goog.math.Coordinate(0, 0); /** @@ -255,16 +265,15 @@ Blockly.Scrollbar.prototype.originHasChanged_ = true; Blockly.Scrollbar.prototype.scrollViewSize_ = 0; /** - * The length of the scrollbar handle. - * Coordinate system: pixel coordinates. + * The length of the scrollbar handle in CSS pixels. * @type {number} * @private */ Blockly.Scrollbar.prototype.handleLength_ = 0; /** - * The offset of the start of the handle from the start of the scrollbar range. - * Coordinate system: pixel coordinates. + * The offset of the start of the handle from the scrollbar position, in CSS + * pixels. * @type {number} * @private */ @@ -285,8 +294,8 @@ Blockly.Scrollbar.prototype.isVisible_ = true; Blockly.Scrollbar.prototype.containerVisible_ = true; /** - * Width of vertical scrollbar or height of horizontal scrollbar. - * Increase the size of scrollbars on touch devices. + * Width of vertical scrollbar or height of horizontal scrollbar in CSS pixels. + * Scrollbars should be larger on touch devices. */ Blockly.Scrollbar.scrollbarThickness = 11; if (goog.events.BrowserFeature.TOUCH_ENABLED) { @@ -344,7 +353,7 @@ Blockly.Scrollbar.prototype.dispose = function() { /** * Set the length of the scrollbar's handle and change the SVG attribute * accordingly. - * @param {number} newLength The new scrollbar handle length. + * @param {number} newLength The new scrollbar handle length in CSS pixels. */ Blockly.Scrollbar.prototype.setHandleLength_ = function(newLength) { this.handleLength_ = newLength; @@ -352,9 +361,9 @@ Blockly.Scrollbar.prototype.setHandleLength_ = function(newLength) { }; /** - * Set the offset of the scrollbar's handle and change the SVG attribute - * accordingly. - * @param {number} newPosition The new scrollbar handle offset. + * Set the offset of the scrollbar's handle from the scrollbar's position, and + * change the SVG attribute accordingly. + * @param {number} newPosition The new scrollbar handle offset in CSS pixels. */ Blockly.Scrollbar.prototype.setHandlePosition = function(newPosition) { this.handlePosition_ = newPosition; @@ -364,7 +373,7 @@ Blockly.Scrollbar.prototype.setHandlePosition = function(newPosition) { /** * Set the size of the scrollbar's background and change the SVG attribute * accordingly. - * @param {number} newSize The new scrollbar background length. + * @param {number} newSize The new scrollbar background length in CSS pixels. * @private */ Blockly.Scrollbar.prototype.setScrollViewSize_ = function(newSize) { @@ -383,11 +392,13 @@ Blockly.ScrollbarPair.prototype.setContainerVisible = function(visible) { }; /** - * Set the position of the scrollbar's svg group. + * Set the position of the scrollbar's svg group in CSS pixels relative to the + * scrollbar's origin. This sets the scrollbar's location within the workspace. * @param {number} x The new x coordinate. * @param {number} y The new y coordinate. + * @private */ -Blockly.Scrollbar.prototype.setPosition = function(x, y) { +Blockly.Scrollbar.prototype.setPosition_ = function(x, y) { this.position_.x = x; this.position_.y = y; @@ -479,7 +490,7 @@ Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) { // Horizontal toolbar should always be just above the bottom of the workspace. var yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - Blockly.Scrollbar.scrollbarThickness - 0.5; - this.setPosition(xCoordinate, yCoordinate); + this.setPosition_(xCoordinate, yCoordinate); // If the view has been resized, a content resize will also be necessary. The // reverse is not true. @@ -546,7 +557,7 @@ Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) { Blockly.Scrollbar.scrollbarThickness - 1; } var yCoordinate = hostMetrics.absoluteTop + 0.5; - this.setPosition(xCoordinate, yCoordinate); + this.setPosition_(xCoordinate, yCoordinate); // If the view has been resized, a content resize will also be necessary. The // reverse is not true. @@ -742,7 +753,7 @@ Blockly.Scrollbar.prototype.onMouseDownHandle_ = function(e) { this.workspace_.setupDragSurface(); // Record the current mouse position. - this.startDragMouse = this.horizontal_ ? e.clientX : e.clientY; + this.startDragMouse_ = this.horizontal_ ? e.clientX : e.clientY; Blockly.Scrollbar.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document, 'mouseup', this, this.onMouseUpHandle_); Blockly.Scrollbar.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document, @@ -763,7 +774,7 @@ Blockly.Scrollbar.prototype.onMouseDownHandle_ = function(e) { */ Blockly.Scrollbar.prototype.onMouseMoveHandle_ = function(e) { var currentMouse = this.horizontal_ ? e.clientX : e.clientY; - var mouseDelta = currentMouse - this.startDragMouse; + var mouseDelta = currentMouse - this.startDragMouse_; var handlePosition = this.startDragHandle + mouseDelta; // Position the bar. this.setHandlePosition(this.constrainHandle_(handlePosition)); @@ -801,8 +812,8 @@ Blockly.Scrollbar.prototype.cleanUp_ = function() { /** * Constrain the handle's position within the minimum (0) and maximum * (length of scrollbar) values allowed for the scrollbar. - * @param {number} value Value that is potentially out of bounds. - * @return {number} Constrained value. + * @param {number} value Value that is potentially out of bounds, in CSS pixels. + * @return {number} Constrained value, in CSS pixels. * @private */ Blockly.Scrollbar.prototype.constrainHandle_ = function(value) { @@ -833,8 +844,10 @@ Blockly.Scrollbar.prototype.onScroll_ = function() { }; /** - * Set the scrollbar slider's position. - * @param {number} value The distance from the top/left end of the bar. + * Set the scrollbar handle's position. + * @param {number} value The distance from the top/left end of the bar, in CSS + * pixels. It may be larger than the maximum allowable position of the + * scrollbar handle. */ Blockly.Scrollbar.prototype.set = function(value) { this.setHandlePosition(this.constrainHandle_(value * this.ratio_)); @@ -842,11 +855,12 @@ Blockly.Scrollbar.prototype.set = function(value) { }; /** - * Set the origin of the upper left of the scrollbar. This if for times - * when the scrollbar is used in an object whose origin isn't the same - * as the main workspace (e.g. in a flyout.) - * @param {number} x The x coordinate of the scrollbar's origin. - * @param {number} y The y coordinate of the scrollbar's origin. + * Record the origin of the workspace that the scrollbar is in, in pixels + * relative to the injection div origin. This is for times when the scrollbar is + * used in an object whose origin isn't the same as the main workspace + * (e.g. in a flyout.) + * @param {number} x The x coordinate of the scrollbar's origin, in CSS pixels. + * @param {number} y The y coordinate of the scrollbar's origin, in CSS pixels. */ Blockly.Scrollbar.prototype.setOrigin = function(x, y) { if (x != this.origin_.x || y != this.origin_.y) { From b033c8d3ab212514a11e97f5faffdf7d492ddece Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 24 Aug 2017 15:51:31 -0700 Subject: [PATCH 0151/2135] Create WorkspaceViewport class (#1291) * Create WorkspaceViewport class * Update comments * Move workspace viewport functions back to the workspace for now * whitespace --- core/workspace_svg.js | 190 ++++++++++++++++++++++++++++++++---------- 1 file changed, 146 insertions(+), 44 deletions(-) diff --git a/core/workspace_svg.js b/core/workspace_svg.js index fb25afbed4..9983b22b69 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1263,6 +1263,7 @@ Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox = function() { * Clean up the workspace by ordering all the blocks in a column. */ Blockly.WorkspaceSvg.prototype.cleanUp = function() { + this.setResizesEnabled(false); Blockly.Events.setGroup(true); var topBlocks = this.getTopBlocks(true); var cursorY = 0; @@ -1274,8 +1275,7 @@ Blockly.WorkspaceSvg.prototype.cleanUp = function() { block.getHeightWidth().height + Blockly.BlockSvg.MIN_BLOCK_Y; } Blockly.Events.setGroup(false); - // Fire an event to allow scrollbars to resize. - this.resizeContents(); + this.setResizesEnabled(true); }; /** @@ -1512,6 +1512,7 @@ Blockly.WorkspaceSvg.prototype.zoom = function(x, y, amount) { .translate(x * (1 - scaleChange), y * (1 - scaleChange)) .scale(scaleChange); // newScale and matrix.a should be identical (within a rounding error). + // ScrollX and scrollY are in pixels. this.scrollX = matrix.e - metrics.absoluteLeft; this.scrollY = matrix.f - metrics.absoluteTop; } @@ -1644,6 +1645,118 @@ Blockly.WorkspaceSvg.prototype.updateStackGlowScale_ = function() { } }; +/** + * Get the dimensions of the given workspace component, in pixels. + * @param {Blockly.Toolbox|Blockly.Flyout} elem The element to get the + * dimensions of, or null. It should be a toolbox or flyout, and should + * implement getWidth() and getHeight(). + * @return {!Object} An object containing width and height attributes, which + * will both be zero if elem did not exist. + * @private + */ +Blockly.WorkspaceSvg.getDimensionsPx_ = function(elem) { + var width = 0; + var height = 0; + if (elem) { + width = elem.getWidth(); + height = elem.getHeight(); + } + return { + width: width, + height: height + }; +}; + +/** + * Get the content dimensions of the given workspace, taking into account + * whether or not it is scrollable and what size the workspace div is on screen. + * @param {!Blockly.WorkspaceSvg} ws The workspace to measure. + * @param {!Object} svgSize An object containing height and width attributes in + * CSS pixels. Together they specify the size of the visible workspace, not + * including areas covered up by the toolbox. + * @return {!Object} The dimensions of the contents of the given workspace, as + * an object containing at least + * - height and width in pixels + * - left and top in pixels relative to the workspace origin. + * @private + */ +Blockly.WorkspaceSvg.getContentDimensions_ = function(ws, svgSize) { + if (ws.scrollbar) { + return Blockly.WorkspaceSvg.getContentDimensionsBounded_(ws, svgSize); + } else { + return Blockly.WorkspaceSvg.getContentDimensionsExact_(ws); + } +}; + +/** + * Get the bounding box for all workspace contents, in pixels. + * @param {!Blockly.WorkspaceSvg} ws The workspace to inspect. + * @return {!Object} The dimensions of the contents of the given workspace, as + * an object containing + * - height and width in pixels + * - left, right, top and bottom in pixels relative to the workspace origin. + * @private + */ +Blockly.WorkspaceSvg.getContentDimensionsExact_ = function(ws) { + // Block bounding box is in workspace coordinates. + var blockBox = ws.getBlocksBoundingBox(); + var scale = ws.scale; + + // Convert to pixels. + var width = blockBox.width * scale; + var height = blockBox.height * scale; + var left = blockBox.x * scale; + var top = blockBox.y * scale; + + return { + left: left, + top: top, + right: left + width, + bottom: top + height, + width: width, + height: height + }; +}; + +/** + * Calculate the size of a scrollable workspace, which should include room for a + * half screen border around the workspace contents. + * @param {!Blockly.WorkspaceSvg} ws The workspace to measure. + * @param {!Object} svgSize An object containing height and width attributes in + * CSS pixels. Together they specify the size of the visible workspace, not + * including areas covered up by the toolbox. + * @return {!Object} The dimensions of the contents of the given workspace, as + * an object containing + * - height and width in pixels + * - left and top in pixels relative to the workspace origin. + * @private + */ +Blockly.WorkspaceSvg.getContentDimensionsBounded_ = function(ws, svgSize) { + var content = Blockly.WorkspaceSvg.getContentDimensionsExact_(ws); + + // View height and width are both in pixels, and are the same as the svg size. + var viewWidth = svgSize.width; + var viewHeight = svgSize.height; + var halfWidth = viewWidth / 2; + var halfHeight = viewHeight / 2; + + // Add a border around the content that is at least half a screenful wide. + // Ensure border is wide enough that blocks can scroll over entire screen. + var left = Math.min(content.left - halfWidth, content.right - viewWidth); + var right = Math.max(content.right + halfWidth, content.left + viewWidth); + + var top = Math.min(content.top - halfHeight, content.bottom - viewHeight); + var bottom = Math.max(content.bottom + halfHeight, content.top + viewHeight); + + var dimensions = { + left: left, + top: top, + height: bottom - top, + width: right - left + }; + return dimensions; +}; + /** * Return an object with all the metrics required to size scrollbars for a * top level workspace. The following properties are computed: @@ -1669,70 +1782,59 @@ Blockly.WorkspaceSvg.prototype.updateStackGlowScale_ = function() { * @this Blockly.WorkspaceSvg */ Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_ = function() { + + var toolboxDimensions = + Blockly.WorkspaceSvg.getDimensionsPx_(this.toolbox_); + var flyoutDimensions = + Blockly.WorkspaceSvg.getDimensionsPx_(this.flyout_); + + // Contains height and width in CSS pixels. + // svgSize is equivalent to the size of the injectionDiv at this point. var svgSize = Blockly.svgSize(this.getParentSvg()); if (this.toolbox_) { if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP || this.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { - svgSize.height -= this.toolbox_.getHeight(); + svgSize.height -= toolboxDimensions.height; } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT || this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - svgSize.width -= this.toolbox_.getWidth(); + svgSize.width -= toolboxDimensions.width; } } - // Set the margin to match the flyout's margin so that the workspace does - // not jump as blocks are added. - var MARGIN = Blockly.Flyout.prototype.CORNER_RADIUS - 1; - var viewWidth = svgSize.width - MARGIN; - var viewHeight = svgSize.height - MARGIN; - var blockBox = this.getBlocksBoundingBox(); + // svgSize is now the space taken up by the Blockly workspace, not including + // the toolbox. + var contentDimensions = + Blockly.WorkspaceSvg.getContentDimensions_(this, svgSize); - // Fix scale. - var contentWidth = blockBox.width * this.scale; - var contentHeight = blockBox.height * this.scale; - var contentX = blockBox.x * this.scale; - var contentY = blockBox.y * this.scale; - if (this.scrollbar) { - // Add a border around the content that is at least half a screenful wide. - // Ensure border is wide enough that blocks can scroll over entire screen. - var leftEdge = Math.min(contentX - viewWidth / 2, - contentX + contentWidth - viewWidth); - var rightEdge = Math.max(contentX + contentWidth + viewWidth / 2, - contentX + viewWidth); - var topEdge = Math.min(contentY - viewHeight / 2, - contentY + contentHeight - viewHeight); - var bottomEdge = Math.max(contentY + contentHeight + viewHeight / 2, - contentY + viewHeight); - } else { - var leftEdge = blockBox.x; - var rightEdge = leftEdge + blockBox.width; - var topEdge = blockBox.y; - var bottomEdge = topEdge + blockBox.height; - } var absoluteLeft = 0; if (this.toolbox_ && this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - absoluteLeft = this.toolbox_.getWidth(); + absoluteLeft = toolboxDimensions.width; } var absoluteTop = 0; if (this.toolbox_ && this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { - absoluteTop = this.toolbox_.getHeight(); + absoluteTop = toolboxDimensions.height; } var metrics = { + contentHeight: contentDimensions.height, + contentWidth: contentDimensions.width, + contentTop: contentDimensions.top, + contentLeft: contentDimensions.left, + viewHeight: svgSize.height, viewWidth: svgSize.width, - contentHeight: bottomEdge - topEdge, - contentWidth: rightEdge - leftEdge, - viewTop: -this.scrollY, - viewLeft: -this.scrollX, - contentTop: topEdge, - contentLeft: leftEdge, + viewTop: -this.scrollY, // Must be in pixels, somehow. + viewLeft: -this.scrollX, // Must be in pixels, somehow. + absoluteTop: absoluteTop, absoluteLeft: absoluteLeft, - toolboxWidth: this.toolbox_ ? this.toolbox_.getWidth() : 0, - toolboxHeight: this.toolbox_ ? this.toolbox_.getHeight() : 0, - flyoutWidth: this.flyout_ ? this.flyout_.getWidth() : 0, - flyoutHeight: this.flyout_ ? this.flyout_.getHeight() : 0, + + toolboxWidth: toolboxDimensions.width, + toolboxHeight: toolboxDimensions.height, + + flyoutWidth: flyoutDimensions.width, + flyoutHeight: flyoutDimensions.height, + toolboxPosition: this.toolboxPosition }; return metrics; From 9ce71d1099ef08b85024580c89d7b2b547ade42f Mon Sep 17 00:00:00 2001 From: Sam El-Husseini Date: Tue, 5 Sep 2017 10:26:58 -0700 Subject: [PATCH 0152/2135] Fix flyout labels causing flyout scrolling issues (#1301) --- core/flyout_base.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/flyout_base.js b/core/flyout_base.js index b179aba95c..74e553c1c3 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -620,8 +620,9 @@ Blockly.Flyout.prototype.clearOldBlocks_ = function() { } } // Delete any background buttons from a previous showing. - for (var j = 0, rect; rect = this.backgroundButtons_[j]; j++) { - goog.dom.removeNode(rect); + for (var j = 0; j < this.backgroundButtons_.length; j++) { + var rect = this.backgroundButtons_[j]; + if (rect) goog.dom.removeNode(rect); } this.backgroundButtons_.length = 0; From 75f52db086238bcf79592642c7ba3b1b6b1c05c1 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Thu, 14 Sep 2017 17:23:50 -0700 Subject: [PATCH 0153/2135] Strip licences from compiled code. (#1318) --- build.py | 24 ++++++++++++++++++++++++ core/block_svg.js | 1 + 2 files changed, 25 insertions(+) diff --git a/build.py b/build.py index e53c4cfb0f..0a338991cf 100755 --- a/build.py +++ b/build.py @@ -337,6 +337,30 @@ def file_lookup(name): code = HEADER + "\n" + json_data["compiledCode"] code = code.replace(remove, "") + + # Trim down Google's (and only Google's) Apache licences. + # The Closure Compiler preserves these. + LICENSE = re.compile("""/\\* + + [\w ]+ + + Copyright \\d+ Google Inc. + https://developers.google.com/blockly/ + + Licensed under the Apache License, Version 2.0 \(the "License"\); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +\\*/""") + code = re.sub(LICENSE, "", code) + stats = json_data["statistics"] original_b = stats["originalSize"] compressed_b = stats["compressedSize"] diff --git a/core/block_svg.js b/core/block_svg.js index 260a584937..c3777ab619 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -30,6 +30,7 @@ goog.require('Blockly.Block'); goog.require('Blockly.ContextMenu'); goog.require('Blockly.Grid'); goog.require('Blockly.RenderedConnection'); +goog.require('Blockly.Tooltip'); goog.require('Blockly.Touch'); goog.require('Blockly.utils'); goog.require('goog.Timer'); From 971c0c83167ba531bf23b44522a4cd5f0acfb573 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 18 Sep 2017 12:44:48 -0700 Subject: [PATCH 0154/2135] Allow the toolbox to scroll (#1319) * Allow the toolbox to scroll * Switch from onMouseDown to onClick_ for toolbox category taps and clicks. --- core/blockly.js | 12 +++++++++--- core/toolbox.js | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/blockly.js b/core/blockly.js index 721e887b33..0f0da9851b 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -383,11 +383,15 @@ Blockly.defineBlocksWithJsonArray = function(jsonArray) { * @param {boolean} opt_noCaptureIdentifier True if triggering on this event * should not block execution of other event handlers on this touch or other * simultaneous touches. + * @param {boolean} opt_noPreventDefault True if triggering on this event + * should prevent the default handler. False by default. If + * opt_noPreventDefault is provided, opt_noCaptureIdentifier must also be + * provided. * @return {!Array.} Opaque data that can be passed to unbindEvent_. * @private */ Blockly.bindEventWithChecks_ = function(node, name, thisObject, func, - opt_noCaptureIdentifier) { + opt_noCaptureIdentifier, opt_noPreventDefault) { var handled = false; var wrapFunc = function(e) { var captureIdentifier = !opt_noCaptureIdentifier; @@ -415,8 +419,10 @@ Blockly.bindEventWithChecks_ = function(node, name, thisObject, func, if (name in Blockly.Touch.TOUCH_MAP) { var touchWrapFunc = function(e) { wrapFunc(e); - // Stop the browser from scrolling/zooming the page. - if (handled) { + // Calling preventDefault stops the browser from scrolling/zooming the + // page. + var preventDef = !opt_noPreventDefault; + if (handled && preventDef) { e.preventDefault(); } }; diff --git a/core/toolbox.js b/core/toolbox.js index 86be5b97fb..9639ad20c5 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -124,7 +124,7 @@ Blockly.Toolbox.prototype.init = function() { Blockly.hideChaff(true); } Blockly.Touch.clearTouchIdentifier(); // Don't block future drags. - }); + }, /*opt_noCaptureIdentifier*/ false, /*opt_noPreventDefault*/ true); this.createFlyout_(); this.categoryMenu_ = new Blockly.Toolbox.CategoryMenu(this, this.HtmlDiv); @@ -540,7 +540,7 @@ Blockly.Toolbox.Category.prototype.createDom = function() { this.item_.appendChild(this.bubble_); this.item_.appendChild(this.label_); this.parentHtml_.appendChild(this.item_); - Blockly.bindEvent_(this.item_, 'mousedown', toolbox, + Blockly.bindEvent_(this.item_, 'mouseup', toolbox, toolbox.setSelectedItemFactory(this)); }; From c2e7f6dfe83ed7250e6daac3dbbb6edea6993554 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 18 Sep 2017 13:09:07 -0700 Subject: [PATCH 0155/2135] Make dropdowns update correctly when you switch between images. (#1321) --- core/field.js | 11 +++++++++++ core/field_dropdown.js | 9 ++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/core/field.js b/core/field.js index b6fe3bc17a..8d7866d307 100644 --- a/core/field.js +++ b/core/field.js @@ -559,6 +559,17 @@ Blockly.Field.prototype.setText = function(newText) { return; } this.text_ = newText; + this.forceRerender(); +}; + +/** + * Force a rerender of the block that this field is installed on, which will + * rerender this field and adjust for any sizing changes. + * Other fields on the same block will not rerender, because their sizes have + * already been recorded. + * @package + */ +Blockly.Field.prototype.forceRerender = function() { // Set width to 0 to force a rerender of this field. this.size_.width = 0; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 99980b1645..8c2a9e9ed9 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -406,17 +406,20 @@ Blockly.FieldDropdown.prototype.setValue = function(newValue) { var content = options[i][0]; if (typeof content == 'object') { this.imageJson_ = content; - this.setText(content.alt); + this.text_ = content.alt; } else { this.imageJson_ = null; - this.setText(content); + this.text_ = content; } + // Always rerender if either the value or the text has changed. + this.forceRerender(); return; } } // Value not found. Add it, maybe it will become valid once set // (like variable names). - this.setText(newValue); + this.text_ = newValue; + this.forceRerender(); }; /** From c55824d6197bc736ac1eae4dbc5592e232c0634c Mon Sep 17 00:00:00 2001 From: Sam El-Husseini Date: Tue, 19 Sep 2017 15:04:25 -0700 Subject: [PATCH 0156/2135] Avoid IE and Edge re-rendering (#1326) * Fix wrong width of field_dropdown with an image on Edge / IE * Avoid re-rendering on IE and Edge by using getBBox().width to compute the text width on those browsers --- core/field.js | 9 ++++++--- core/flyout_base.js | 8 -------- core/workspace_svg.js | 15 +-------------- core/xml.js | 15 --------------- 4 files changed, 7 insertions(+), 40 deletions(-) diff --git a/core/field.js b/core/field.js index 8d7866d307..e70b0d3042 100644 --- a/core/field.js +++ b/core/field.js @@ -448,10 +448,13 @@ Blockly.Field.getCachedWidth = function(textElement) { // Attempt to compute fetch the width of the SVG text element. try { - width = textElement.getComputedTextLength(); + if (goog.userAgent.IE || goog.userAgent.EDGE) { + width = textElement.getBBox().width; + } else { + width = textElement.getComputedTextLength(); + } } catch (e) { - // MSIE 11 and Edge are known to throw "Unexpected call to method or - // property access." if the block is hidden. Instead, use an + // In other cases where we fail to geth the computed text. Instead, use an // approximation and do not cache the result. At some later point in time // when the block is inserted into the visible DOM, this method will be // called again and, at that point in time, will not throw an exception. diff --git a/core/flyout_base.js b/core/flyout_base.js index 74e553c1c3..75416d0dae 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -697,14 +697,6 @@ Blockly.Flyout.prototype.createBlock = function(originalBlock) { this.targetWorkspace_.setResizesEnabled(false); try { newBlock = this.placeNewBlock_(originalBlock); - //Force a render on IE and Edge to get around the issue described in - //Blockly.Field.getCachedWidth - if (goog.userAgent.IE || goog.userAgent.EDGE) { - var blocks = newBlock.getDescendants(); - for (var i = blocks.length - 1; i >= 0; i--) { - blocks[i].render(false); - } - } // Close the flyout. Blockly.hideChaff(); } finally { diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 9983b22b69..d6a079d076 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -953,21 +953,8 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) { Blockly.Events.disable(); try { var block = Blockly.Xml.domToBlock(xmlBlock, this); - // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste - Blockly.utils.changeObscuredShadowIds(block); - - var blocks = block.getDescendants(); - for (var i = blocks.length - 1; i >= 0; i--) { - var descendant = blocks[i]; - // Rerender to get around problem with IE and Edge not measuring text - // correctly when it is hidden. - if (goog.userAgent.IE || goog.userAgent.EDGE) { - descendant.render(false); - } - } - - // Move the duplicate to original position. + Blockly.utils.changeObscuredShadowIds(block); // Move the duplicate to original position. var blockX = parseInt(xmlBlock.getAttribute('x'), 10); var blockY = parseInt(xmlBlock.getAttribute('y'), 10); if (!isNaN(blockX) && !isNaN(blockY)) { diff --git a/core/xml.js b/core/xml.js index ff0b431c83..7c6b83a737 100644 --- a/core/xml.js +++ b/core/xml.js @@ -473,21 +473,6 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) { setTimeout(function() { if (topBlock.workspace) { // Check that the block hasn't been deleted. topBlock.setConnectionsHidden(false); - // Force a render on IE and Edge to get around the issue described in - // Blockly.Field.getCachedWidth - if (goog.userAgent.IE || goog.userAgent.EDGE) { - topBlock.render(); - } - } - }, 1); - } else { - setTimeout(function() { - if (topBlock.workspace) { // Check that the block hasn't been deleted. - // Force a render on IE and Edge to get around the issue described in - // Blockly.Field.getCachedWidth - if (goog.userAgent.IE || goog.userAgent.EDGE) { - topBlock.render(); - } } }, 1); } From 6868b9cccbb21f3a061401c9c42dd282fe55328f Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 20 Sep 2017 16:38:13 -0700 Subject: [PATCH 0157/2135] Code cleanup: widget creation (#1332) --- core/contextmenu.js | 30 +++++++++++++++++++++++------- core/field_colour.js | 24 +++++++++++++++++------- core/field_date.js | 26 ++++++++++++++++++-------- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/core/contextmenu.js b/core/contextmenu.js index c1c1d8c9e0..da1bcbd15e 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -88,7 +88,6 @@ Blockly.ContextMenu.populate_ = function(options, rtl) { callback: Blockly.MakeItSo} */ var menu = new goog.ui.Menu(); - menu.setAllowAutoFocus(true); menu.setRightToLeft(rtl); for (var i = 0, option; option = options[i]; i++) { var menuItem = new goog.ui.MenuItem(option.text); @@ -119,13 +118,9 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) { // Record windowSize and scrollOffset before adding menu. var windowSize = goog.dom.getViewportSize(); var scrollOffset = goog.style.getViewportPageOffset(document); - var div = Blockly.WidgetDiv.DIV; - menu.render(div); + + Blockly.ContextMenu.createWidget_(menu); var menuDom = menu.getElement(); - Blockly.utils.addClass(menuDom, 'blocklyContextMenu'); - // Prevent system context menu when right-clicking a Blockly context menu. - Blockly.bindEventWithChecks_(menuDom, 'contextmenu', null, - Blockly.utils.noEvent); // Record menuSize after adding menu. var menuSize = goog.style.getSize(menuDom); @@ -147,6 +142,27 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) { } } Blockly.WidgetDiv.position(x, y, windowSize, scrollOffset, rtl); + // Calling menuDom.focus() has to wait until after the menu has been placed + // correctly. Otherwise it will cause a page scroll to get the misplaced menu + // in view. See issue #1329. + menuDom.focus(); +}; + +/** + * Create and render the menu widget inside Blockly's widget div. + * @param {!goog.ui.Menu} menu The menu to add to the widget div. + * @private + */ +Blockly.ContextMenu.createWidget_ = function(menu) { + var div = Blockly.WidgetDiv.DIV; + menu.render(div); + var menuDom = menu.getElement(); + Blockly.utils.addClass(menuDom, 'blocklyContextMenu'); + // Prevent system context menu when right-clicking a Blockly context menu. + Blockly.bindEventWithChecks_(menuDom, 'contextmenu', null, + Blockly.utils.noEvent); + // Enable autofocus after the initial render to avoid issue #1329. + menu.setAllowAutoFocus(true); }; /** diff --git a/core/field_colour.js b/core/field_colour.js index 64bec59229..c628db2126 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -175,10 +175,6 @@ Blockly.FieldColour.prototype.setColumns = function(columns) { Blockly.FieldColour.prototype.showEditor_ = function() { Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, Blockly.FieldColour.widgetDispose_); - // Create the palette using Closure. - var picker = new goog.ui.ColorPicker(); - picker.setSize(this.columns_ || Blockly.FieldColour.COLUMNS); - picker.setColors(this.colours_ || Blockly.FieldColour.COLOURS); // Position the palette to line up with the field. // Record windowSize and scrollOffset before adding the palette. @@ -186,10 +182,8 @@ Blockly.FieldColour.prototype.showEditor_ = function() { var scrollOffset = goog.style.getViewportPageOffset(document); var xy = this.getAbsoluteXY_(); var borderBBox = this.getScaledBBox_(); - var div = Blockly.WidgetDiv.DIV; - picker.render(div); - picker.setSelectedColor(this.getValue()); // Record paletteSize after adding the palette. + var picker = this.createWidget_(); var paletteSize = goog.style.getSize(picker.getElement()); // Flip the palette vertically if off the bottom. @@ -232,6 +226,22 @@ Blockly.FieldColour.prototype.showEditor_ = function() { }); }; +/** + * Create a color picker widget and render it inside the widget div. + * @return {!goog.ui.ColorPicker} The newly created color picker. + * @private + */ +Blockly.FieldColour.prototype.createWidget_ = function() { + // Create the palette using Closure. + var picker = new goog.ui.ColorPicker(); + picker.setSize(this.columns_ || Blockly.FieldColour.COLUMNS); + picker.setColors(this.colours_ || Blockly.FieldColour.COLOURS); + var div = Blockly.WidgetDiv.DIV; + picker.render(div); + picker.setSelectedColor(this.getValue()); + return picker; +}; + /** * Hide the colour palette. * @private diff --git a/core/field_date.js b/core/field_date.js index b22bee0261..96a0d3eb38 100644 --- a/core/field_date.js +++ b/core/field_date.js @@ -102,11 +102,6 @@ Blockly.FieldDate.prototype.setValue = function(date) { Blockly.FieldDate.prototype.showEditor_ = function() { Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, Blockly.FieldDate.widgetDispose_); - // Create the date picker using Closure. - Blockly.FieldDate.loadLanguage_(); - var picker = new goog.ui.DatePicker(); - picker.setAllowNone(false); - picker.setShowWeekNum(false); // Position the picker to line up with the field. // Record windowSize and scrollOffset before adding the picker. @@ -114,9 +109,7 @@ Blockly.FieldDate.prototype.showEditor_ = function() { var scrollOffset = goog.style.getViewportPageOffset(document); var xy = this.getAbsoluteXY_(); var borderBBox = this.getScaledBBox_(); - var div = Blockly.WidgetDiv.DIV; - picker.render(div); - picker.setDate(goog.date.fromIsoString(this.getValue())); + var picker = this.createWidget_(); // Record pickerSize after adding the date picker. var pickerSize = goog.style.getSize(picker.getElement()); @@ -158,6 +151,23 @@ Blockly.FieldDate.prototype.showEditor_ = function() { }); }; +/** + * Create a date picker widget and render it inside the widget div. + * @return {!goog.ui.DatePicker} The newly created date picker. + * @private + */ +Blockly.FieldDate.prototype.createWidget_ = function() { + // Create the date picker using Closure. + Blockly.FieldDate.loadLanguage_(); + var picker = new goog.ui.DatePicker(); + picker.setAllowNone(false); + picker.setShowWeekNum(false); + var div = Blockly.WidgetDiv.DIV; + picker.render(div); + picker.setDate(goog.date.fromIsoString(this.getValue())); + return picker; +}; + /** * Hide the date picker. * @private From e802fb921bd136126d74d0e7cdd7b43a12e6fdcb Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 22 Sep 2017 16:07:32 -0700 Subject: [PATCH 0158/2135] Unify code for positioning the widget div (#1334) * New widget div functions, used in context menu code * Make all widget div positioning functions use the same argument order * Use new widget div functions for fields * share code for measuring menu size * Get rid of positionMenu * Update copyright date * Rebuild blockly_uncompressed because there's a new require in town --- core/contextmenu.js | 41 +++++++--------- core/field.js | 19 +++++--- core/field_colour.js | 39 ++++------------ core/field_date.js | 39 ++++------------ core/ui_menu_utils.js | 67 ++++++++++++++++++++++++++ core/utils.js | 20 ++++++++ core/widgetdiv.js | 106 ++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 239 insertions(+), 92 deletions(-) create mode 100644 core/ui_menu_utils.js diff --git a/core/contextmenu.js b/core/contextmenu.js index da1bcbd15e..e65b918c4c 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -30,6 +30,9 @@ */ goog.provide('Blockly.ContextMenu'); +goog.require('Blockly.utils'); +goog.require('Blockly.utils.uiMenu'); + goog.require('goog.dom'); goog.require('goog.events'); goog.require('goog.style'); @@ -115,37 +118,29 @@ Blockly.ContextMenu.populate_ = function(options, rtl) { * @private */ Blockly.ContextMenu.position_ = function(menu, e, rtl) { - // Record windowSize and scrollOffset before adding menu. - var windowSize = goog.dom.getViewportSize(); - var scrollOffset = goog.style.getViewportPageOffset(document); + // Record windowSize and scrollOffset before adding menu. + var viewportBBox = Blockly.utils.getViewportBBox(); + // This one is just a point, but we'll pretend that it's a rect so we can use + // some helper functions. + var anchorBBox = { + top: e.clientY + viewportBBox.top, + bottom: e.clientY + viewportBBox.top, + left: e.clientX + viewportBBox.left, + right: e.clientX + viewportBBox.left + }; Blockly.ContextMenu.createWidget_(menu); - var menuDom = menu.getElement(); - // Record menuSize after adding menu. - var menuSize = goog.style.getSize(menuDom); + var menuSize = Blockly.utils.uiMenu.getSize(menu); - // Position the menu. - var x = e.clientX + scrollOffset.x; - var y = e.clientY + scrollOffset.y; - // Flip menu vertically if off the bottom. - if (e.clientY + menuSize.height >= windowSize.height) { - y -= menuSize.height; - } - // Flip menu horizontally if off the edge. if (rtl) { - if (menuSize.width >= e.clientX) { - x += menuSize.width; - } - } else { - if (e.clientX + menuSize.width >= windowSize.width) { - x -= menuSize.width; - } + Blockly.utils.uiMenu.adjustBBoxesForRTL(viewportBBox, anchorBBox, menuSize); } - Blockly.WidgetDiv.position(x, y, windowSize, scrollOffset, rtl); + + Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, menuSize, rtl); // Calling menuDom.focus() has to wait until after the menu has been placed // correctly. Otherwise it will cause a page scroll to get the misplaced menu // in view. See issue #1329. - menuDom.focus(); + menu.getElement().focus(); }; /** diff --git a/core/field.js b/core/field.js index e70b0d3042..949380449b 100644 --- a/core/field.js +++ b/core/field.js @@ -502,16 +502,23 @@ Blockly.Field.prototype.getSize = function() { }; /** - * Returns the height and width of the field, - * accounting for the workspace scaling. - * @return {!goog.math.Size} Height and width. + * Returns the bounding box of the rendered field, accounting for workspace + * scaling. + * @return {!Object} An object with top, bottom, left, and right in pixels + * relative to the top left corner of the page (window coordinates). * @private */ Blockly.Field.prototype.getScaledBBox_ = function() { var size = this.getSize(); - // Create new object, so as to not return an uneditable SVGRect in IE. - return new goog.math.Size(size.width * this.sourceBlock_.workspace.scale, - size.height * this.sourceBlock_.workspace.scale); + var scaledHeight = size.height * this.sourceBlock_.workspace.scale; + var scaledWidth = size.width * this.sourceBlock_.workspace.scale; + var xy = this.getAbsoluteXY_(); + return { + top: xy.y, + bottom: xy.y + scaledHeight, + left: xy.x, + right: xy.x + scaledWidth + }; }; /** diff --git a/core/field_colour.js b/core/field_colour.js index c628db2126..65a4d91290 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -27,6 +27,8 @@ goog.provide('Blockly.FieldColour'); goog.require('Blockly.Field'); +goog.require('Blockly.utils'); + goog.require('goog.dom'); goog.require('goog.events'); goog.require('goog.style'); @@ -176,38 +178,17 @@ Blockly.FieldColour.prototype.showEditor_ = function() { Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, Blockly.FieldColour.widgetDispose_); - // Position the palette to line up with the field. - // Record windowSize and scrollOffset before adding the palette. - var windowSize = goog.dom.getViewportSize(); - var scrollOffset = goog.style.getViewportPageOffset(document); - var xy = this.getAbsoluteXY_(); - var borderBBox = this.getScaledBBox_(); - // Record paletteSize after adding the palette. + // Record viewport dimensions before adding the widget. + var viewportBBox = Blockly.utils.getViewportBBox(); + var anchorBBox = this.getScaledBBox_(); + + // Create and add the colour picker, then record the size. var picker = this.createWidget_(); var paletteSize = goog.style.getSize(picker.getElement()); - // Flip the palette vertically if off the bottom. - if (xy.y + paletteSize.height + borderBBox.height >= - windowSize.height + scrollOffset.y) { - xy.y -= paletteSize.height - 1; - } else { - xy.y += borderBBox.height - 1; - } - if (this.sourceBlock_.RTL) { - xy.x += borderBBox.width; - xy.x -= paletteSize.width; - // Don't go offscreen left. - if (xy.x < scrollOffset.x) { - xy.x = scrollOffset.x; - } - } else { - // Don't go offscreen right. - if (xy.x > windowSize.width + scrollOffset.x - paletteSize.width) { - xy.x = windowSize.width + scrollOffset.x - paletteSize.width; - } - } - Blockly.WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset, - this.sourceBlock_.RTL); + // Position the picker to line up with the field. + Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, paletteSize, + this.sourceBlock_.RTL); // Configure event handler. var thisField = this; diff --git a/core/field_date.js b/core/field_date.js index 96a0d3eb38..ca963d57f2 100644 --- a/core/field_date.js +++ b/core/field_date.js @@ -27,6 +27,8 @@ goog.provide('Blockly.FieldDate'); goog.require('Blockly.Field'); +goog.require('Blockly.utils'); + goog.require('goog.date'); goog.require('goog.dom'); goog.require('goog.events'); @@ -103,38 +105,17 @@ Blockly.FieldDate.prototype.showEditor_ = function() { Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, Blockly.FieldDate.widgetDispose_); - // Position the picker to line up with the field. - // Record windowSize and scrollOffset before adding the picker. - var windowSize = goog.dom.getViewportSize(); - var scrollOffset = goog.style.getViewportPageOffset(document); - var xy = this.getAbsoluteXY_(); - var borderBBox = this.getScaledBBox_(); + // Record viewport dimensions before adding the picker. + var viewportBBox = Blockly.utils.getViewportBBox(); + var anchorBBox = this.getScaledBBox_(); + + // Create and add the date picker, then record the size. var picker = this.createWidget_(); - // Record pickerSize after adding the date picker. var pickerSize = goog.style.getSize(picker.getElement()); - // Flip the picker vertically if off the bottom. - if (xy.y + pickerSize.height + borderBBox.height >= - windowSize.height + scrollOffset.y) { - xy.y -= pickerSize.height - 1; - } else { - xy.y += borderBBox.height - 1; - } - if (this.sourceBlock_.RTL) { - xy.x += borderBBox.width; - xy.x -= pickerSize.width; - // Don't go offscreen left. - if (xy.x < scrollOffset.x) { - xy.x = scrollOffset.x; - } - } else { - // Don't go offscreen right. - if (xy.x > windowSize.width + scrollOffset.x - pickerSize.width) { - xy.x = windowSize.width + scrollOffset.x - pickerSize.width; - } - } - Blockly.WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset, - this.sourceBlock_.RTL); + // Position the picker to line up with the field. + Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, pickerSize, + this.sourceBlock_.RTL); // Configure event handler. var thisField = this; diff --git a/core/ui_menu_utils.js b/core/ui_menu_utils.js new file mode 100644 index 0000000000..740873b33b --- /dev/null +++ b/core/ui_menu_utils.js @@ -0,0 +1,67 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2017 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Utility methods for working with the closure menu (goog.ui.menu). + * @author fenichel@google.com (Rachel Fenichel) + */ +'use strict'; + +/** + * @name Blockly.utils.uiMenu + * @namespace + **/ +goog.provide('Blockly.utils.uiMenu'); + +/** + * Get the size of a rendered goog.ui.Menu. + * @param {!goog.ui.Menu} menu The menu to measure. + * @return {!goog.math.Size} Object with width and height properties. + * @package + */ +Blockly.utils.uiMenu.getSize = function(menu) { + var menuDom = menu.getElement(); + var menuSize = goog.style.getSize(menuDom); + // Recalculate height for the total content, not only box height. + menuSize.height = menuDom.scrollHeight; + return menuSize; +}; + +/** + * Adjust the bounding boxes used to position the widget div to deal with RTL + * goog.ui.Menu positioning. In RTL mode the menu renders down and to the left + * of its start point, instead of down and to the right. Adjusting all of the + * bounding boxes accordingly allows us to use the same code for all widgets. + * This function in-place modifies the provided bounding boxes. + * @param {!Object} viewportBBox The bounding rectangle of the current viewport, + * in window coordinates. + * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window + * coordinates. + * @param {!goog.math.Size} menuSize The size of the menu that is inside the + * widget div, in window coordinates. + * @package + */ +Blockly.utils.uiMenu.adjustBBoxesForRTL = function(viewportBBox, anchorBBox, + menuSize) { + anchorBBox.left += menuSize.width; + anchorBBox.right += menuSize.width; + viewportBBox.left += menuSize.width; + viewportBBox.right += menuSize.width; +}; diff --git a/core/utils.js b/core/utils.js index a32380acf0..878b7965d4 100644 --- a/core/utils.js +++ b/core/utils.js @@ -968,3 +968,23 @@ Blockly.utils.isShadowArgumentReporter = function(block) { return (block.isShadow() && (block.type == 'argument_reporter_boolean' || block.type == 'argument_reporter_string_number')); }; + +/** + * Get the position of the current viewport in window coordinates. This takes + * scroll into account. + * @return {!Object} an object containing window width, height, and scroll + * position in window coordinates. + * @package + */ +Blockly.utils.getViewportBBox = function() { + // Pixels. + var windowSize = goog.dom.getViewportSize(); + // Pixels, in window coordinates. + var scrollOffset = goog.style.getViewportPageOffset(document); + return { + right: windowSize.width + scrollOffset.x, + bottom: windowSize.height + scrollOffset.y, + top: scrollOffset.y, + left: scrollOffset.x + }; +}; diff --git a/core/widgetdiv.js b/core/widgetdiv.js index 675bcd705b..77e048867e 100644 --- a/core/widgetdiv.js +++ b/core/widgetdiv.js @@ -193,8 +193,8 @@ Blockly.WidgetDiv.hideIfOwner = function(oldOwner) { /** * Position the widget at a given location. Prevent the widget from going * offscreen top or left (right in RTL). - * @param {number} anchorX Horizontal location (window coorditates, not body). - * @param {number} anchorY Vertical location (window coorditates, not body). + * @param {number} anchorX Horizontal location (window coordinates, not body). + * @param {number} anchorY Vertical location (window coordinates, not body). * @param {!goog.math.Size} windowSize Height/width of window. * @param {!goog.math.Coordinate} scrollOffset X/y of window scrollbars. * @param {boolean} rtl True if RTL, false if LTR. @@ -216,7 +216,103 @@ Blockly.WidgetDiv.position = function(anchorX, anchorY, windowSize, anchorX = scrollOffset.x; } } - Blockly.WidgetDiv.DIV.style.left = anchorX + 'px'; - Blockly.WidgetDiv.DIV.style.top = anchorY + 'px'; - Blockly.WidgetDiv.DIV.style.height = windowSize.height + 'px'; + Blockly.WidgetDiv.positionInternal_(anchorX, anchorY, windowSize.height); +}; + +/** + * Set the widget div's position and height. This function does nothing clever: + * it will not ensure that your widget div ends up in the visible window. + * @param {number} x Horizontal location (window coordinates, not body). + * @param {number} y Vertical location (window coordinates, not body). + * @param {number} height The height of the widget div (pixels). + * @private + */ +Blockly.WidgetDiv.positionInternal_ = function(x, y, height) { + Blockly.WidgetDiv.DIV.style.left = x + 'px'; + Blockly.WidgetDiv.DIV.style.top = y + 'px'; + Blockly.WidgetDiv.DIV.style.height = height + 'px'; +}; + +/** + * Position the widget div based on an anchor rectangle. + * The widget should be placed adjacent to but not overlapping the anchor + * rectangle. The preferred position is directly below and aligned to the left + * (ltr) or right (rtl) side of the anchor. + * @param {!Object} viewportBBox The bounding rectangle of the current viewport, + * in window coordinates. + * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window + * coordinates. + * @param {!goog.math.Size} widgetSize The size of the widget that is inside the + * widget div, in window coordinates. + * @param {boolean} rtl Whether the workspace is in RTL mode. This determines + * horizontal alignment. + * @package + */ +Blockly.WidgetDiv.positionWithAnchor = function(viewportBBox, anchorBBox, + widgetSize, rtl) { + var y = Blockly.WidgetDiv.calculateY_(viewportBBox, anchorBBox, widgetSize); + var x = Blockly.WidgetDiv.calculateX_(viewportBBox, anchorBBox, widgetSize, + rtl); + + Blockly.WidgetDiv.positionInternal_(x, y, widgetSize.height); +}; + +/** + * Calculate an x position (in window coordinates) such that the widget will not + * be offscreen on the right or left. + * @param {!Object} viewportBBox The bounding rectangle of the current viewport, + * in window coordinates. + * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window + * coordinates. + * @param {goog.math.Size} widgetSize The dimensions of the widget inside the + * widget div. + * @param {boolean} rtl Whether the Blockly workspace is in RTL mode. + * @return {number} A valid x-coordinate for the top left corner of the widget + * div, in window coordinates. + * @private + */ +Blockly.WidgetDiv.calculateX_ = function(viewportBBox, anchorBBox, widgetSize, + rtl) { + if (rtl) { + // Try to align the right side of the field and the right side of the widget. + var widgetLeft = anchorBBox.right - widgetSize.width; + // Don't go offscreen left. + var x = Math.max(widgetLeft, viewportBBox.left); + // But really don't go offscreen right: + return Math.min(x, viewportBBox.right - widgetSize.width); + } else { + // Try to align the left side of the field and the left side of the widget. + // Don't go offscreen right. + var x = Math.min(anchorBBox.left, + viewportBBox.right - widgetSize.width); + // But left is more important, because that's where the text is. + return Math.max(x, viewportBBox.left); + } +}; + +/** + * Calculate a y position (in window coordinates) such that the widget will not + * be offscreen on the top or bottom. + * @param {!Object} viewportBBox The bounding rectangle of the current viewport, + * in window coordinates. + * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window + * coordinates. + * @param {goog.math.Size} widgetSize The dimensions of the widget inside the + * widget div. + * @return {number} A valid y-coordinate for the top left corner of the widget + * div, in window coordinates. + * @private + */ +Blockly.WidgetDiv.calculateY_ = function(viewportBBox, anchorBBox, widgetSize) { + // Flip the widget vertically if off the bottom. + if (anchorBBox.bottom + widgetSize.height >= + viewportBBox.bottom) { + // The bottom of the widget is at the top of the field. + return anchorBBox.top - widgetSize.height; + // The widget could go off the top of the window, but it would also go off + // the bottom. The window is just too small. + } else { + // The top of the widget is at the bottom of the field. + return anchorBBox.bottom; + } }; From 3d37a8ae9eba90b98ab079fe0f44f00bea6d2e75 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 22 Sep 2017 16:10:47 -0700 Subject: [PATCH 0159/2135] Tests for widget div math. (#1338) * New widget div functions, used in context menu code * Make all widget div positioning functions use the same argument order * Use new widget div functions for fields * share code for measuring menu size * Get rid of positionMenu * Update copyright date * Rebuild blockly_uncompressed because there's a new require in town * Test for widget div math --- tests/jsunit/index.html | 1 + tests/jsunit/widget_div_test.js | 154 ++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 tests/jsunit/widget_div_test.js diff --git a/tests/jsunit/index.html b/tests/jsunit/index.html index b43943bca0..e3bcec5c4d 100644 --- a/tests/jsunit/index.html +++ b/tests/jsunit/index.html @@ -24,5 +24,6 @@ + diff --git a/tests/jsunit/widget_div_test.js b/tests/jsunit/widget_div_test.js new file mode 100644 index 0000000000..e43cde2175 --- /dev/null +++ b/tests/jsunit/widget_div_test.js @@ -0,0 +1,154 @@ +/** + * @license + * Blockly Tests + * + * Copyright 2017 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +goog.require('goog.testing'); + +function widgetdiv_testHelper_makeBBox(left, top, width, height) { + return { + left: left, + right: left + width, + top: top, + bottom: top + height + }; +} + +function widgetdiv_testHelper_makeSize(width, height) { + return { + width: width, + height: height + }; +} + +var widgetDiv_test_viewport = widgetdiv_testHelper_makeBBox(0, 0, 1000, 1000); +var widgetDiv_test_widgetSize = widgetdiv_testHelper_makeSize(100, 100); + +// Anchor is always 90 px wide and 90 px tall for this test. +var widgetDiv_test_anchorSize = 90; + +function widgetdiv_testHelper_makeAnchor(left, top) { + return { + left: left, + right: left + widgetDiv_test_anchorSize, + top: top, + bottom: top + widgetDiv_test_anchorSize + }; +} + +function test_widgetDiv_topConflict() { + var anchorTop = 50; + // Anchor placed close to the top. + var anchorBBox = widgetdiv_testHelper_makeAnchor(500, anchorTop); + + // The widget div should be placed just below the anchor. + var calculated = Blockly.WidgetDiv.calculateY_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize); + assertEquals(anchorTop + widgetDiv_test_anchorSize, calculated); +} + +function test_widgetDiv_bottomConflict() { + var anchorTop = 900; + // Anchor placed close to the bottom. + var anchorBBox = widgetdiv_testHelper_makeAnchor(500, anchorTop); + + // The widget div should be placed just above the anchor. + var calculated = Blockly.WidgetDiv.calculateY_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize); + assertEquals(anchorTop - widgetDiv_test_widgetSize.height, calculated); +} + +function test_widgetDiv_noYConflict() { + var anchorTop = 500; + // Anchor placed in the middle. + var anchorBBox = widgetdiv_testHelper_makeAnchor(500, anchorTop); + + // The widget div should be placed just below the anchor. + var calculated = Blockly.WidgetDiv.calculateY_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize); + assertEquals(anchorTop + widgetDiv_test_anchorSize, calculated); +} + + +function test_widgetDiv_leftConflict_LTR() { + var anchorLeft = 50; + // Anchor placed close to the left side. + var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500); + + // The widget div should be placed at the anchor. + var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize, false /* rtl */); + assertEquals(anchorLeft, calculated); +} + +function test_widgetDiv_rightConflict_LTR() { + var anchorLeft = 950; + // Anchor placed close to the right side. + var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500); + + // The widget div should be placed as far right as possible--at the edge of + // the screen. + var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize, false /* rtl */); + assertEquals(1000 - widgetDiv_test_widgetSize.width, calculated); +} + +function test_widgetDiv_noXConflict_LTR() { + var anchorLeft = 500; + // Anchor in the middle + var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500); + // The widget div should be placed just at the left side of the anchor. + var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize, false /* rtl */); + assertEquals(anchorLeft, calculated); +} + +function test_widgetDiv_leftConflict_RTL() { + var anchorLeft = 10; + // Anchor placed close to the left side. + var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500); + // The widget div should be placed as far left as possible--at the edge of + // the screen. + var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize, true /* rtl */); + assertEquals(0, calculated); +} + +function test_widgetDiv_rightConflict_RTL() { + var anchorLeft = 950; + // Anchor placed close to the right side. + var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500); + + // The widget div should be placed as far right as possible--at the edge of + // the screen. + var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize, true /* rtl */); + assertEquals(1000 - widgetDiv_test_widgetSize.width, calculated); +} + +function test_widgetDiv_noXConflict_RTL() { + var anchorLeft = 500; + // anchor placed in the middle + var anchorBBox = widgetdiv_testHelper_makeAnchor(anchorLeft, 500); + // The widget div should be placed at the right side of the anchor. + var calculated = Blockly.WidgetDiv.calculateX_(widgetDiv_test_viewport, + anchorBBox, widgetDiv_test_widgetSize, true /* rtl */); + assertEquals(anchorBBox.right - widgetDiv_test_widgetSize.width, calculated); +} + From 5e386d5bd0792023907b79ad2a0989ba6b583f2d Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 4 Oct 2017 10:16:32 -0700 Subject: [PATCH 0160/2135] Fix iOS toolbox flashing on tap (#1351) --- core/css.js | 1 + core/workspace_drag_surface_svg.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/css.js b/core/css.js index bbae9e98d9..b9f351f197 100644 --- a/core/css.js +++ b/core/css.js @@ -697,6 +697,7 @@ Blockly.Css.CONTENT = [ 'position: absolute;', 'font-family: "Helvetica Neue", Helvetica, sans-serif;', 'z-index: 40;', /* so blocks go over toolbox when dragging */ + '-webkit-tap-highlight-color: transparent;', /* issue #1345 */ '}', '.blocklyTreeRoot {', diff --git a/core/workspace_drag_surface_svg.js b/core/workspace_drag_surface_svg.js index 4bf932a455..6309a05a56 100644 --- a/core/workspace_drag_surface_svg.js +++ b/core/workspace_drag_surface_svg.js @@ -93,7 +93,7 @@ Blockly.WorkspaceDragSurfaceSvg.prototype.createDom = function() { 'xmlns:html': Blockly.HTML_NS, 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 'version': '1.1', - 'class': 'blocklyWsDragSurface blocklyOverflowVisible', + 'class': 'blocklyWsDragSurface blocklyOverflowVisible' }, null); this.container_.appendChild(this.SVG_); }; From 86a7b939cbd04f2d895027df8052df680d8d8364 Mon Sep 17 00:00:00 2001 From: Nick Allred Date: Mon, 18 Sep 2017 16:21:43 -0400 Subject: [PATCH 0161/2135] Subtract metrics.contentTop from metrics.viewTop so that the flyout does not begin to scroll down once the delta is smaller than the value of metrics.contentTop (#1309) --- core/flyout_vertical.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index 118cdfdce9..489e6c6059 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -371,7 +371,7 @@ Blockly.VerticalFlyout.prototype.wheel_ = function(e) { delta *= 10; } var metrics = this.getMetrics_(); - var pos = -this.workspace_.scrollY + delta; + var pos = (metrics.viewTop - metrics.contentTop) + delta; var limit = metrics.contentHeight - metrics.viewHeight; pos = Math.min(pos, limit); pos = Math.max(pos, 0); From 6fced52ef8e3502fa34858bc91186a0c88fdafab Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Oct 2017 15:27:26 -0400 Subject: [PATCH 0162/2135] Add new test to the vertical and horizontal test runner files --- tests/jsunit/horizontal_tests.html | 1 + tests/jsunit/vertical_tests.html | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/jsunit/horizontal_tests.html b/tests/jsunit/horizontal_tests.html index 789825482d..894ce12f0d 100644 --- a/tests/jsunit/horizontal_tests.html +++ b/tests/jsunit/horizontal_tests.html @@ -24,6 +24,7 @@ + diff --git a/tests/jsunit/vertical_tests.html b/tests/jsunit/vertical_tests.html index 4fd4a8bc3e..6b7e9a3a7e 100644 --- a/tests/jsunit/vertical_tests.html +++ b/tests/jsunit/vertical_tests.html @@ -23,6 +23,7 @@ + From d906095698c025642534b5b5cbfa2433c51c55b0 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 17 Oct 2017 15:19:50 -0700 Subject: [PATCH 0163/2135] Style improvements. No functional changes. (#1367) --- core/extensions.js | 44 ++++++++++++++++++++------------------------ core/utils.js | 29 +++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/core/extensions.js b/core/extensions.js index 63b02fab4f..3ea2fb310b 100644 --- a/core/extensions.js +++ b/core/extensions.js @@ -88,8 +88,8 @@ Blockly.Extensions.registerMixin = function(name, mixinObj) { * decompose are defined on the mixin. * @param {string} name The name of this mutator extension. * @param {!Object} mixinObj The values to mix in. - * @param {(function())=} opt_helperFn An optional function to apply after mixing - * in the object. + * @param {(function())=} opt_helperFn An optional function to apply after + * mixing in the object. * @param {Array.=} opt_blockList A list of blocks to appear in the * flyout of the mutator dialog. * @throws {Error} if the mutation is invalid or can't be applied to the block. @@ -153,8 +153,8 @@ Blockly.Extensions.apply = function(name, block, isMutator) { Blockly.Extensions.checkBlockHasMutatorProperties_(errorPrefix, block); } else { if (!Blockly.Extensions.mutatorPropertiesMatch_(mutatorProperties, block)) { - throw new Error('Error when applying extension "' + name + - '": mutation properties changed when applying a non-mutator extension.'); + throw new Error('Error when applying extension "' + name + '": ' + + 'mutation properties changed when applying a non-mutator extension.'); } } }; @@ -174,7 +174,7 @@ Blockly.Extensions.checkHasFunction_ = function(errorPrefix, func, 'missing required property "' + propertyName + '"'); } else if (typeof func != 'function') { throw new Error(errorPrefix + - '" required property "' + propertyName + '" must be a function'); + '" required property "' + propertyName + '" must be a function'); } }; @@ -214,9 +214,9 @@ Blockly.Extensions.checkMutatorDialog_ = function(object, errorPrefix) { var hasDecompose = object.decompose !== undefined; if (hasCompose && hasDecompose) { - if (typeof object.compose !== 'function') { + if (typeof object.compose != 'function') { throw new Error(errorPrefix + 'compose must be a function.'); - } else if (typeof object.decompose !== 'function') { + } else if (typeof object.decompose != 'function') { throw new Error(errorPrefix + 'decompose must be a function.'); } return true; @@ -240,8 +240,9 @@ Blockly.Extensions.checkBlockHasMutatorProperties_ = function(errorPrefix, if (typeof block.domToMutation !== 'function') { throw new Error(errorPrefix + 'Applying a mutator didn\'t add "domToMutation"'); } - if (typeof block.mutationToDom !== 'function') { - throw new Error(errorPrefix + 'Applying a mutator didn\'t add "mutationToDom"'); + if (typeof block.mutationToDom != 'function') { + throw new Error(errorPrefix + + 'Applying a mutator didn\'t add "mutationToDom"'); } // A block with a mutator isn't required to have a mutation dialog, but @@ -285,19 +286,16 @@ Blockly.Extensions.getMutatorProperties_ = function(block) { * @private */ Blockly.Extensions.mutatorPropertiesMatch_ = function(oldProperties, block) { - var match = true; var newProperties = Blockly.Extensions.getMutatorProperties_(block); if (newProperties.length != oldProperties.length) { - match = false; - } else { - for (var i = 0; i < newProperties.length; i++) { - if (oldProperties[i] != newProperties[i]) { - match = false; - } + return false; + } + for (var i = 0; i < newProperties.length; i++) { + if (oldProperties[i] != newProperties[i]) { + return false; } } - - return match; + return true; }; /** @@ -343,7 +341,7 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, lookupTable) var extensionFn = function() { if (this.type && blockTypesChecked.indexOf(this.type) === -1) { Blockly.Extensions.checkDropdownOptionsInTable_( - this, dropdownName, lookupTable); + this, dropdownName, lookupTable); blockTypesChecked.push(this.type); } @@ -352,7 +350,7 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, lookupTable) var tooltip = lookupTable[value]; if (tooltip == null) { if (blockTypesChecked.indexOf(this.type) === -1) { - // Warn for missing values on generated tooltips + // Warn for missing values on generated tooltips. var warning = 'No tooltip mapping for value ' + value + ' of field ' + dropdownName; if (this.type != null) { @@ -440,10 +438,8 @@ Blockly.Extensions.extensionParentTooltip_ = function() { this.tooltipWhenNotConnected_ = this.tooltip; this.setTooltip(function() { var parent = this.getParent(); - return (parent && - parent.getInputsInline() && - parent.tooltip) || - this.tooltipWhenNotConnected_; + return (parent && parent.getInputsInline() && parent.tooltip) || + this.tooltipWhenNotConnected_; }.bind(this)); }; Blockly.Extensions.register('parent_tooltip_when_inline', diff --git a/core/utils.js b/core/utils.js index 878b7965d4..d967325dbf 100644 --- a/core/utils.js +++ b/core/utils.js @@ -38,6 +38,20 @@ goog.require('goog.events.BrowserFeature'); goog.require('goog.math.Coordinate'); goog.require('goog.userAgent'); + +/** + * To allow ADVANCED_OPTIMIZATIONS, combining variable.name and variable['name'] + * is not possible. To access the exported Blockly.Msg.Something it needs to be + * accessed through the exact name that was exported. Note, that all the exports + * are happening as the last thing in the generated js files, so they won't be + * accessible before JavaScript loads! + * @return {!Object} The message array + * @private + */ +Blockly.utils.getMessageArray_ = function() { + return goog.global['Blockly']['Msg']; +}; + /** * Remove an attribute from a element even if it's in IE 10. * Similar to Element.removeAttribute() but it works on SVG elements in IE 10. @@ -194,10 +208,9 @@ Blockly.utils.getRelativeXY = function(element) { Blockly.utils.getInjectionDivXY_ = function(element) { var x = 0; var y = 0; - var scale = 1; while (element) { var xy = Blockly.utils.getRelativeXY(element); - scale = Blockly.utils.getScale_(element); + var scale = Blockly.utils.getScale_(element); x = (x * scale) + xy.x; y = (y * scale) + xy.y; var classes = element.getAttribute('class') || ''; @@ -449,18 +462,18 @@ Blockly.utils.replaceMessageReferences = function(message) { * Otherwise, false. */ Blockly.utils.checkMessageReferences = function(message) { - var isValid = true; // True until a bad reference is found + var isValid = true; // True until a bad reference is found. var regex = /%{BKY_([a-zA-Z][a-zA-Z0-9_]*)}/g; var match = regex.exec(message); - while (match != null) { + while (match) { var msgKey = match[1]; - if (Blockly.Msg[msgKey] == null) { + if (Blockly.utils.getMessageArray_()[msgKey] == undefined) { console.log('WARNING: No message string for %{BKY_' + msgKey + '}.'); isValid = false; } - // Re-run on remainder of sting. + // Re-run on remainder of string. message = message.substring(match.index + msgKey.length + 1); match = regex.exec(message); } @@ -469,7 +482,7 @@ Blockly.utils.checkMessageReferences = function(message) { }; /** - * Internal implemention of the message reference and interpolation token + * Internal implementation of the message reference and interpolation token * parsing used by tokenizeInterpolation() and replaceMessageReferences(). * @param {string} message Text which might contain string table references and * interpolation tokens. @@ -624,7 +637,7 @@ Blockly.utils.genUid = function() { * Legal characters for the unique ID. Should be all on a US keyboard. * No characters that conflict with XML or JSON. Requests to remove additional * 'problematic' characters from this soup will be denied. That's your failure - * to properly escape in your own environment. Issues #251, #625, #682. + * to properly escape in your own environment. Issues #251, #625, #682, #1304. * @private */ Blockly.utils.genUid.soup_ = '!#$%()*+,-./:;=?@[]^_`{|}~' + From 8f5156c72cc83e1f6afe432489b3614c5dfc7e27 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 19 Oct 2017 17:10:42 -0400 Subject: [PATCH 0164/2135] Fix lost newline --- core/workspace_svg.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/workspace_svg.js b/core/workspace_svg.js index d6a079d076..085098a998 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -954,7 +954,8 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) { try { var block = Blockly.Xml.domToBlock(xmlBlock, this); // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste - Blockly.utils.changeObscuredShadowIds(block); // Move the duplicate to original position. + Blockly.utils.changeObscuredShadowIds(block); + // Move the duplicate to original position. var blockX = parseInt(xmlBlock.getAttribute('x'), 10); var blockY = parseInt(xmlBlock.getAttribute('y'), 10); if (!isNaN(blockX) && !isNaN(blockY)) { From 5e3073c441be257dace5f6aa53be076d1ce624e7 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 19 Oct 2017 16:06:12 -0700 Subject: [PATCH 0165/2135] Hack to fix 1127 while we consider strict connection types --- core/connection.js | 12 +++++++ tests/jsunit/connection_test.js | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/core/connection.js b/core/connection.js index 5d51757909..8cc1bda521 100644 --- a/core/connection.js +++ b/core/connection.js @@ -63,6 +63,8 @@ Blockly.Connection.REASON_TARGET_NULL = 3; Blockly.Connection.REASON_CHECKS_FAILED = 4; Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5; Blockly.Connection.REASON_SHADOW_PARENT = 6; +// Fixes #1127, but may be the wrong solution. +Blockly.Connection.REASON_CUSTOM_PROCEDURE = 7; /** * Connection this connection connects to. Null if not connected. @@ -292,9 +294,11 @@ Blockly.Connection.prototype.canConnectWithReason_ = function(target) { if (this.isSuperior()) { var blockA = this.sourceBlock_; var blockB = target.getSourceBlock(); + var superiorConn = this; } else { var blockB = this.sourceBlock_; var blockA = target.getSourceBlock(); + var superiorConn = target; } if (blockA && blockA == blockB) { return Blockly.Connection.REASON_SELF_CONNECTION; @@ -306,6 +310,12 @@ Blockly.Connection.prototype.canConnectWithReason_ = function(target) { return Blockly.Connection.REASON_CHECKS_FAILED; } else if (blockA.isShadow() && !blockB.isShadow()) { return Blockly.Connection.REASON_SHADOW_PARENT; + } else if (blockA.type == 'procedures_defnoreturn' && + blockB.type != 'procedures_callnoreturn_internal' && + superiorConn == blockA.getInput('custom_block').connection ) { + // Hack to fix #1127: Fail attempts to connect to the custom_block input + // on a defnoreturn block, unless the connecting block is a specific type. + return Blockly.Connection.REASON_CUSTOM_PROCEDURE; } return Blockly.Connection.CAN_CONNECT; }; @@ -336,6 +346,8 @@ Blockly.Connection.prototype.checkConnection_ = function(target) { throw msg; case Blockly.Connection.REASON_SHADOW_PARENT: throw 'Connecting non-shadow to shadow block.'; + case Blockly.Connection.REASON_CUSTOM_PROCEDURE: + throw 'Trying to replace a shadow on a custom procedure definition.'; default: throw 'Unknown connection failure: this should never happen!'; } diff --git a/tests/jsunit/connection_test.js b/tests/jsunit/connection_test.js index 3768e0ca7f..663aa9e14a 100644 --- a/tests/jsunit/connection_test.js +++ b/tests/jsunit/connection_test.js @@ -343,3 +343,62 @@ function testCheckConnection_Okay() { connectionTest_tearDown(); } + +function test_canConnectWithReason_Procedures_WrongBlockType() { + var sharedWorkspace = {}; + var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT); + one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); + one.sourceBlock_.type = 'procedures_defnoreturn'; + // Make one be the connection on its source block's input. + one.sourceBlock_.getInput = function() { + return { + connection: one + }; + }; + + var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT); + two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); + // Fail because two's source block is the wrong type. + two.sourceBlock_.type = 'wrong_type'; + assertEquals(Blockly.Connection.REASON_CUSTOM_PROCEDURE, + one.canConnectWithReason_(two)); +} + +function test_canConnectWithReason_Procedures_Pass() { + var sharedWorkspace = {}; + var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT); + one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); + one.sourceBlock_.type = 'procedures_defnoreturn'; + // Make one be the connection on its source block's input. + one.sourceBlock_.getInput = function() { + return { + connection: one + }; + }; + var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT); + two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); + two.sourceBlock_.type = 'procedures_callnoreturn_internal'; + assertEquals(Blockly.Connection.CAN_CONNECT, + one.canConnectWithReason_(two)); +} + +function test_canConnectWithReason_Procedures_NextConnection() { + var sharedWorkspace = {}; + var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT); + one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); + one.sourceBlock_.type = 'procedures_defnoreturn'; + // One is the next connection, not an input connection + one.sourceBlock_.nextConnection = one; + one.sourceBlock_.getInput = function() { + return { + connection: null + }; + }; + var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT); + two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); + // It should be okay, even if two's source block has the wrong type, because + // it's not trying to connect to the input. + two.sourceBlock_.type = 'wrong_type'; + assertEquals(Blockly.Connection.CAN_CONNECT, + one.canConnectWithReason_(two)); +} From 8263afcba57a6a637f8da0a173a910d5eae933be Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 20 Oct 2017 15:06:48 -0400 Subject: [PATCH 0166/2135] Revert "Implement Blockly.Events.filter in linear time (#1205)" This reverts commit 910284856e8016980790f2cc807258ab3bfcace3. --- core/events.js | 64 ++++++++++-------- tests/jsunit/event_test.js | 133 ------------------------------------- 2 files changed, 36 insertions(+), 161 deletions(-) diff --git a/core/events.js b/core/events.js index 064d17daab..80a2620fa8 100644 --- a/core/events.js +++ b/core/events.js @@ -174,37 +174,45 @@ Blockly.Events.filter = function(queueIn, forward) { // Undo is merged in reverse order. queue.reverse(); } - var mergedQueue = []; - var hash = Object.create(null); - // Merge duplicates. - for (var i = 0, event; event = queue[i]; i++) { - if (!event.isNull()) { - var key = [event.type, event.blockId, event.workspaceId].join(' '); - var lastEvent = hash[key]; - if (!lastEvent) { - hash[key] = event; - mergedQueue.push(event); - } else if (event.type == Blockly.Events.MOVE) { - // Merge move events. - lastEvent.newParentId = event.newParentId; - lastEvent.newInputName = event.newInputName; - lastEvent.newCoordinate = event.newCoordinate; - } else if (event.type == Blockly.Events.CHANGE && - event.element == lastEvent.element && - event.name == lastEvent.name) { - // Merge change events. - lastEvent.newValue = event.newValue; - } else if (event.type == Blockly.Events.UI && - event.element == 'click' && - (lastEvent.element == 'commentOpen' || - lastEvent.element == 'mutatorOpen' || - lastEvent.element == 'warningOpen')) { - // Merge click events. - lastEvent.newValue = event.newValue; + // Merge duplicates. O(n^2), but n should be very small. + for (var i = 0, event1; event1 = queue[i]; i++) { + for (var j = i + 1, event2; event2 = queue[j]; j++) { + if (event1.type == event2.type && + event1.blockId == event2.blockId && + event1.workspaceId == event2.workspaceId) { + if (event1.type == Blockly.Events.MOVE) { + // Merge move events. + event1.newParentId = event2.newParentId; + event1.newInputName = event2.newInputName; + event1.newCoordinate = event2.newCoordinate; + queue.splice(j, 1); + j--; + } else if (event1.type == Blockly.Events.CHANGE && + event1.element == event2.element && + event1.name == event2.name) { + // Merge change events. + event1.newValue = event2.newValue; + queue.splice(j, 1); + j--; + } else if (event1.type == Blockly.Events.UI && + event2.element == 'click' && + (event1.element == 'commentOpen' || + event1.element == 'mutatorOpen' || + event1.element == 'warningOpen')) { + // Merge change events. + event1.newValue = event2.newValue; + queue.splice(j, 1); + j--; + } } } } - queue = mergedQueue; + // Remove null events. + for (var i = queue.length - 1; i >= 0; i--) { + if (queue[i].isNull()) { + queue.splice(i, 1); + } + } if (!forward) { // Restore undo order. queue.reverse(); diff --git a/tests/jsunit/event_test.js b/tests/jsunit/event_test.js index d5583089e3..c019515e94 100644 --- a/tests/jsunit/event_test.js +++ b/tests/jsunit/event_test.js @@ -392,136 +392,3 @@ function test_varBackard_runForward() { checkVariableValues(workspace, 'name1', 'type1', 'id1'); eventTest_tearDown(); } - -function test_events_filter() { - eventTest_setUpWithMockBlocks(); - var block1 = workspace.newBlock('field_variable_test_block', '1'); - var events = [ - new Blockly.Events.BlockCreate(block1), - new Blockly.Events.BlockMove(block1), - new Blockly.Events.BlockChange(block1, 'field', 'VAR', 'item', 'item1'), - new Blockly.Events.Ui(block1, 'click') - ]; - var filteredEvents = Blockly.Events.filter(events, true); - assertEquals(4, filteredEvents.length); // no event should have been removed. - // test that the order hasn't changed - assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate); - assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove); - assertTrue(filteredEvents[2] instanceof Blockly.Events.BlockChange); - assertTrue(filteredEvents[3] instanceof Blockly.Events.Ui); -} - -function test_events_filterForward() { - eventTest_setUpWithMockBlocks(); - var block1 = workspace.newBlock('field_variable_test_block', '1'); - var events = [ - new Blockly.Events.BlockCreate(block1), - ]; - helper_addMoveEvent(events, block1, 1, 1); - helper_addMoveEvent(events, block1, 2, 2); - helper_addMoveEvent(events, block1, 3, 3); - var filteredEvents = Blockly.Events.filter(events, true); - assertEquals(2, filteredEvents.length); // duplicate moves should have been removed. - // test that the order hasn't changed - assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate); - assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove); - assertEquals(3, filteredEvents[1].newCoordinate.x); - assertEquals(3, filteredEvents[1].newCoordinate.y); - eventTest_tearDownWithMockBlocks(); -} - -function test_events_filterBackward() { - eventTest_setUpWithMockBlocks(); - var block1 = workspace.newBlock('field_variable_test_block', '1'); - var events = [ - new Blockly.Events.BlockCreate(block1), - ]; - helper_addMoveEvent(events, block1, 1, 1); - helper_addMoveEvent(events, block1, 2, 2); - helper_addMoveEvent(events, block1, 3, 3); - var filteredEvents = Blockly.Events.filter(events, false); - assertEquals(2, filteredEvents.length); // duplicate event should have been removed. - // test that the order hasn't changed - assertTrue(filteredEvents[0] instanceof Blockly.Events.BlockCreate); - assertTrue(filteredEvents[1] instanceof Blockly.Events.BlockMove); - assertEquals(1, filteredEvents[1].newCoordinate.x); - assertEquals(1, filteredEvents[1].newCoordinate.y); - eventTest_tearDownWithMockBlocks(); -} - -function test_events_filterDifferentBlocks() { - eventTest_setUpWithMockBlocks(); - var block1 = workspace.newBlock('field_variable_test_block', '1'); - var block2 = workspace.newBlock('field_variable_test_block', '2'); - var events = [ - new Blockly.Events.BlockCreate(block1), - new Blockly.Events.BlockMove(block1), - new Blockly.Events.BlockCreate(block2), - new Blockly.Events.BlockMove(block2) - ]; - var filteredEvents = Blockly.Events.filter(events, true); - assertEquals(4, filteredEvents.length); // no event should have been removed. - eventTest_tearDownWithMockBlocks(); -} - -function test_events_mergeMove() { - eventTest_setUpWithMockBlocks(); - var block1 = workspace.newBlock('field_variable_test_block', '1'); - var events = []; - helper_addMoveEvent(events, block1, 0, 0); - helper_addMoveEvent(events, block1, 1, 1); - var filteredEvents = Blockly.Events.filter(events, true); - assertEquals(1, filteredEvents.length); // second move event merged into first - assertEquals(1, filteredEvents[0].newCoordinate.x); - assertEquals(1, filteredEvents[0].newCoordinate.y); - eventTest_tearDownWithMockBlocks(); -} - -function test_events_mergeChange() { - eventTest_setUpWithMockBlocks(); - var block1 = workspace.newBlock('field_variable_test_block', '1'); - var events = [ - new Blockly.Events.Change(block1, 'field', 'VAR', 'item', 'item1'), - new Blockly.Events.Change(block1, 'field', 'VAR', 'item1', 'item2') - ]; - var filteredEvents = Blockly.Events.filter(events, true); - assertEquals(1, filteredEvents.length); // second change event merged into first - assertEquals('item', filteredEvents[0].oldValue); - assertEquals('item2', filteredEvents[0].newValue); - eventTest_tearDownWithMockBlocks(); -} - -function test_events_mergeUi() { - eventTest_setUpWithMockBlocks(); - var block1 = workspace.newBlock('field_variable_test_block', '1'); - var block2 = workspace.newBlock('field_variable_test_block', '2'); - var block3 = workspace.newBlock('field_variable_test_block', '3'); - var events = [ - new Blockly.Events.Ui(block1, 'commentOpen', 'false', 'true'), - new Blockly.Events.Ui(block1, 'click', 'false', 'true'), - new Blockly.Events.Ui(block2, 'mutatorOpen', 'false', 'true'), - new Blockly.Events.Ui(block2, 'click', 'false', 'true'), - new Blockly.Events.Ui(block3, 'warningOpen', 'false', 'true'), - new Blockly.Events.Ui(block3, 'click', 'false', 'true') - ]; - var filteredEvents = Blockly.Events.filter(events, true); - assertEquals(3, filteredEvents.length); // click event merged into corresponding *Open event - assertEquals('commentOpen', filteredEvents[0].element); - assertEquals('mutatorOpen', filteredEvents[1].element); - assertEquals('warningOpen', filteredEvents[2].element); - eventTest_tearDownWithMockBlocks(); -} - -/** - * Helper function to simulate block move events. - * - * @param {!Array.} events a queue of events. - * @param {!Blockly.Block} block the block to be moved - * @param {number} newX new X coordinate of the block - * @param {number} newY new Y coordinate of the block - */ -function helper_addMoveEvent(events, block, newX, newY) { - events.push(new Blockly.Events.BlockMove(block)); - block.xy_ = new goog.math.Coordinate(newX, newY); - events[events.length-1].recordNew(); -} From cb058a69af5edccf05b3b2dd1e66d5d7670d223e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 20 Oct 2017 13:58:20 -0700 Subject: [PATCH 0167/2135] Add a serializable attribute to field, distinct from EDITABLE --- core/block.js | 12 ++++++------ core/field.js | 12 +++++++++++- core/field_label_editable.js | 29 ++++++++++++++++++++--------- core/field_variable_getter.js | 15 +++++++++++++-- core/xml.js | 2 +- 5 files changed, 51 insertions(+), 19 deletions(-) diff --git a/core/block.js b/core/block.js index 0a064a44f2..d57ee8335a 100644 --- a/core/block.js +++ b/core/block.js @@ -31,7 +31,7 @@ goog.require('Blockly.Colours'); goog.require('Blockly.Comment'); goog.require('Blockly.Connection'); goog.require('Blockly.Extensions'); -goog.require('Blockly.FieldLabelEditable'); +goog.require('Blockly.FieldLabelSerializable'); goog.require('Blockly.FieldVariableGetter'); goog.require('Blockly.Input'); goog.require('Blockly.Mutator'); @@ -1337,7 +1337,7 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { field = Blockly.Block.newFieldLabelFromJson_(element); break; case 'field_label_editable': - field = Blockly.Block.newFieldLabelEditableFromJson_(element); + field = Blockly.Block.newFieldLabelSerializableFromJson_(element); break; case 'field_input': field = Blockly.Block.newFieldTextInputFromJson_(element); @@ -1449,15 +1449,15 @@ Blockly.Block.newFieldLabelFromJson_ = function(options) { }; /** - * Helper function to construct a FieldLabelEditable from a JSON arg object, + * Helper function to construct a FieldLabelSerializable from a JSON arg object, * dereferencing any string table references. * @param {!Object} options A JSON object with options (text, and class). - * @returns {!Blockly.FieldLabelEditable} The new label. + * @returns {!Blockly.FieldLabelSerializable} The new label. * @private */ -Blockly.Block.newFieldLabelEditableFromJson_ = function(options) { +Blockly.Block.newFieldLabelSerializableFromJson_ = function(options) { var text = Blockly.utils.replaceMessageReferences(options['text']); - return new Blockly.FieldLabelEditable(text, options['class']); + return new Blockly.FieldLabelSerializable(text, options['class']); }; /** diff --git a/core/field.js b/core/field.js index 949380449b..100fec973b 100644 --- a/core/field.js +++ b/core/field.js @@ -132,10 +132,20 @@ Blockly.Field.prototype.validator_ = null; Blockly.Field.NBSP = '\u00A0'; /** - * Editable fields are saved by the XML renderer, non-editable fields are not. + * Editable fields usually show some sort of UI for the user to change them. + * @type {boolean} + * @public */ Blockly.Field.prototype.EDITABLE = true; +/** + * Serializable fields are saved by the XML renderer, non-serializable fields + * are not. Editable fields should be serialized. + * @type {boolean} + * @public + */ +Blockly.Field.prototype.SERIALIZABLE = Blockly.Field.prototype.EDITABLE; + /** * Attach this field to a block. * @param {!Blockly.Block} block The block containing this field. diff --git a/core/field_label_editable.js b/core/field_label_editable.js index 1a7e9dd6b9..3201fddb39 100644 --- a/core/field_label_editable.js +++ b/core/field_label_editable.js @@ -20,12 +20,12 @@ /** * @fileoverview Serialized label field. Behaves like a normal label but is - * always serialized to XML. + * always serialized to XML. It may only be edited programmatically. * @author fenichel@google.com (Rachel Fenichel) */ 'use strict'; -goog.provide('Blockly.FieldLabelEditable'); +goog.provide('Blockly.FieldLabelSerializable'); goog.require('Blockly.FieldLabel'); @@ -38,25 +38,36 @@ goog.require('Blockly.FieldLabel'); * @constructor * */ -Blockly.FieldLabelEditable = function(text, opt_class) { - Blockly.FieldLabelEditable.superClass_.constructor.call(this, text, +Blockly.FieldLabelSerializable = function(text, opt_class) { + Blockly.FieldLabelSerializable.superClass_.constructor.call(this, text, opt_class); // Used in base field rendering, but we don't need it. this.arrowWidth_ = 0; }; -goog.inherits(Blockly.FieldLabelEditable, Blockly.FieldLabel); +goog.inherits(Blockly.FieldLabelSerializable, Blockly.FieldLabel); /** - * Editable fields are saved by the XML renderer, non-editable fields are not. + * Editable fields usually show some sort of UI for the user to change them. + * This field should be serialized, but only edited programmatically. + * @type {boolean} + * @public */ -Blockly.FieldLabelEditable.prototype.EDITABLE = true; +Blockly.FieldLabelSerializable.prototype.EDITABLE = false; + +/** + * Serializable fields are saved by the XML renderer, non-serializable fields + * are not. This field should be serialized, but only edited programmatically. + * @type {boolean} + * @public + */ +Blockly.FieldLabelSerializable.prototype.SERIALIZABLE = true; /** * Updates the width of the field. This calls getCachedWidth which won't cache * the approximated width on IE/Edge when `getComputedTextLength` fails. Once * it eventually does succeed, the result will be cached. **/ -Blockly.FieldLabelEditable.prototype.updateWidth = function() { +Blockly.FieldLabelSerializable.prototype.updateWidth = function() { // Set width of the field. // Unlike the base Field class, this doesn't add space to editable fields. this.size_.width = Blockly.Field.getCachedWidth(this.textElement_); @@ -67,7 +78,7 @@ Blockly.FieldLabelEditable.prototype.updateWidth = function() { * Saves the computed width in a property. * @private */ -Blockly.FieldLabelEditable.prototype.render_ = function() { +Blockly.FieldLabelSerializable.prototype.render_ = function() { if (this.visible_ && this.textElement_) { // Replace the text. goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_)); diff --git a/core/field_variable_getter.js b/core/field_variable_getter.js index 4ee6a76e7f..3e8bc41f62 100644 --- a/core/field_variable_getter.js +++ b/core/field_variable_getter.js @@ -45,9 +45,20 @@ Blockly.FieldVariableGetter = function(text, name) { goog.inherits(Blockly.FieldVariableGetter, Blockly.Field); /** - * Editable fields are saved by the XML renderer, non-editable fields are not. + * Editable fields usually show some sort of UI for the user to change them. + * This field should be serialized, but only edited programmatically. + * @type {boolean} + * @public */ -Blockly.FieldVariableGetter.prototype.EDITABLE = true; +Blockly.FieldVariableGetter.prototype.EDITABLE = false; + +/** + * Serializable fields are saved by the XML renderer, non-serializable fields + * are not. This field should be serialized, but only edited programmatically. + * @type {boolean} + * @public + */ +Blockly.FieldVariableGetter.prototype.SERIALIZABLE = true; /** * Install this field on a block. diff --git a/core/xml.js b/core/xml.js index 7c6b83a737..ab153656d9 100644 --- a/core/xml.js +++ b/core/xml.js @@ -107,7 +107,7 @@ Blockly.Xml.blockToDom = function(block, opt_noId) { } } function fieldToDom(field) { - if (field.name && field.EDITABLE) { + if (field.name && field.SERIALIZABLE) { var container = goog.dom.createDom('field', null, field.getValue()); container.setAttribute('name', field.name); if (field instanceof Blockly.FieldVariable || field instanceof From 0ea49318d31f3b2c42bcdd5c07f8ca3fef5830bc Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 20 Oct 2017 14:00:09 -0700 Subject: [PATCH 0168/2135] rename field_label_editable to field_label_serializable --- core/{field_label_editable.js => field_label_serializable.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/{field_label_editable.js => field_label_serializable.js} (100%) diff --git a/core/field_label_editable.js b/core/field_label_serializable.js similarity index 100% rename from core/field_label_editable.js rename to core/field_label_serializable.js From 3b09a538555935a546eaa7b176cde693df3f0d36 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Fri, 20 Oct 2017 15:09:42 -0700 Subject: [PATCH 0169/2135] Get workspace_svg test to pass in scratch-blocks. It is off in another directory outside of jsunit so it is not run on travis... There are also some js errors reported that should be fixed too. But minor progress at least. (#1164) --- tests/workspace_svg/index.html | 364 ++-------------------- tests/workspace_svg/workspace_svg_test.js | 2 +- 2 files changed, 21 insertions(+), 345 deletions(-) diff --git a/tests/workspace_svg/index.html b/tests/workspace_svg/index.html index 65890169ad..8b130ab76c 100644 --- a/tests/workspace_svg/index.html +++ b/tests/workspace_svg/index.html @@ -3,17 +3,12 @@ Blockly Workspace SVG testing - + + + - - - - - - - - + @@ -51,346 +46,27 @@

Blockly Workspace testing

+ - '); Blockly.Xml.domToWorkspace(dom, workspace); @@ -225,7 +225,7 @@ function test_domToWorkspace_VariablesAtTop_MissingType() { ' name1' + ' ' + ' ' + - ' name3' + + ' name3' + ' ' + ''); Blockly.Xml.domToWorkspace(dom, workspace); @@ -248,7 +248,7 @@ function test_domToWorkspace_VariablesAtTop_MismatchBlockType() { ' name1' + ' ' + ' ' + - ' name1' + + ' name1' + ' ' + ''); Blockly.Xml.domToWorkspace(dom, workspace); @@ -408,3 +408,28 @@ function test_fieldIsNotSerialized() { xmlTest_tearDownWithMockBlocks(); } + +function test_variableFieldXml_caseSensitive() { + var id = 'testId'; + var type = 'testType'; + var name = 'testName'; + + var mockVariableModel = { + type: type, + name: name, + getId: function() { + return id; + } + }; + + var generatedXml = Blockly.Variables.generateVariableFieldXml_(mockVariableModel); + // The field contains this XML tag as a result of how we're generating this + // XML. This is not desirable, but the goal of this test is to make sure + // we're preserving case-sensitivity. + var xmlns = 'xmlns="http://www.w3.org/1999/xhtml"'; + var goldenXml = + '' + name + ''; + assertEquals(goldenXml, generatedXml); +} From 86c5f3b3f2fbd7e21518ce87971254f3ab472a55 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 6 Nov 2017 11:48:04 -0500 Subject: [PATCH 0212/2135] Bring back the list data category --- core/data_category.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/data_category.js b/core/data_category.js index 4165ec0511..acadfaf071 100644 --- a/core/data_category.js +++ b/core/data_category.js @@ -62,8 +62,7 @@ Blockly.DataCategory = function(workspace) { } // Now add list variables to the flyout - // Disable new list button until it is supported in scratch-vm - // Blockly.DataCategory.addCreateButton(xmlList, workspace, 'LIST'); + Blockly.DataCategory.addCreateButton(xmlList, workspace, 'LIST'); variableModelList = workspace.getVariablesOfType('list'); variableModelList.sort(Blockly.VariableModel.compareByName); for (var i = 0; i < variableModelList.length; i++) { From 5010a330af0930e75b62823c7b6fbd1c3cbc65c9 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 6 Nov 2017 15:12:34 -0800 Subject: [PATCH 0213/2135] Handle repeated mutations on the internal procedure caller block --- blocks_vertical/procedures.js | 154 +++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 67 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index f6eb0abc2f..a89e5fe5b3 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -197,19 +197,11 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) throw new Error( 'Found an custom procedure with an invalid type: ' + inputType); } - newLabel = component.substring(2).trim(); - var id = this.argumentIds_[inputCount]; - var oldBlock = null; - if (connectionMap && (id in connectionMap)) { - oldBlock = connectionMap[id]; - } - - var inputName = inputPrefix + (inputCount++); - var input = this.createInput_(inputType, inputName, oldBlock, id, - connectionMap); - params[id] = input; + var inputName = inputPrefix + inputCount; + this.createInput_(inputType, inputName, inputCount, connectionMap, params); + inputCount++; } else { newLabel = component.trim(); } @@ -295,6 +287,37 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) } }; +/** + * Create a new shadow block and attach it to the given input. + * @param {!Blockly.Input} input The value input to attach a block to. + * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * @private + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.attachShadowCallerInternal_ = function( + input, inputType, argumentName) { + var blockType = ''; + switch (inputType) { + case 'n': + blockType = 'argument_reporter_string_number'; + break; + case 'b': + blockType = 'argument_reporter_boolean'; + break; + case 's': + blockType = 'argument_reporter_string_number'; + break; + } + var newBlock = this.workspace.newBlock(blockType); + newBlock.setShadow(true); + newBlock.setFieldValue(argumentName, 'VALUE'); + if (!this.isInsertionMarker()) { + newBlock.initSvg(); + newBlock.render(false); + } + newBlock.outputConnection.connect(input.connection); +}; + /** * Create an input and attach the correct block to it. * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). @@ -309,17 +332,55 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(inputType, - inputName, oldBlock, id, connectionMap) { - var input = this.appendValueInput(inputName); +Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, + count, connectionMap, params) { + var id = this.argumentIds_[count]; + var oldBlock = null; + if (connectionMap && (id in connectionMap)) { + oldBlock = connectionMap[id]; + } + var input = this.appendValueInput(name); if (connectionMap && oldBlock) { - this.reattachBlock_(input, inputType, oldBlock, id, connectionMap); + this.reattachBlock_(input, type, oldBlock, id, connectionMap); } else { - this.attachShadow_(input, inputType); + this.attachShadow_(input, type); } - return input; + params[id] = input; }; + +/** + * Create an input and attach the correct block to it. + * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {string} inputName The name to use when adding the input to the block. + * @param {Blockly.Block} oldBlock The block that needs to be attached to the + * input, or null if none existed. + * @param {string} id The ID of the current parameter. + * @param {!Object.} connectionMap An object mapping + * parameter IDs to the blocks that were connected to those IDs at the + * beginning of the mutation. + * @return {!Blockly.Input} The newly created value input. + * @private + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, + name, count, connectionMap, params) { + var id = this.argumentIds_[count]; + var oldBlock = null; + if (connectionMap && (id in connectionMap)) { + oldBlock = connectionMap[id]; + } + var input = this.appendValueInput(name); + if (connectionMap && oldBlock) { + this.reattachBlock_(input, type, oldBlock, id, connectionMap); + } else { + var argumentText = this.argumentNames_[count]; + this.attachShadow_(input, type, argumentText); + } + params[id] = input; +}; + + /** * Update the block's structure and appearance to match the internally stored * mutation. @@ -414,56 +475,15 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, - updateDisplay_: function() { - var params = {}; - // Split the proc into components, by %n, %b, and %s (ignoring escaped). - var procComponents = this.procCode_.split(/(?=[^\\]\%[nbs])/); - procComponents = procComponents.map(function(c) { - return c.trim(); // Strip whitespace. - }); - // Create inputs and shadow blocks as appropriate. - var inputPrefix = 'input'; - var inputCount = 0; - for (var i = 0, component; component = procComponents[i]; i++) { - var newLabel; - if (component.substring(0, 1) == '%') { - var inputType = component.substring(1, 2); - newLabel = component.substring(2).trim(); - var inputName = inputPrefix + inputCount; - var blockType = ''; - switch (inputType) { - case 'n': - blockType = 'argument_reporter_string_number'; - break; - case 'b': - blockType = 'argument_reporter_boolean'; - break; - case 's': - blockType = 'argument_reporter_string_number'; - break; - } - if (blockType) { - var id = this.argumentIds_[inputCount]; - - var argumentName = this.argumentNames_[inputCount]; - var shadow = goog.dom.createDom('shadow'); - shadow.setAttribute('type', blockType); - var field = goog.dom.createDom('field', null, argumentName); - field.setAttribute('name', 'VALUE'); - shadow.appendChild(field); - var input = this.appendValueInput(inputName); - var newBlock = Blockly.Xml.domToBlock(shadow, this.workspace); - newBlock.outputConnection.connect(input.connection); - params[id] = input.connection; - } - inputCount++; - } else { - newLabel = component.trim(); - } - this.appendDummyInput().appendField(newLabel.replace(/\\%/, '%')); - } - this.paramMap_ = params; - } + removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, + disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, + deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, + createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, + buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_, + createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_, + updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, + reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowCallerInternal_ }; Blockly.Blocks['procedures_param'] = { From 181f9c6c39110c8eaf23774329d294a56129cc2d Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 6 Nov 2017 15:31:40 -0800 Subject: [PATCH 0214/2135] Documentation, fixes for return values --- blocks_vertical/procedures.js | 99 +++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index a89e5fe5b3..9bc217b864 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -173,14 +173,11 @@ Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_ = function(connectionMap) { * @param {!Object.} connectionMap An object mapping * parameter IDs to the blocks that were connected to those IDs at the * beginning of the mutation. - * @return {!Object.} params An object mapping parameter - * IDs to the blocks that are connected to those IDs at the end of the - * mutation. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) { - var params = {}; + this.paramMap_ = {}; // Split the proc into components, by %n, %b, and %s (ignoring escaped). var procComponents = this.procCode_.split(/(?=[^\\]\%[nbs])/); procComponents = procComponents.map(function(c) { @@ -200,14 +197,13 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) newLabel = component.substring(2).trim(); var inputName = inputPrefix + inputCount; - this.createInput_(inputType, inputName, inputCount, connectionMap, params); + this.createInput_(inputType, inputName, inputCount, connectionMap); inputCount++; } else { newLabel = component.trim(); } this.appendDummyInput().appendField(newLabel.replace(/\\%/, '%')); } - return params; }; /** @@ -288,25 +284,22 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) }; /** - * Create a new shadow block and attach it to the given input. + * Create a new argument reporter block and attach it to the given input. + * This function is used by the procedures_callnoreturn_internal block. + * TODO (#1213) consider renaming. * @param {!Blockly.Input} input The value input to attach a block to. * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {string} argumentName The name of the argument as provided by the + * user, which becomes the text of the label on the argument reporter block. * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.attachShadowCallerInternal_ = function( +Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( input, inputType, argumentName) { - var blockType = ''; - switch (inputType) { - case 'n': - blockType = 'argument_reporter_string_number'; - break; - case 'b': - blockType = 'argument_reporter_boolean'; - break; - case 's': - blockType = 'argument_reporter_string_number'; - break; + if (inputType == 'n' || inputType == 's') { + var blockType = 'argument_reporter_string_number'; + } else { + var blockType = 'argument_reporter_boolean'; } var newBlock = this.workspace.newBlock(blockType); newBlock.setShadow(true); @@ -319,22 +312,21 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadowCallerInternal_ = function( }; /** - * Create an input and attach the correct block to it. - * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {string} inputName The name to use when adding the input to the block. - * @param {Blockly.Block} oldBlock The block that needs to be attached to the - * input, or null if none existed. - * @param {string} id The ID of the current parameter. + * Create an input, attach the correct block to it, and insert it into the + * params map. + * This function is used by the procedures_callnoreturn block. + * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {string} name The name to use when adding the input to the block. + * @param {number} index The index of this input into the argument id array. * @param {!Object.} connectionMap An object mapping * parameter IDs to the blocks that were connected to those IDs at the * beginning of the mutation. - * @return {!Blockly.Input} The newly created value input. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, - count, connectionMap, params) { - var id = this.argumentIds_[count]; + index, connectionMap) { + var id = this.argumentIds_[index]; var oldBlock = null; if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; @@ -345,27 +337,27 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, } else { this.attachShadow_(input, type); } - params[id] = input; + this.paramMap_[id] = input; }; - /** - * Create an input and attach the correct block to it. - * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {string} inputName The name to use when adding the input to the block. - * @param {Blockly.Block} oldBlock The block that needs to be attached to the - * input, or null if none existed. - * @param {string} id The ID of the current parameter. + * Create an input, attach the correct block to it, and insert it into the + * params map. + * This function is used by the procedures_callnoreturn_internal block. + * TODO (#1213) consider renaming. + * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {string} name The name to use when adding the input to the block. + * @param {number} index The index of this input into the argument id and name + * arrays. * @param {!Object.} connectionMap An object mapping * parameter IDs to the blocks that were connected to those IDs at the * beginning of the mutation. - * @return {!Blockly.Input} The newly created value input. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, - name, count, connectionMap, params) { - var id = this.argumentIds_[count]; + name, index, connectionMap) { + var id = this.argumentIds_[index]; var oldBlock = null; if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; @@ -374,13 +366,12 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, if (connectionMap && oldBlock) { this.reattachBlock_(input, type, oldBlock, id, connectionMap); } else { - var argumentText = this.argumentNames_[count]; + var argumentText = this.argumentNames_[index]; this.attachShadow_(input, type, argumentText); } - params[id] = input; + this.paramMap_[id] = input; }; - /** * Update the block's structure and appearance to match the internally stored * mutation. @@ -396,7 +387,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { this.removeAllInputs_(); } - this.paramMap_ = this.createAllInputs_(connectionMap); + this.createAllInputs_(connectionMap); this.deleteShadows_(connectionMap); // TODO: Maybe also bump all old non-shadow blocks explicitly. @@ -437,6 +428,12 @@ Blockly.Blocks['procedures_callnoreturn'] = { "extensions": ["colours_more", "shape_statement", "procedure_call_contextmenu"] }); this.procCode_ = ''; + /** + * @type {!Object.} + * An object mapping parameter IDs to the blocks that are connected to those + * IDs. + */ + this.paramMap_ = null; }, getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom, @@ -471,6 +468,13 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { this.argumentNames_ = []; this.argumentDefaults_ = []; this.warp_ = false; + + /** + * @type {!Object.} + * An object mapping parameter IDs to the blocks that are connected to those + * IDs. + */ + this.paramMap_ = null; }, getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, @@ -483,7 +487,7 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowCallerInternal_ + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ }; Blockly.Blocks['procedures_param'] = { @@ -587,6 +591,13 @@ Blockly.Blocks['procedures_mutator_root'] = { this.argumentNames_ = []; this.argumentDefaults_ = []; this.warp_ = false; + + /** + * @type {!Object.} + * An object mapping parameter IDs to the blocks that are connected to those + * IDs. + */ + this.paramMap_ = null; }, getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, From 9d8a59749067914dd17c6925cc50d506939697fb Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 6 Nov 2017 15:32:56 -0800 Subject: [PATCH 0215/2135] Fix naming --- blocks_vertical/procedures.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 9bc217b864..ac0972c56e 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -367,7 +367,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, this.reattachBlock_(input, type, oldBlock, id, connectionMap); } else { var argumentText = this.argumentNames_[index]; - this.attachShadow_(input, type, argumentText); + this.attachArgumentReporter_(input, type, argumentText); } this.paramMap_[id] = input; }; @@ -487,7 +487,7 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ + attachArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ }; Blockly.Blocks['procedures_param'] = { From d014a7de6ec7fefcb596789566db320c1768de83 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 9 Nov 2017 13:29:48 -0800 Subject: [PATCH 0216/2135] Make inputs own input shapes, and dispose of them correctly --- core/block_render_svg_vertical.js | 17 +++++++++++------ core/block_svg.js | 28 +--------------------------- core/input.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js index b42cedee79..5e02301324 100644 --- a/core/block_render_svg_vertical.js +++ b/core/block_render_svg_vertical.js @@ -531,8 +531,10 @@ Blockly.BlockSvg.prototype.updateColour = function() { this.svgPath_.setAttribute('fill-opacity', this.getOpacity()); // Update colours of input shapes. - for (var shape in this.inputShapes_) { - this.inputShapes_[shape].setAttribute('fill', this.getColourTertiary()); + for (var i = 0, input; input = this.inputList[i]; i++) { + if (input.outlinePath) { + input.outlinePath.setAttribute('fill', this.getColourTertiary()); + } } // Render icon(s) if applicable @@ -577,13 +579,16 @@ Blockly.BlockSvg.prototype.highlightShapeForInput = function(conn, add) { if (!input) { throw 'No input found for the connection'; } - var inputShape = this.inputShapes_[input.name]; + if (!input.outlinePath) { + return; + } if (add) { - inputShape.setAttribute('filter', 'url(#blocklyReplacementGlowFilter)'); + input.outlinePath.setAttribute('filter', + 'url(#blocklyReplacementGlowFilter)'); Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), 'blocklyReplaceable'); } else { - inputShape.removeAttribute('filter'); + input.outlinePath.removeAttribute('filter'); Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), 'blocklyReplaceable'); } @@ -1314,7 +1319,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, * @param {Number} y Y offset of input. */ Blockly.BlockSvg.prototype.renderInputShape_ = function(input, x, y) { - var inputShape = this.inputShapes_[input.name]; + var inputShape = input.outlinePath; if (!inputShape) { // No input shape for this input - e.g., the block is an insertion marker. return; diff --git a/core/block_svg.js b/core/block_svg.js index bb97c4ba58..a45c0a3ed3 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -66,9 +66,6 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { /** @type {boolean} */ this.rendered = false; - /** @type {Object.} */ - this.inputShapes_ = {}; - /** * Whether to move the block to the drag surface when it is dragged. * True if it should move, false if it should be translated directly. @@ -146,9 +143,7 @@ Blockly.BlockSvg.prototype.initSvg = function() { // Input shapes are empty holes drawn when a value input is not connected. for (var i = 0, input; input = this.inputList[i]; i++) { input.init(); - if (input.type === Blockly.INPUT_VALUE) { - this.initInputShape(input); - } + input.initOutlinePath(this.svgGroup_); } var icons = this.getIcons(); for (i = 0; i < icons.length; i++) { @@ -168,27 +163,6 @@ Blockly.BlockSvg.prototype.initSvg = function() { } }; -/** - * Create and initialize the SVG element for an input shape. - * May be called more than once for an input. - * @param {!Blockly.Input} input Value input to add a shape SVG element for. - */ -Blockly.BlockSvg.prototype.initInputShape = function(input) { - if (this.inputShapes_[input.name] || input.connection.getShadowDom()) { - // Only create the shape elements once, and don't bother creating them if - // there's a shadow block that will always cover the input shape. - return; - } - this.inputShapes_[input.name] = Blockly.utils.createSvgElement( - 'path', - { - 'class': 'blocklyPath', - 'style': 'visibility: hidden' // Hide by default - shown when not connected. - }, - this.svgGroup_ - ); -}; - /** * Select this block. Highlight it visually. */ diff --git a/core/input.js b/core/input.js index 1d188b2c6f..5be15c28b4 100644 --- a/core/input.js +++ b/core/input.js @@ -57,6 +57,13 @@ Blockly.Input = function(type, name, block, connection) { this.connection = connection; /** @type {!Array.} */ this.fieldRow = []; + + /** + * The shape that is displayed when this input is rendered but not filled. + * @type {SVGElement} + * @package + */ + this.outlinePath = null; }; /** @@ -241,6 +248,9 @@ Blockly.Input.prototype.init = function() { * Sever all links to this input. */ Blockly.Input.prototype.dispose = function() { + if (this.outlinePath) { + goog.dom.removeNode(this.outlinePath); + } for (var i = 0, field; field = this.fieldRow[i]; i++) { field.dispose(); } @@ -249,3 +259,22 @@ Blockly.Input.prototype.dispose = function() { } this.sourceBlock_ = null; }; + +/** + * Create the input shape path element and attach it to the given SVG element. + * @param {!SVGElement} svgRoot The parent on which ot append the new element. + * @package + */ +Blockly.Input.prototype.initOutlinePath = function(svgRoot) { + if (this.type == Blockly.INPUT_VALUE) { + this.outlinePath = Blockly.utils.createSvgElement( + 'path', + { + 'class': 'blocklyPath', + 'style': 'visibility: hidden', // Hide by default - shown when not connected. + 'd': '' // IE doesn't like paths without the data definition, set an empty default + }, + svgRoot + ); + } +}; From 3838208bf73bb0f343e4e58002ed783bf82baedd Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 10 Nov 2017 09:36:32 -0500 Subject: [PATCH 0217/2135] Use data_listcontents instead of data_variable for list getter block --- core/data_category.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/data_category.js b/core/data_category.js index db114d732d..af50fb146e 100644 --- a/core/data_category.js +++ b/core/data_category.js @@ -66,7 +66,7 @@ Blockly.DataCategory = function(workspace) { variableModelList = workspace.getVariablesOfType('list'); variableModelList.sort(Blockly.VariableModel.compareByName); for (var i = 0; i < variableModelList.length; i++) { - Blockly.DataCategory.addDataVariable(xmlList, variableModelList[i]); + Blockly.DataCategory.addDataList(xmlList, variableModelList[i]); } if (variableModelList.length > 0) { @@ -171,6 +171,20 @@ Blockly.DataCategory.addHideVariable = function(xmlList, variable) { 'VARIABLE'); }; +/** + * Construct and add a data_listcontents block to xmlList. + * @param {!Array.} xmlList Array of XML block elements. + * @param {?Blockly.VariableModel} variable Variable to select in the field. + */ +Blockly.DataCategory.addDataList = function(xmlList, variable) { + // + // variablename + // + Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontents', 'LIST'); + // In the flyout, this ID must match variable ID for monitor syncing reasons + xmlList[xmlList.length - 1].setAttribute('id', variable.getId()); +}; + /** * Construct and add a data_addtolist block to xmlList. * @param {!Array.} xmlList Array of XML block elements. From 1113c5d4a7fd311f73cd8388581ce9348fa11cf8 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 7 Nov 2017 18:33:25 -0800 Subject: [PATCH 0218/2135] Rename inputs based on an updated mutation string! --- blocks_vertical/procedures.js | 174 +++++++++++++++++++++++++++++++--- 1 file changed, 161 insertions(+), 13 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index ac0972c56e..feef8a088f 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -358,20 +358,125 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, name, index, connectionMap) { var id = this.argumentIds_[index]; + var argumentText = this.argumentNames_[index]; var oldBlock = null; if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; } + var oldTypeMatches = + Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); var input = this.appendValueInput(name); - if (connectionMap && oldBlock) { + if (connectionMap && oldBlock && oldTypeMatches) { this.reattachBlock_(input, type, oldBlock, id, connectionMap); + oldBlock.setFieldValue(argumentText, 'VALUE'); } else { - var argumentText = this.argumentNames_[index]; this.attachArgumentReporter_(input, type, argumentText); } this.paramMap_[id] = input; }; +/** + * Create an input, attach the correct block to it, and insert it into the + * params map. + * This function is used by the procedures_callnoreturn_internal block. + * TODO (#1213) consider renaming. + * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {string} name The name to use when adding the input to the block. + * @param {number} index The index of this input into the argument id and name + * arrays. + * @param {!Object.} connectionMap An object mapping + * parameter IDs to the blocks that were connected to those IDs at the + * beginning of the mutation. + * @private + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_ = function(type, + name, index, connectionMap) { + var id = this.argumentIds_[index]; + var argumentText = this.argumentNames_[index]; + + var oldBlock = null; + if (connectionMap && (id in connectionMap)) { + oldBlock = connectionMap[id]; + } + var input = this.appendValueInput(name); + + var oldTypeMatches = + Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); + + if (connectionMap && oldBlock && oldTypeMatches) { + this.reattachBlock_(input, type, oldBlock, id, connectionMap); + oldBlock.setFieldValue(argumentText, 'TEXT'); + } else { + this.attachShadow_(input, type, argumentText); + } + this.paramMap_[id] = input; +}; + +Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, + type) { + if (!oldBlock) { + return false; + } + if ((type == 'n' || type == 's') && + oldBlock.type == 'argument_reporter_string_number') { + return true; + } + if (type == 'b' && oldBlock.type == 'argument_reporter_boolean') { + return true; + } + return false; +}; + +/** + * Create a new shadow block and attach it to the given input. + * @param {!Blockly.Input} input The value input to attach a block to. + * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * @private + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ = function(input, + inputType, argumentName) { + console.log(argumentName); + if (inputType == 'n' || inputType == 's') { + var newBlock = this.workspace.newBlock('text'); + } else { + var newBlock = this.workspace.newBlock('boolean_textinput'); + input.setCheck('Boolean'); + } + newBlock.setFieldValue(argumentName, 'TEXT'); + newBlock.setShadow(true); + if (!this.isInsertionMarker()) { + newBlock.initSvg(); + newBlock.render(false); + } + newBlock.outputConnection.connect(input.connection); +}; + +/** + * Reattach a block to the correct input after a mutation, and give the + * connection a shadow DOM based on its input type. + * @param {!Blockly.Input} input The value input to attach a block to. + * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {Blockly.Block} oldBlock The block that needs to be attached to the + * input, or null if none existed. + * @param {string} id The ID of the current parameter. + * @param {!Object.} connectionMap An object mapping + * parameter IDs to the blocks that were connected to those IDs at the + * beginning of the mutation. + * @private + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.reattachBlockMutatorRoot_ = function(input, inputType, + oldBlock, id, connectionMap, argumentName) { + if (inputType == 'b') { + input.setCheck('Boolean'); + } + oldBlock.setFieldValue(argumentName, 'TEXT'); + connectionMap[id] = null; + oldBlock.outputConnection.connect(input.connection); +}; + /** * Update the block's structure and appearance to match the internally stored * mutation. @@ -399,6 +504,34 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { } }; +Blockly.ScratchBlocks.ProcedureUtils.mutatorMakeProcCode_ = function() { + return 'say %s %n times if %b'; +}; + +Blockly.ScratchBlocks.ProcedureUtils.mutatorRootMutationToDom_ = function() { + this.procCode_ = this.getProcCode(); + + // Update argument names array. + var children = this.getChildren(); + this.argumentNames_ = []; + for (var i = 0; i < children.length; i++) { + this.argumentNames_.push(children[i].getFieldValue('TEXT')); + } + + this.argumentDefaults_ = ['argument', 'defaults', 'not', 'implemented']; + this.warp_ = false; + + + var container = document.createElement('mutation'); + container.setAttribute('proccode', this.procCode_); + container.setAttribute('argumentids', JSON.stringify(this.argumentIds_)); + container.setAttribute('argumentnames', JSON.stringify(this.argumentNames_)); + container.setAttribute('argumentdefaults', + JSON.stringify(this.argumentDefaults_)); + container.setAttribute('warp', this.warp_); + return container; +}; + Blockly.Blocks['procedures_defnoreturn'] = { /** * Block for defining a procedure with no return value. @@ -456,12 +589,9 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { * @this Blockly.Block */ init: function() { - this.setPreviousStatement(true); - this.setNextStatement(true); - this.setCategory(Blockly.Categories.more); - this.setColour(Blockly.Colours.more.primary, - Blockly.Colours.more.secondary, - Blockly.Colours.more.tertiary); + this.jsonInit({ + "extensions": ["colours_more", "shape_statement"] + }); /* Data known about the procedure. */ this.procCode_ = ''; @@ -577,6 +707,24 @@ Blockly.Blocks['argument_reporter_string_number'] = { } }; +Blockly.Blocks['boolean_textinput'] = { + init: function() { + this.jsonInit({ "message0": " %1", + "args0": [ + { + "type": "field_input", + "name": "TEXT", + "text": "foo" + } + ], + "colour": Blockly.Colours.textField, + "colourSecondary": Blockly.Colours.textField, + "colourTertiary": Blockly.Colours.textField, + "extensions": ["output_boolean"] + }); + } +}; + Blockly.Blocks['procedures_mutator_root'] = { /** * The root block in the procedure editing workspace. @@ -599,17 +747,17 @@ Blockly.Blocks['procedures_mutator_root'] = { */ this.paramMap_ = null; }, - getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, + getProcCode: Blockly.ScratchBlocks.ProcedureUtils.mutatorMakeProcCode_, + mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.mutatorRootMutationToDom_, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_, - createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInput_, + createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ + reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlockMutatorRoot_, + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ }; From 9854afeedb8e5e2491db52fb39cd68259b31965c Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 8 Nov 2017 15:24:01 -0800 Subject: [PATCH 0219/2135] Add labels and inputs, rename them, and push changes to the right side playground --- blocks_vertical/procedures.js | 78 +++++++++++++++++++++++--- tests/custom_procedure_playground.html | 49 ++++++++++++++-- 2 files changed, 112 insertions(+), 15 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index feef8a088f..d02f7f7ae3 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -202,7 +202,18 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) } else { newLabel = component.trim(); } - this.appendDummyInput().appendField(newLabel.replace(/\\%/, '%')); + newLabel.replace(/\\%/, '%'); + this.addLabel_(newLabel); + } +}; + +Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ = function(text) { + this.appendDummyInput().appendField(text); +}; + +Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_ = function(text) { + if (text) { + this.appendDummyInput().appendField(new Blockly.FieldTextInput(text)); } }; @@ -405,8 +416,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_ = function(type, Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); if (connectionMap && oldBlock && oldTypeMatches) { - this.reattachBlock_(input, type, oldBlock, id, connectionMap); - oldBlock.setFieldValue(argumentText, 'TEXT'); + this.reattachBlock_(input, type, oldBlock, id, connectionMap, argumentText); } else { this.attachShadow_(input, type, argumentText); } @@ -504,8 +514,51 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { } }; -Blockly.ScratchBlocks.ProcedureUtils.mutatorMakeProcCode_ = function() { - return 'say %s %n times if %b'; +Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { + var procCode = ''; + var argumentNames = []; + for (var i = 0; i < this.inputList.length; i++) { + if (i != 0) { + procCode += ' '; + } + var input = this.inputList[i]; + if (input.type == Blockly.DUMMY_INPUT) { + procCode += input.fieldRow[0].getValue(); + } else if (input.type == Blockly.INPUT_VALUE) { + argumentNames.push(input.connection.targetBlock().getFieldValue('TEXT')); + if (input.connection.targetBlock().type == 'boolean_textinput') { + procCode += '%b'; + } else { + procCode += '%s'; + } + } else { + throw new Error( + 'Unexpected input type on a procedure mutator root: ' + input.type); + } + } + this.procCode_ = procCode; + this.argumentNames_ = argumentNames; +}; + +Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { + this.procCode_ = this.procCode_ + ' label text'; + this.updateDisplay_(); +}; + +Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { + this.procCode_ = this.procCode_ + ' %b'; + this.argumentNames_.push('boolean'); + this.argumentIds_.push(Blockly.utils.genUid()); + this.argumentDefaults_.push('todo'); + this.updateDisplay_(); +}; + +Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { + this.procCode_ = this.procCode_ + ' %s'; + this.argumentNames_.push('string or number'); + this.argumentIds_.push(Blockly.utils.genUid()); + this.argumentDefaults_.push('todo'); + this.updateDisplay_(); }; Blockly.ScratchBlocks.ProcedureUtils.mutatorRootMutationToDom_ = function() { @@ -579,7 +632,8 @@ Blockly.Blocks['procedures_callnoreturn'] = { createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInput_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_, + addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ }; Blockly.Blocks['procedures_callnoreturn_internal'] = { @@ -617,7 +671,8 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, - attachArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ + attachArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_, + addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ }; Blockly.Blocks['procedures_param'] = { @@ -747,7 +802,7 @@ Blockly.Blocks['procedures_mutator_root'] = { */ this.paramMap_ = null; }, - getProcCode: Blockly.ScratchBlocks.ProcedureUtils.mutatorMakeProcCode_, + getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.mutatorRootMutationToDom_, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, @@ -758,6 +813,11 @@ Blockly.Blocks['procedures_mutator_root'] = { createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlockMutatorRoot_, - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_, + addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_, + addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, + addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, + addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, + onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ }; diff --git a/tests/custom_procedure_playground.html b/tests/custom_procedure_playground.html index 2dfe940ebc..d7f75777c4 100644 --- a/tests/custom_procedure_playground.html +++ b/tests/custom_procedure_playground.html @@ -47,9 +47,9 @@ - - - + + + @@ -109,28 +109,65 @@ + + + + From da17573fe4347030c158c89babe8890837f96c78 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 9 Nov 2017 12:55:07 -0800 Subject: [PATCH 0220/2135] 'add random input' and 'remove random input' buttons --- blocks_vertical/procedures.js | 19 ++++++++++------- tests/custom_procedure_playground.html | 28 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index d02f7f7ae3..53e550cad7 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -184,7 +184,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) return c.trim(); // Strip whitespace. }); // Create inputs and shadow blocks as appropriate. - var inputPrefix = 'input'; + //var inputPrefix = 'input'; var inputCount = 0; for (var i = 0, component; component = procComponents[i]; i++) { var newLabel; @@ -196,7 +196,8 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) } newLabel = component.substring(2).trim(); - var inputName = inputPrefix + inputCount; + //var inputName = inputPrefix + inputCount; + var inputName = ''; this.createInput_(inputType, inputName, inputCount, connectionMap); inputCount++; } else { @@ -342,7 +343,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; } - var input = this.appendValueInput(name); + var input = this.appendValueInput(id); if (connectionMap && oldBlock) { this.reattachBlock_(input, type, oldBlock, id, connectionMap); } else { @@ -376,7 +377,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, } var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); - var input = this.appendValueInput(name); + var input = this.appendValueInput(id); if (connectionMap && oldBlock && oldTypeMatches) { this.reattachBlock_(input, type, oldBlock, id, connectionMap); oldBlock.setFieldValue(argumentText, 'VALUE'); @@ -410,7 +411,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_ = function(type, if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; } - var input = this.appendValueInput(name); + var input = this.appendValueInput(id); var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); @@ -517,6 +518,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { var procCode = ''; var argumentNames = []; + var argumentIds = []; for (var i = 0; i < this.inputList.length; i++) { if (i != 0) { procCode += ' '; @@ -525,8 +527,10 @@ Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { if (input.type == Blockly.DUMMY_INPUT) { procCode += input.fieldRow[0].getValue(); } else if (input.type == Blockly.INPUT_VALUE) { - argumentNames.push(input.connection.targetBlock().getFieldValue('TEXT')); - if (input.connection.targetBlock().type == 'boolean_textinput') { + var target = input.connection.targetBlock(); + argumentNames.push(target.getFieldValue('TEXT')); + argumentIds.push(input.name); + if (target.type == 'boolean_textinput') { procCode += '%b'; } else { procCode += '%s'; @@ -538,6 +542,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { } this.procCode_ = procCode; this.argumentNames_ = argumentNames; + this.argumentIds_ = argumentIds; }; Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { diff --git a/tests/custom_procedure_playground.html b/tests/custom_procedure_playground.html index d7f75777c4..93619c43ca 100644 --- a/tests/custom_procedure_playground.html +++ b/tests/custom_procedure_playground.html @@ -52,6 +52,8 @@ + + @@ -168,6 +170,32 @@ function addTextNumber() { mutationRoot.addStringNumberExternal(); } + + function removeRandomInput() { + var rnd = Math.floor(Math.random() * mutationRoot.inputList.length); + mutationRoot.removeInput(mutationRoot.inputList[rnd].name); + mutationRoot.onChangeFn(); + mutationRoot.updateDisplay_(); + + applyMutation(); + } + + function addRandomInput() { + var rnd = Math.floor(Math.random() * 3); + switch (rnd) { + case 0: + addTextNumber(); + break; + case 1: + addLabel(); + break; + case 2: + addBoolean(); + break; + } + + applyMutation(); + } From 702def8c941d8d22317c77ea5e9840bdda780058 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 9 Nov 2017 15:16:21 -0800 Subject: [PATCH 0221/2135] Cleanup --- blocks_vertical/procedures.js | 68 ++++++++++------------------------- 1 file changed, 19 insertions(+), 49 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 53e550cad7..851d535aa8 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -96,8 +96,9 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom = function() { */ Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function(xmlElement) { this.procCode_ = xmlElement.getAttribute('proccode'); - this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); this.warp_ = xmlElement.getAttribute('warp'); + + this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); this.argumentNames_ = JSON.parse(xmlElement.getAttribute('argumentnames')); this.argumentDefaults_ = JSON.parse( xmlElement.getAttribute('argumentdefaults')); @@ -196,9 +197,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) } newLabel = component.substring(2).trim(); - //var inputName = inputPrefix + inputCount; - var inputName = ''; - this.createInput_(inputType, inputName, inputCount, connectionMap); + this.createInput_(inputType, inputCount, connectionMap); inputCount++; } else { newLabel = component.trim(); @@ -214,7 +213,8 @@ Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ = function(text) { Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_ = function(text) { if (text) { - this.appendDummyInput().appendField(new Blockly.FieldTextInput(text)); + this.appendDummyInput(Blockly.utils.genUid()). + appendField(new Blockly.FieldTextInput(text)); } }; @@ -328,7 +328,6 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( * params map. * This function is used by the procedures_callnoreturn block. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {string} name The name to use when adding the input to the block. * @param {number} index The index of this input into the argument id array. * @param {!Object.} connectionMap An object mapping * parameter IDs to the blocks that were connected to those IDs at the @@ -336,8 +335,8 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, - index, connectionMap) { +Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, index, + connectionMap) { var id = this.argumentIds_[index]; var oldBlock = null; if (connectionMap && (id in connectionMap)) { @@ -358,7 +357,6 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, * This function is used by the procedures_callnoreturn_internal block. * TODO (#1213) consider renaming. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {string} name The name to use when adding the input to the block. * @param {number} index The index of this input into the argument id and name * arrays. * @param {!Object.} connectionMap An object mapping @@ -368,7 +366,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, name, * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, - name, index, connectionMap) { + index, connectionMap) { var id = this.argumentIds_[index]; var argumentText = this.argumentNames_[index]; var oldBlock = null; @@ -393,7 +391,6 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, * This function is used by the procedures_callnoreturn_internal block. * TODO (#1213) consider renaming. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {string} name The name to use when adding the input to the block. * @param {number} index The index of this input into the argument id and name * arrays. * @param {!Object.} connectionMap An object mapping @@ -403,7 +400,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_ = function(type, - name, index, connectionMap) { + index, connectionMap) { var id = this.argumentIds_[index]; var argumentText = this.argumentNames_[index]; @@ -516,33 +513,30 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { }; Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { - var procCode = ''; - var argumentNames = []; - var argumentIds = []; + this.procCode_ = ''; + this.argumentNames_ = []; + this.argumentIds_ = []; for (var i = 0; i < this.inputList.length; i++) { if (i != 0) { - procCode += ' '; + this.procCode_ += ' '; } var input = this.inputList[i]; if (input.type == Blockly.DUMMY_INPUT) { - procCode += input.fieldRow[0].getValue(); + this.procCode_ += input.fieldRow[0].getValue(); } else if (input.type == Blockly.INPUT_VALUE) { var target = input.connection.targetBlock(); - argumentNames.push(target.getFieldValue('TEXT')); - argumentIds.push(input.name); + this.argumentNames_.push(target.getFieldValue('TEXT')); + this.argumentIds_.push(input.name); if (target.type == 'boolean_textinput') { - procCode += '%b'; + this.procCode_ += '%b'; } else { - procCode += '%s'; + this.procCode_ += '%s'; } } else { throw new Error( 'Unexpected input type on a procedure mutator root: ' + input.type); } } - this.procCode_ = procCode; - this.argumentNames_ = argumentNames; - this.argumentIds_ = argumentIds; }; Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { @@ -566,30 +560,6 @@ Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { this.updateDisplay_(); }; -Blockly.ScratchBlocks.ProcedureUtils.mutatorRootMutationToDom_ = function() { - this.procCode_ = this.getProcCode(); - - // Update argument names array. - var children = this.getChildren(); - this.argumentNames_ = []; - for (var i = 0; i < children.length; i++) { - this.argumentNames_.push(children[i].getFieldValue('TEXT')); - } - - this.argumentDefaults_ = ['argument', 'defaults', 'not', 'implemented']; - this.warp_ = false; - - - var container = document.createElement('mutation'); - container.setAttribute('proccode', this.procCode_); - container.setAttribute('argumentids', JSON.stringify(this.argumentIds_)); - container.setAttribute('argumentnames', JSON.stringify(this.argumentNames_)); - container.setAttribute('argumentdefaults', - JSON.stringify(this.argumentDefaults_)); - container.setAttribute('warp', this.warp_); - return container; -}; - Blockly.Blocks['procedures_defnoreturn'] = { /** * Block for defining a procedure with no return value. @@ -808,7 +778,7 @@ Blockly.Blocks['procedures_mutator_root'] = { this.paramMap_ = null; }, getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.mutatorRootMutationToDom_, + mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, From aa00bcc6ff3093f327b5dc30b1f3061bbf73b696 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 9 Nov 2017 15:59:22 -0800 Subject: [PATCH 0222/2135] Get rid of reattachBlock --- blocks_vertical/procedures.js | 91 +++++++++++++---------------------- 1 file changed, 34 insertions(+), 57 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 851d535aa8..705a326687 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -243,31 +243,6 @@ Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ = function(type) { return shadowDom; }; -/** - * Reattach a block to the correct input after a mutation, and give the - * connection a shadow DOM based on its input type. - * @param {!Blockly.Input} input The value input to attach a block to. - * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {Blockly.Block} oldBlock The block that needs to be attached to the - * input, or null if none existed. - * @param {string} id The ID of the current parameter. - * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the - * beginning of the mutation. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_ = function(input, inputType, - oldBlock, id, connectionMap) { - if (inputType == 'n' || inputType == 's') { - input.connection.setShadowDom(this.buildShadowDom_(inputType)); - } else { - input.setCheck('Boolean'); - } - connectionMap[id] = null; - oldBlock.outputConnection.connect(input.connection); -}; - /** * Create a new shadow block and attach it to the given input. * @param {!Blockly.Input} input The value input to attach a block to. @@ -337,14 +312,24 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( */ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, index, connectionMap) { + // Shared var id = this.argumentIds_[index]; var oldBlock = null; if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; } var input = this.appendValueInput(id); + if (type == 'b') { + input.setCheck('Boolean'); + } + // End shared + + + if (connectionMap && oldBlock) { - this.reattachBlock_(input, type, oldBlock, id, connectionMap); + // Reattach the old block. + connectionMap[id] = null; + oldBlock.outputConnection.connect(input.connection); } else { this.attachShadow_(input, type); } @@ -367,18 +352,29 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, index, */ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, index, connectionMap) { + // Shared var id = this.argumentIds_[index]; - var argumentText = this.argumentNames_[index]; var oldBlock = null; if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; } + var input = this.appendValueInput(id); + if (type == 'b') { + input.setCheck('Boolean'); + } + // End shared + + var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); - var input = this.appendValueInput(id); + var argumentText = this.argumentNames_[index]; if (connectionMap && oldBlock && oldTypeMatches) { - this.reattachBlock_(input, type, oldBlock, id, connectionMap); + // Reattach the old block, and update the text if needed. + // The old block is the same type, and on the same input, but the input name + // may have changed. oldBlock.setFieldValue(argumentText, 'VALUE'); + connectionMap[id] = null; + oldBlock.outputConnection.connect(input.connection); } else { this.attachArgumentReporter_(input, type, argumentText); } @@ -401,20 +397,26 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, */ Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_ = function(type, index, connectionMap) { + // Shared var id = this.argumentIds_[index]; - var argumentText = this.argumentNames_[index]; - var oldBlock = null; if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; } var input = this.appendValueInput(id); + if (type == 'b') { + input.setCheck('Boolean'); + } + // End shared var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); + var argumentText = this.argumentNames_[index]; if (connectionMap && oldBlock && oldTypeMatches) { - this.reattachBlock_(input, type, oldBlock, id, connectionMap, argumentText); + oldBlock.setFieldValue(argumentText, 'TEXT'); + connectionMap[id] = null; + oldBlock.outputConnection.connect(input.connection); } else { this.attachShadow_(input, type, argumentText); } @@ -461,30 +463,6 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ = function(input, newBlock.outputConnection.connect(input.connection); }; -/** - * Reattach a block to the correct input after a mutation, and give the - * connection a shadow DOM based on its input type. - * @param {!Blockly.Input} input The value input to attach a block to. - * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {Blockly.Block} oldBlock The block that needs to be attached to the - * input, or null if none existed. - * @param {string} id The ID of the current parameter. - * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the - * beginning of the mutation. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.reattachBlockMutatorRoot_ = function(input, inputType, - oldBlock, id, connectionMap, argumentName) { - if (inputType == 'b') { - input.setCheck('Boolean'); - } - oldBlock.setFieldValue(argumentName, 'TEXT'); - connectionMap[id] = null; - oldBlock.outputConnection.connect(input.connection); -}; - /** * Update the block's structure and appearance to match the internally stored * mutation. @@ -787,7 +765,6 @@ Blockly.Blocks['procedures_mutator_root'] = { buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_, createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlockMutatorRoot_, attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_, addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_, addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, From d0611a30273cd40f66d54f73458bf9bdaaf6476e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 9 Nov 2017 16:06:53 -0800 Subject: [PATCH 0223/2135] Add back use of buildShadowDom, but only in the single case that needs it --- blocks_vertical/procedures.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 705a326687..5754d5c0ee 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -185,7 +185,6 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) return c.trim(); // Strip whitespace. }); // Create inputs and shadow blocks as appropriate. - //var inputPrefix = 'input'; var inputCount = 0; for (var i = 0, component; component = procComponents[i]; i++) { var newLabel; @@ -202,8 +201,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) } else { newLabel = component.trim(); } - newLabel.replace(/\\%/, '%'); - this.addLabel_(newLabel); + this.addLabel_(newLabel.replace(/\\%/, '%')); } }; @@ -330,6 +328,9 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, index, // Reattach the old block. connectionMap[id] = null; oldBlock.outputConnection.connect(input.connection); + if (type != 'b') { + input.connection.setShadowDom(this.buildShadowDom_(type)); + } } else { this.attachShadow_(input, type); } @@ -620,7 +621,6 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_, createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, @@ -762,7 +762,6 @@ Blockly.Blocks['procedures_mutator_root'] = { disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_, createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_, From 9ddf8a79a9cf522ace52404ec02c4be534d5d761 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 16 Nov 2017 11:45:59 -0500 Subject: [PATCH 0224/2135] Adding ability to create a new broadcast message using the dropdown option. Broadcast messages are now typed variables with the variable type as 'broadcast_msg'. Needed to do some extra work to translate variables from blocks to XML correctly because the default broadcast messages were getting added as new variables with the incorrect type upon trying to move any of the broadcast message related blocks from the toolbar into the workspace because it was trying to create a new variable with a different type than already existed. Currently, broadcast message names conflict with variable names, and I need to make it so that isn't so. Additionally, need to update the source block's message name after creation of a new message via the dropdown. --- blocks_vertical/default_toolbox.js | 6 ------ blocks_vertical/event.js | 30 +++++++++++++-------------- core/constants.js | 9 ++++++++ core/field_variable.js | 33 +++++++++++++++++++++++++----- core/xml.js | 15 ++++++++++++++ msg/messages.js | 4 ++++ 6 files changed, 70 insertions(+), 27 deletions(-) diff --git a/blocks_vertical/default_toolbox.js b/blocks_vertical/default_toolbox.js index f8c375c318..5d2bf95666 100644 --- a/blocks_vertical/default_toolbox.js +++ b/blocks_vertical/default_toolbox.js @@ -315,14 +315,8 @@ Blockly.Blocks.defaultToolbox = ''+ ''+ ''+ - ''+ - ''+ - ''+ ''+ ''+ - ''+ - ''+ - ''+ ''+ ''+ ''+ diff --git a/blocks_vertical/event.js b/blocks_vertical/event.js index 3039e999f0..a9d303e81a 100644 --- a/blocks_vertical/event.js +++ b/blocks_vertical/event.js @@ -77,13 +77,10 @@ Blockly.Blocks['event_whenbroadcastreceived'] = { "message0": "when I receive %1", "args0": [ { - "type": "field_dropdown", + "type": "field_variable", "name": "BROADCAST_OPTION", - "options": [ - ['message1', 'message1'], - ['message2', 'message2'], - ['new message', 'new message'] - ] + "variableTypes": ["broadcast_msg"], + "variable": "message1" } ], "category": Blockly.Categories.event, @@ -153,13 +150,10 @@ Blockly.Blocks['event_broadcast_menu'] = { "message0": "%1", "args0": [ { - "type": "field_dropdown", + "type": "field_variable", "name": "BROADCAST_OPTION", - "options": [ - ['message1', 'message1'], - ['message2', 'message2'], - ['new message', 'new message'] - ] + "variableTypes":["broadcast_msg"], + "variable":"message1" } ], "colour": Blockly.Colours.event.secondary, @@ -181,8 +175,10 @@ Blockly.Blocks['event_broadcast'] = { "message0": "broadcast %1", "args0": [ { - "type": "input_value", - "name": "BROADCAST_OPTION" + "type": "field_variable", + "name": "BROADCAST_OPTION", + "variableTypes": ["broadcast_msg"], + "variable": "message1" } ], "category": Blockly.Categories.event, @@ -201,8 +197,10 @@ Blockly.Blocks['event_broadcastandwait'] = { "message0": "broadcast %1 and wait", "args0": [ { - "type": "input_value", - "name": "BROADCAST_OPTION" + "type": "field_variable", + "name": "BROADCAST_OPTION", + "variableTypes": ["broadcast_msg"], + "variable":"message1" } ], "category": Blockly.Categories.event, diff --git a/core/constants.js b/core/constants.js index 323900ee45..5d96685918 100644 --- a/core/constants.js +++ b/core/constants.js @@ -326,3 +326,12 @@ Blockly.RENAME_VARIABLE_ID = 'RENAME_VARIABLE_ID'; * @const {string} */ Blockly.DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID'; + +/** + * String for use in the dropdown created in field_variable, + * specifically for broadcast messages. + * This string indicates that this option in the dropdown is 'New message...' + * and if selected, should trigger the prompt to create a new message. + * @const {string} + */ +Blockly.NEW_MESSAGE_ID = 'NEW_MESSAGE_ID'; diff --git a/core/field_variable.js b/core/field_variable.js index cd564da8b8..22ea3ed0c7 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -81,7 +81,18 @@ Blockly.FieldVariable.prototype.initModel = function() { // For instance, some blocks in the toolbox have variable dropdowns filled // in by default. if (!this.sourceBlock_.isInFlyout) { - this.sourceBlock_.workspace.createVariable(this.getValue()); + // Check if there was exactly one element specified in the + // variableTypes list. This is the list that specifies which types of + // variables to include in the dropdown. + // If there is exactly one element specified, make the default variable + // being created this specified type. Else, default behavior is to create + // a scalar variable + if (this.getVariableTypes_().length === 1) { + this.sourceBlock_.workspace.createVariable(this.getValue(), + this.getVariableTypes_()[0]); + } else { + this.sourceBlock_.workspace.createVariable(this.getValue()); + } } }; @@ -171,6 +182,7 @@ Blockly.FieldVariable.dropdownCreate = function() { if (this.sourceBlock_) { workspace = this.sourceBlock_.workspace; } + var isBroadcastType = false; if (workspace) { var variableTypes = this.getVariableTypes_(); var variableModelList = []; @@ -178,6 +190,9 @@ Blockly.FieldVariable.dropdownCreate = function() { // doesn't modify the workspace's list. for (var i = 0; i < variableTypes.length; i++) { var variableType = variableTypes[i]; + if (variableType === 'broadcast_msg'){ + isBroadcastType = true; + } var variables = workspace.getVariablesOfType(variableType); variableModelList = variableModelList.concat(variables); } @@ -200,10 +215,14 @@ Blockly.FieldVariable.dropdownCreate = function() { // Set the uuid as the internal representation of the variable. options[i] = [variableModelList[i].name, variableModelList[i].getId()]; } - options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]); - if (Blockly.Msg.DELETE_VARIABLE) { - options.push([Blockly.Msg.DELETE_VARIABLE.replace('%1', name), - Blockly.DELETE_VARIABLE_ID]); + if (isBroadcastType) { + options.push([Blockly.Msg.NEW_MESSAGE, Blockly.NEW_MESSAGE_ID]); + } else { + options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]); + if (Blockly.Msg.DELETE_VARIABLE) { + options.push([Blockly.Msg.DELETE_VARIABLE.replace('%1', name), + Blockly.DELETE_VARIABLE_ID]); + } } return options; }; @@ -237,6 +256,10 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) { // Delete variable. workspace.deleteVariable(this.getText()); return; + } else if (id == Blockly.NEW_MESSAGE_ID) { + Blockly.Variables.createVariable(workspace, null, 'broadcast_msg'); + // TODO update sourceblock's variable name... + return; } // Call any validation function, and allow it to override. diff --git a/core/xml.js b/core/xml.js index 895754c130..a8344ebb11 100644 --- a/core/xml.js +++ b/core/xml.js @@ -116,6 +116,21 @@ Blockly.Xml.blockToDom = function(block, opt_noId) { if (variable) { container.setAttribute('id', variable.getId()); container.setAttribute('variabletype', variable.type); + } else { + // Above works well for untyped variables, but we need to correctly + // set the type for blocks that exist by default in the toolbox + // (e.g. broadcast messages) + // TODO figure out if we ever need to do something where there's + // more than one element in variableTypes field + + // must check that field is an instance of FieldVariable because + // FieldVariableGetter doesn't have getVariableTypes_ function + if (field instanceof Blockly.FieldVariable) { + var variableTypes = field.getVariableTypes_(); + if (variableTypes.length === 1) { + container.setAttribute('variabletype', variableTypes[0]); + } + } } } element.appendChild(container); diff --git a/msg/messages.js b/msg/messages.js index 27effae304..3845d815e2 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -148,6 +148,10 @@ Blockly.Msg.CANNOT_DELETE_VARIABLE_PROCEDURE = 'Can\'t delete the variable "%1" /// dropdown choice - Delete the currently selected variable. Blockly.Msg.DELETE_VARIABLE = 'Delete the "%1" variable'; +// Broadcast Message creation +/// dropdown choice - Create a new message. +Blockly.Msg.NEW_MESSAGE = 'New message...'; + // Colour Blocks. /// url - Information about colour. Blockly.Msg.COLOUR_PICKER_HELPURL = 'https://en.wikipedia.org/wiki/Color'; From ffb5c9e369923f7b00d353da194c22620481a067 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 9 Nov 2017 16:12:18 -0800 Subject: [PATCH 0225/2135] Extract shared code; general cleanup --- blocks_vertical/procedures.js | 120 +++++++++++++++------------------- 1 file changed, 51 insertions(+), 69 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 5754d5c0ee..9f6d5eabbb 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -107,6 +107,7 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function(xmlEleme /** * Remove all inputs on the block, including dummy inputs. + * Assumes no input has shadow DOM set. * @private * @this Blockly.Block */ @@ -196,7 +197,16 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) } newLabel = component.substring(2).trim(); - this.createInput_(inputType, inputCount, connectionMap); + var id = this.argumentIds_[inputCount]; + var oldBlock = null; + if (connectionMap && (id in connectionMap)) { + oldBlock = connectionMap[id]; + } + var input = this.appendValueInput(id); + if (inputType == 'b') { + input.setCheck('Boolean'); + } + this.populateInput_(inputType, inputCount, connectionMap, id, oldBlock, input); inputCount++; } else { newLabel = component.trim(); @@ -263,8 +273,6 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) newBlock.render(false); } newBlock.outputConnection.connect(input.connection); - } else { - input.setCheck('Boolean'); } }; @@ -308,22 +316,8 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, index, - connectionMap) { - // Shared - var id = this.argumentIds_[index]; - var oldBlock = null; - if (connectionMap && (id in connectionMap)) { - oldBlock = connectionMap[id]; - } - var input = this.appendValueInput(id); - if (type == 'b') { - input.setCheck('Boolean'); - } - // End shared - - - +Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_ = function(type, index, + connectionMap, id, oldBlock, input) { if (connectionMap && oldBlock) { // Reattach the old block. connectionMap[id] = null; @@ -351,21 +345,8 @@ Blockly.ScratchBlocks.ProcedureUtils.createInput_ = function(type, index, * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, - index, connectionMap) { - // Shared - var id = this.argumentIds_[index]; - var oldBlock = null; - if (connectionMap && (id in connectionMap)) { - oldBlock = connectionMap[id]; - } - var input = this.appendValueInput(id); - if (type == 'b') { - input.setCheck('Boolean'); - } - // End shared - - +Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(type, + index, connectionMap, id, oldBlock, input) { var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); var argumentText = this.argumentNames_[index]; @@ -396,20 +377,8 @@ Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_ = function(type, * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_ = function(type, - index, connectionMap) { - // Shared - var id = this.argumentIds_[index]; - var oldBlock = null; - if (connectionMap && (id in connectionMap)) { - oldBlock = connectionMap[id]; - } - var input = this.appendValueInput(id); - if (type == 'b') { - input.setCheck('Boolean'); - } - // End shared - +Blockly.ScratchBlocks.ProcedureUtils.populateInputMutatorRoot_ = function(type, + index, connectionMap, id, oldBlock, input) { var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); @@ -448,12 +417,10 @@ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, */ Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ = function(input, inputType, argumentName) { - console.log(argumentName); if (inputType == 'n' || inputType == 's') { var newBlock = this.workspace.newBlock('text'); } else { var newBlock = this.workspace.newBlock('boolean_textinput'); - input.setCheck('Boolean'); } newBlock.setFieldValue(argumentName, 'TEXT'); newBlock.setShadow(true); @@ -482,8 +449,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { this.createAllInputs_(connectionMap); this.deleteShadows_(connectionMap); - // TODO: Maybe also bump all old non-shadow blocks explicitly. - + this.bumpNeighbours_(); this.rendered = wasRendered; if (wasRendered && !this.isInsertionMarker()) { this.initSvg(); @@ -575,19 +541,25 @@ Blockly.Blocks['procedures_callnoreturn'] = { */ this.paramMap_ = null; }, + // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom, - domToMutation: Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_, - createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInput_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, + + // Exist on all three blocks, but have different implementations. + mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom, + domToMutation: Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation, + populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_, + addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_, + + // Shared with procedures_mutator_root, but with a different implementation. attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_, - addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ + + // Only exists on the external caller. + buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ }; Blockly.Blocks['procedures_callnoreturn_internal'] = { @@ -614,18 +586,22 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { */ this.paramMap_ = null; }, + // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, - domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputCallerInternal_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - reattachBlock_: Blockly.ScratchBlocks.ProcedureUtils.reattachBlock_, - attachArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_, - addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ + + // Exist on all three blocks, but have different implementations. + mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, + domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, + populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_, + addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_, + + // Only exists on the internal caller. + attachArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ }; Blockly.Blocks['procedures_param'] = { @@ -755,20 +731,26 @@ Blockly.Blocks['procedures_mutator_root'] = { */ this.paramMap_ = null; }, + // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, - domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - createInput_: Blockly.ScratchBlocks.ProcedureUtils.createInputMutatorRoot_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_, + + // Exist on all three blocks, but have different implementations. + mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, + domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, + populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputMutatorRoot_, addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_, + + // Shared with procedures_callnoreturn, but with a different implementation. + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_, + + // Only exist on the mutator root. addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ }; - From 2e614ea32a0f94b3deaf747f0788e3c3041f172d Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 9 Nov 2017 16:48:37 -0800 Subject: [PATCH 0226/2135] Remove procedures_param --- blocks_vertical/procedures.js | 57 ----------------------------------- 1 file changed, 57 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 9f6d5eabbb..38c1afdc62 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -604,63 +604,6 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { attachArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ }; -Blockly.Blocks['procedures_param'] = { - /** - * Block for a parameter. - * @this Blockly.Block - */ - init: function() { - this.appendDummyInput() - .appendField(new Blockly.FieldLabel(), 'paramName'); - - this.setPreviousStatement(false); - this.setNextStatement(false); - this.setOutput(true); - - this.setCategory(Blockly.Categories.more); - this.setColour(Blockly.Colours.more.primary, - Blockly.Colours.more.secondary, - Blockly.Colours.more.tertiary); - this._paramName = 'undefined'; - this._shape = 'r'; - }, - /** - * Create XML to represent the (non-editable) name and arguments. - * @return {!Element} XML storage element. - * @this Blockly.Block - */ - mutationToDom: function() { - var container = document.createElement('mutation'); - container.setAttribute('paramname', this._paramName); - container.setAttribute('shape', this._shape); - return container; - }, - /** - * Parse XML to restore the (non-editable) name and parameters. - * @param {!Element} xmlElement XML storage element. - * @this Blockly.Block - */ - domToMutation: function(xmlElement) { - this._paramName = xmlElement.getAttribute('paramname'); - this._shape = xmlElement.getAttribute('shape'); - this.updateDisplay_(); - }, - updateDisplay_: function() { - this.setFieldValue(this._paramName, 'paramName'); - switch (this._shape) { - case 'b': - this.setOutputShape(Blockly.OUTPUT_SHAPE_HEXAGONAL); - this.setOutput(true, 'Boolean'); - break; - case 's': - default: - this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); - this.setOutput(true, 'String'); - break; - } - } -}; - Blockly.Blocks['argument_reporter_boolean'] = { init: function() { this.jsonInit({ "message0": " %1", From d35768bb5aa250d16b57d77fd79c6789bb5c2ba9 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 10 Nov 2017 10:53:16 -0800 Subject: [PATCH 0227/2135] argument names -> display names --- blocks_vertical/procedures.js | 178 ++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 74 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 38c1afdc62..3afa7ac6fb 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -32,15 +32,7 @@ goog.require('Blockly.constants'); // TODO: Create a namespace properly. Blockly.ScratchBlocks.ProcedureUtils = {}; -/** - * Returns the name of the procedure this block calls, or the empty string if - * it has not yet been set. - * @return {string} Procedure name. - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.getProcCode = function() { - return this.procCode_; -}; +// Serialization and deserialization. /** * Create XML to represent the (non-editable) name and arguments of a procedure @@ -80,7 +72,7 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom = function() { var container = document.createElement('mutation'); container.setAttribute('proccode', this.procCode_); container.setAttribute('argumentids', JSON.stringify(this.argumentIds_)); - container.setAttribute('argumentnames', JSON.stringify(this.argumentNames_)); + container.setAttribute('argumentnames', JSON.stringify(this.displayNames_)); container.setAttribute('argumentdefaults', JSON.stringify(this.argumentDefaults_)); container.setAttribute('warp', this.warp_); @@ -99,25 +91,48 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function(xmlEleme this.warp_ = xmlElement.getAttribute('warp'); this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); - this.argumentNames_ = JSON.parse(xmlElement.getAttribute('argumentnames')); + this.displayNames_ = JSON.parse(xmlElement.getAttribute('argumentnames')); this.argumentDefaults_ = JSON.parse( xmlElement.getAttribute('argumentdefaults')); this.updateDisplay_(); }; +// End of serialization and deserialization. + +// Shared by all three procedure blocks. /** - * Remove all inputs on the block, including dummy inputs. - * Assumes no input has shadow DOM set. + * Returns the name of the procedure this block calls, or the empty string if + * it has not yet been set. + * @return {string} Procedure name. + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.getProcCode = function() { + return this.procCode_; +}; + +/** + * Update the block's structure and appearance to match the internally stored + * mutation. * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function() { - // Delete inputs directly instead of with block.removeInput to avoid splicing - // out of the input list at every index. - for (var i = 0, input; input = this.inputList[i]; i++) { - input.dispose(); +Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { + var wasRendered = this.rendered; + this.rendered = false; + + if (this.paramMap_) { + var connectionMap = this.disconnectOldBlocks_(); + this.removeAllInputs_(); + } + + this.createAllInputs_(connectionMap); + this.deleteShadows_(connectionMap); + + this.rendered = wasRendered; + if (wasRendered && !this.isInsertionMarker()) { + this.initSvg(); + this.render(); } - this.inputList = []; }; /** @@ -149,24 +164,18 @@ Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_ = function() { }; /** - * Delete all shadow blocks in the given map. - * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the - * beginning of the mutation. + * Remove all inputs on the block, including dummy inputs. + * Assumes no input has shadow DOM set. * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_ = function(connectionMap) { - // Get rid of all of the old shadow blocks if they aren't connected. - if (connectionMap) { - for (var id in connectionMap) { - var block = connectionMap[id]; - if (block && block.isShadow()) { - block.dispose(); - connectionMap[id] = null; - } - } +Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function() { + // Delete inputs directly instead of with block.removeInput to avoid splicing + // out of the input list at every index. + for (var i = 0, input; input = this.inputList[i]; i++) { + input.dispose(); } + this.inputList = []; }; /** @@ -215,6 +224,28 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) } }; +/** + * Delete all shadow blocks in the given map. + * @param {!Object.} connectionMap An object mapping + * parameter IDs to the blocks that were connected to those IDs at the + * beginning of the mutation. + * @private + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_ = function(connectionMap) { + // Get rid of all of the old shadow blocks if they aren't connected. + if (connectionMap) { + for (var id in connectionMap) { + var block = connectionMap[id]; + if (block && block.isShadow()) { + block.dispose(); + connectionMap[id] = null; + } + } + } +}; +// End of shared code. + Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ = function(text) { this.appendDummyInput().appendField(text); }; @@ -282,13 +313,13 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) * TODO (#1213) consider renaming. * @param {!Blockly.Input} input The value input to attach a block to. * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {string} argumentName The name of the argument as provided by the + * @param {string} displayName The name of the argument as provided by the * user, which becomes the text of the label on the argument reporter block. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( - input, inputType, argumentName) { + input, inputType, displayName) { if (inputType == 'n' || inputType == 's') { var blockType = 'argument_reporter_string_number'; } else { @@ -296,7 +327,7 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( } var newBlock = this.workspace.newBlock(blockType); newBlock.setShadow(true); - newBlock.setFieldValue(argumentName, 'VALUE'); + newBlock.setFieldValue(displayName, 'VALUE'); if (!this.isInsertionMarker()) { newBlock.initSvg(); newBlock.render(false); @@ -349,16 +380,16 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(typ index, connectionMap, id, oldBlock, input) { var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); - var argumentText = this.argumentNames_[index]; + var displayName = this.displayNames_[index]; if (connectionMap && oldBlock && oldTypeMatches) { // Reattach the old block, and update the text if needed. // The old block is the same type, and on the same input, but the input name // may have changed. - oldBlock.setFieldValue(argumentText, 'VALUE'); + oldBlock.setFieldValue(displayName, 'VALUE'); connectionMap[id] = null; oldBlock.outputConnection.connect(input.connection); } else { - this.attachArgumentReporter_(input, type, argumentText); + this.attachArgumentReporter_(input, type, displayName); } this.paramMap_[id] = input; }; @@ -382,17 +413,23 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputMutatorRoot_ = function(type, var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); - var argumentText = this.argumentNames_[index]; + var displayName = this.displayNames_[index]; if (connectionMap && oldBlock && oldTypeMatches) { - oldBlock.setFieldValue(argumentText, 'TEXT'); + oldBlock.setFieldValue(displayName, 'TEXT'); connectionMap[id] = null; oldBlock.outputConnection.connect(input.connection); } else { - this.attachShadow_(input, type, argumentText); + this.attachShadow_(input, type, displayName); } this.paramMap_[id] = input; }; +/** + * Check whether the type of the old block corresponds to the given input type. + * @param {Blockly.BlockSvg} oldBlock The old block to check. + * @param {string} type The input type. One of 'n', 'n', or 's'. + * @return {boolean} True if the type matches, false otherwise. + */ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, type) { if (!oldBlock) { @@ -412,17 +449,19 @@ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, * Create a new shadow block and attach it to the given input. * @param {!Blockly.Input} input The value input to attach a block to. * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {string} displayName The display name of this argument, which is the + * text of the field on the shadow block. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ = function(input, - inputType, argumentName) { + inputType, displayName) { if (inputType == 'n' || inputType == 's') { var newBlock = this.workspace.newBlock('text'); } else { var newBlock = this.workspace.newBlock('boolean_textinput'); } - newBlock.setFieldValue(argumentName, 'TEXT'); + newBlock.setFieldValue(displayName, 'TEXT'); newBlock.setShadow(true); if (!this.isInsertionMarker()) { newBlock.initSvg(); @@ -432,34 +471,12 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ = function(input, }; /** - * Update the block's structure and appearance to match the internally stored - * mutation. - * @private - * @this Blockly.Block + * Update the serializable information on the block based on the existing inputs + * and their text. */ -Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { - var wasRendered = this.rendered; - this.rendered = false; - - if (this.paramMap_) { - var connectionMap = this.disconnectOldBlocks_(); - this.removeAllInputs_(); - } - - this.createAllInputs_(connectionMap); - this.deleteShadows_(connectionMap); - - this.bumpNeighbours_(); - this.rendered = wasRendered; - if (wasRendered && !this.isInsertionMarker()) { - this.initSvg(); - this.render(); - } -}; - Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { this.procCode_ = ''; - this.argumentNames_ = []; + this.displayNames_ = []; this.argumentIds_ = []; for (var i = 0; i < this.inputList.length; i++) { if (i != 0) { @@ -470,7 +487,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { this.procCode_ += input.fieldRow[0].getValue(); } else if (input.type == Blockly.INPUT_VALUE) { var target = input.connection.targetBlock(); - this.argumentNames_.push(target.getFieldValue('TEXT')); + this.displayNames_.push(target.getFieldValue('TEXT')); this.argumentIds_.push(input.name); if (target.type == 'boolean_textinput') { this.procCode_ += '%b'; @@ -484,22 +501,35 @@ Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { } }; +/** + * Externally-visible function to add a label field to the mutator root block. + * @public + */ Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { this.procCode_ = this.procCode_ + ' label text'; this.updateDisplay_(); }; +/** + * Externally-visible function to add a boolean field to the mutator root block. + * @public + */ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { this.procCode_ = this.procCode_ + ' %b'; - this.argumentNames_.push('boolean'); + this.displayNames_.push('boolean'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); this.updateDisplay_(); }; +/** + * Externally-visible function to add a string/number field to the mutator root + * block. + * @public + */ Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { this.procCode_ = this.procCode_ + ' %s'; - this.argumentNames_.push('string or number'); + this.displayNames_.push('string or number'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); this.updateDisplay_(); @@ -575,7 +605,7 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { /* Data known about the procedure. */ this.procCode_ = ''; - this.argumentNames_ = []; + this.displayNames_ = []; this.argumentDefaults_ = []; this.warp_ = false; @@ -663,7 +693,7 @@ Blockly.Blocks['procedures_mutator_root'] = { }); /* Data known about the procedure. */ this.procCode_ = ''; - this.argumentNames_ = []; + this.displayNames_ = []; this.argumentDefaults_ = []; this.warp_ = false; From 3f170d8c4096a22faa46ee22663d497379733594 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 10 Nov 2017 10:57:57 -0800 Subject: [PATCH 0228/2135] procedures_mutator_root -> procedures_declaration_root --- blocks_vertical/procedures.js | 8 ++++---- tests/custom_procedure_playground.html | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 3afa7ac6fb..89becca3bc 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -64,7 +64,7 @@ Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation = function(xmlElement) /** * Create XML to represent the (non-editable) name and arguments of a procedure * definition block (procedures_callnoreturn_internal, which is part of a definition, - * or procedures_mutator_root). + * or procedures_declaration_root). * @return {!Element} XML storage element. * @this Blockly.Block */ @@ -82,7 +82,7 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom = function() { /** * Parse XML to restore the (non-editable) name and parameters of a procedure * definition block (procedures_callnoreturn_internal, which is part of a definition, - * or procedures_mutator_root). + * or procedures_declaration_root). * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ @@ -585,7 +585,7 @@ Blockly.Blocks['procedures_callnoreturn'] = { populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_, addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_, - // Shared with procedures_mutator_root, but with a different implementation. + // Shared with procedures_declaration_root, but with a different implementation. attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_, // Only exists on the external caller. @@ -682,7 +682,7 @@ Blockly.Blocks['boolean_textinput'] = { } }; -Blockly.Blocks['procedures_mutator_root'] = { +Blockly.Blocks['procedures_declaration_root'] = { /** * The root block in the procedure editing workspace. * @this Blockly.Block diff --git a/tests/custom_procedure_playground.html b/tests/custom_procedure_playground.html index 93619c43ca..dc676563a8 100644 --- a/tests/custom_procedure_playground.html +++ b/tests/custom_procedure_playground.html @@ -67,7 +67,7 @@ @@ -127,7 +127,7 @@ From d241de93cadc2d72e707650418c5976ade306bb2 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 10 Nov 2017 11:37:03 -0800 Subject: [PATCH 0229/2135] Get rid of the param map --- blocks_vertical/procedures.js | 49 +++++++++-------------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 89becca3bc..e9bd890248 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -120,10 +120,8 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { var wasRendered = this.rendered; this.rendered = false; - if (this.paramMap_) { - var connectionMap = this.disconnectOldBlocks_(); - this.removeAllInputs_(); - } + var connectionMap = this.disconnectOldBlocks_(); + this.removeAllInputs_(); this.createAllInputs_(connectionMap); this.deleteShadows_(connectionMap); @@ -147,14 +145,13 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_ = function() { // Remove old stuff var connectionMap = {}; - for (var id in this.paramMap_) { - var input = this.paramMap_[id]; + for (var i = 0, input; input = this.inputList[i]; i++) { if (input.connection) { // Remove the shadow DOM. Otherwise a shadow block will respawn // instantly, and we'd have to remove it when we remove the input. input.connection.setShadowDom(null); var target = input.connection.targetBlock(); - connectionMap[id] = target; + connectionMap[input.name] = target; if (target) { input.connection.disconnect(); } @@ -188,7 +185,6 @@ Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function() { * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) { - this.paramMap_ = {}; // Split the proc into components, by %n, %b, and %s (ignoring escaped). var procComponents = this.procCode_.split(/(?=[^\\]\%[nbs])/); procComponents = procComponents.map(function(c) { @@ -336,8 +332,7 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( }; /** - * Create an input, attach the correct block to it, and insert it into the - * params map. + * Create an input and attach the correct block to it. * This function is used by the procedures_callnoreturn block. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). * @param {number} index The index of this input into the argument id array. @@ -359,12 +354,10 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_ = function(type, index } else { this.attachShadow_(input, type); } - this.paramMap_[id] = input; }; /** - * Create an input, attach the correct block to it, and insert it into the - * params map. + * Create an input and attach the correct block to it. * This function is used by the procedures_callnoreturn_internal block. * TODO (#1213) consider renaming. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). @@ -391,12 +384,10 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(typ } else { this.attachArgumentReporter_(input, type, displayName); } - this.paramMap_[id] = input; }; /** - * Create an input, attach the correct block to it, and insert it into the - * params map. + * Create an input and attach the correct block to it. * This function is used by the procedures_callnoreturn_internal block. * TODO (#1213) consider renaming. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). @@ -421,7 +412,6 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputMutatorRoot_ = function(type, } else { this.attachShadow_(input, type, displayName); } - this.paramMap_[id] = input; }; /** @@ -519,6 +509,7 @@ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { this.displayNames_.push('boolean'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); + console.log(this.argumentIds_); this.updateDisplay_(); }; @@ -564,12 +555,8 @@ Blockly.Blocks['procedures_callnoreturn'] = { "extensions": ["colours_more", "shape_statement", "procedure_call_contextmenu"] }); this.procCode_ = ''; - /** - * @type {!Object.} - * An object mapping parameter IDs to the blocks that are connected to those - * IDs. - */ - this.paramMap_ = null; + this.argumentIds_ = []; + this.warp_ = false; }, // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, @@ -606,15 +593,9 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { /* Data known about the procedure. */ this.procCode_ = ''; this.displayNames_ = []; + this.argumentIds_ = []; this.argumentDefaults_ = []; this.warp_ = false; - - /** - * @type {!Object.} - * An object mapping parameter IDs to the blocks that are connected to those - * IDs. - */ - this.paramMap_ = null; }, // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, @@ -694,15 +675,9 @@ Blockly.Blocks['procedures_declaration_root'] = { /* Data known about the procedure. */ this.procCode_ = ''; this.displayNames_ = []; + this.argumentIds_ = []; this.argumentDefaults_ = []; this.warp_ = false; - - /** - * @type {!Object.} - * An object mapping parameter IDs to the blocks that are connected to those - * IDs. - */ - this.paramMap_ = null; }, // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, From 12f2fda99419ef04033c4bb2e5694565076ff86e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 10 Nov 2017 11:50:37 -0800 Subject: [PATCH 0230/2135] Use input.name and stop passing around the id --- blocks_vertical/procedures.js | 63 ++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index e9bd890248..76ed2a8e6d 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -211,7 +211,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) if (inputType == 'b') { input.setCheck('Boolean'); } - this.populateInput_(inputType, inputCount, connectionMap, id, oldBlock, input); + this.populateInput_(inputType, inputCount, connectionMap, oldBlock, input); inputCount++; } else { newLabel = component.trim(); @@ -242,10 +242,23 @@ Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_ = function(connectionMap) { }; // End of shared code. +/** + * Add a label with the given text to a procedures_callnoreturn or + * procedures_callnoreturn_internal block. + * @param {string} text The label text. + * @private + */ Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ = function(text) { this.appendDummyInput().appendField(text); }; +/** + * Add a label editor with the given text to a procedures_declaration_root + * block. Editing the text in the new input updates the text of the + * corresponding labels on function calls. + * @param {string} text The label text. + * @private + */ Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_ = function(text) { if (text) { this.appendDummyInput(Blockly.utils.genUid()). @@ -314,8 +327,8 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( - input, inputType, displayName) { +Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function(input, + inputType, displayName) { if (inputType == 'n' || inputType == 's') { var blockType = 'argument_reporter_string_number'; } else { @@ -332,21 +345,24 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function( }; /** - * Create an input and attach the correct block to it. + * Populate the given input with the correct child block or values. * This function is used by the procedures_callnoreturn block. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). * @param {number} index The index of this input into the argument id array. * @param {!Object.} connectionMap An object mapping * parameter IDs to the blocks that were connected to those IDs at the * beginning of the mutation. + * @param {Blockly.BlockSvg} oldBlock The block that was previously connected to + * this input, or null. + * @param {!Blockly.Input} input The newly created input to populate. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_ = function(type, index, - connectionMap, id, oldBlock, input) { + connectionMap, oldBlock, input) { if (connectionMap && oldBlock) { // Reattach the old block. - connectionMap[id] = null; + connectionMap[input.name] = null; oldBlock.outputConnection.connect(input.connection); if (type != 'b') { input.connection.setShadowDom(this.buildShadowDom_(type)); @@ -357,7 +373,7 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_ = function(type, index }; /** - * Create an input and attach the correct block to it. + * Populate the given input with the correct child block or values. * This function is used by the procedures_callnoreturn_internal block. * TODO (#1213) consider renaming. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). @@ -366,11 +382,14 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_ = function(type, index * @param {!Object.} connectionMap An object mapping * parameter IDs to the blocks that were connected to those IDs at the * beginning of the mutation. + * @param {Blockly.BlockSvg} oldBlock The block that was previously connected to + * this input, or null. + * @param {!Blockly.Input} input The newly created input to populate. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(type, - index, connectionMap, id, oldBlock, input) { + index, connectionMap, oldBlock, input) { var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); var displayName = this.displayNames_[index]; @@ -379,7 +398,7 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(typ // The old block is the same type, and on the same input, but the input name // may have changed. oldBlock.setFieldValue(displayName, 'VALUE'); - connectionMap[id] = null; + connectionMap[input.name] = null; oldBlock.outputConnection.connect(input.connection); } else { this.attachArgumentReporter_(input, type, displayName); @@ -387,7 +406,7 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(typ }; /** - * Create an input and attach the correct block to it. + * Populate the given input with the correct child block or values. * This function is used by the procedures_callnoreturn_internal block. * TODO (#1213) consider renaming. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). @@ -396,18 +415,21 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(typ * @param {!Object.} connectionMap An object mapping * parameter IDs to the blocks that were connected to those IDs at the * beginning of the mutation. + * @param {Blockly.BlockSvg} oldBlock The block that was previously connected to + * this input, or null. + * @param {!Blockly.Input} input The newly created input to populate. * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.populateInputMutatorRoot_ = function(type, - index, connectionMap, id, oldBlock, input) { +Blockly.ScratchBlocks.ProcedureUtils.populateInputDeclarationRoot_ = function(type, + index, connectionMap, oldBlock, input) { var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); var displayName = this.displayNames_[index]; if (connectionMap && oldBlock && oldTypeMatches) { oldBlock.setFieldValue(displayName, 'TEXT'); - connectionMap[id] = null; + connectionMap[input.name] = null; oldBlock.outputConnection.connect(input.connection); } else { this.attachShadow_(input, type, displayName); @@ -437,6 +459,8 @@ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, /** * Create a new shadow block and attach it to the given input. + * The shadow block has a single text field which is used to set the display + * name of the argument. * @param {!Blockly.Input} input The value input to attach a block to. * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). * @param {string} displayName The display name of this argument, which is the @@ -444,8 +468,8 @@ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ = function(input, - inputType, displayName) { +Blockly.ScratchBlocks.ProcedureUtils.attachShadowDeclarationRoot_ = function( + input, inputType, displayName) { if (inputType == 'n' || inputType == 's') { var newBlock = this.workspace.newBlock('text'); } else { @@ -464,7 +488,7 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_ = function(input, * Update the serializable information on the block based on the existing inputs * and their text. */ -Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ = function() { +Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeDeclarationRoot_ = function() { this.procCode_ = ''; this.displayNames_ = []; this.argumentIds_ = []; @@ -509,7 +533,6 @@ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { this.displayNames_.push('boolean'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); - console.log(this.argumentIds_); this.updateDisplay_(); }; @@ -690,15 +713,15 @@ Blockly.Blocks['procedures_declaration_root'] = { // Exist on all three blocks, but have different implementations. mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, - populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputMutatorRoot_, + populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputDeclarationRoot_, addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_, // Shared with procedures_callnoreturn, but with a different implementation. - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowMutatorRoot_, + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowDeclarationRoot_, // Only exist on the mutator root. addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, - onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeMutatorRoot_ + onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeDeclarationRoot_ }; From c865f368377f978902943b7bb4ed962b164a9074 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 15 Nov 2017 14:16:34 -0800 Subject: [PATCH 0231/2135] Apply new names for procedure components --- blocks_vertical/procedures.js | 327 ++++++++++++++----------- core/block_render_svg_vertical.js | 12 +- core/connection.js | 4 +- core/constants.js | 18 ++ core/procedures.js | 17 +- core/workspace.js | 2 +- tests/custom_procedure_playground.html | 22 +- tests/jsunit/connection_test.js | 8 +- tests/jsunit/procedure_test.js | 37 ++- 9 files changed, 251 insertions(+), 196 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 76ed2a8e6d..2eb5fafbbe 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -36,7 +36,7 @@ Blockly.ScratchBlocks.ProcedureUtils = {}; /** * Create XML to represent the (non-editable) name and arguments of a procedure - * call block (procedures_callnoreturn block). + * call block. * @return {!Element} XML storage element. * @this Blockly.Block */ @@ -49,8 +49,8 @@ Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom = function() { }; /** - * Parse XML to restore the (non-editable) name and parameters of a procedure - * call block (procedures_callnoreturn block). + * Parse XML to restore the (non-editable) name and arguments of a procedure + * call block. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ @@ -62,9 +62,8 @@ Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation = function(xmlElement) }; /** - * Create XML to represent the (non-editable) name and arguments of a procedure - * definition block (procedures_callnoreturn_internal, which is part of a definition, - * or procedures_declaration_root). + * Create XML to represent the (non-editable) name and arguments of a + * procedures_prototype block or a procedures_declaration block. * @return {!Element} XML storage element. * @this Blockly.Block */ @@ -80,9 +79,8 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom = function() { }; /** - * Parse XML to restore the (non-editable) name and parameters of a procedure - * definition block (procedures_callnoreturn_internal, which is part of a definition, - * or procedures_declaration_root). + * Parse XML to restore the (non-editable) name and arguments of a + * procedures_prototype block or a procedures_declaration block. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ @@ -99,7 +97,8 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function(xmlEleme // End of serialization and deserialization. -// Shared by all three procedure blocks. +// Shared by all three procedure blocks (procedures_declaration, +// procedures_call, and procedures_prototype). /** * Returns the name of the procedure this block calls, or the empty string if * it has not yet been set. @@ -136,7 +135,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { /** * Disconnect old blocks from all value inputs on this block, but hold onto them * in case they can be reattached later. - * @return {!Object.} An object mapping parameter IDs to + * @return {!Object.} An object mapping argument IDs to * the blocks that were connected to those IDs at the beginning of the * mutation. * @private @@ -179,7 +178,7 @@ Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function() { * Create all inputs specified by the new procCode, and populate them with * shadow blocks or reconnected old blocks as appropriate. * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the + * argument IDs to the blocks that were connected to those IDs at the * beginning of the mutation. * @private * @this Blockly.Block @@ -190,40 +189,42 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) procComponents = procComponents.map(function(c) { return c.trim(); // Strip whitespace. }); - // Create inputs and shadow blocks as appropriate. - var inputCount = 0; + // Create arguments and labels as appropriate. + var argumentCount = 0; for (var i = 0, component; component = procComponents[i]; i++) { - var newLabel; + var labelText; if (component.substring(0, 1) == '%') { - var inputType = component.substring(1, 2); - if (!(inputType == 'n' || inputType == 'b' || inputType == 's')) { + var argumentType = component.substring(1, 2); + if (!(argumentType == 'n' || argumentType == 'b' || argumentType == 's')) { throw new Error( - 'Found an custom procedure with an invalid type: ' + inputType); + 'Found an custom procedure with an invalid type: ' + argumentType); } - newLabel = component.substring(2).trim(); + labelText = component.substring(2).trim(); - var id = this.argumentIds_[inputCount]; + var id = this.argumentIds_[argumentCount]; var oldBlock = null; if (connectionMap && (id in connectionMap)) { oldBlock = connectionMap[id]; } + var input = this.appendValueInput(id); - if (inputType == 'b') { + if (argumentType == 'b') { input.setCheck('Boolean'); } - this.populateInput_(inputType, inputCount, connectionMap, oldBlock, input); - inputCount++; + this.populateArgument_(argumentType, argumentCount, connectionMap, oldBlock, + input); + argumentCount++; } else { - newLabel = component.trim(); + labelText = component.trim(); } - this.addLabel_(newLabel.replace(/\\%/, '%')); + this.addProcedureLabel_(labelText.replace(/\\%/, '%')); } }; /** * Delete all shadow blocks in the given map. * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the + * argument IDs to the blocks that were connected to those IDs at the * beginning of the mutation. * @private * @this Blockly.Block @@ -243,23 +244,23 @@ Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_ = function(connectionMap) { // End of shared code. /** - * Add a label with the given text to a procedures_callnoreturn or - * procedures_callnoreturn_internal block. + * Add a label field with the given text to a procedures_call or + * procedures_prototype block. * @param {string} text The label text. * @private */ -Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_ = function(text) { +Blockly.ScratchBlocks.ProcedureUtils.addLabelField_ = function(text) { this.appendDummyInput().appendField(text); }; /** - * Add a label editor with the given text to a procedures_declaration_root - * block. Editing the text in the new input updates the text of the - * corresponding labels on function calls. + * Add a label editor with the given text to a procedures_declaration + * block. Editing the text in the label editor updates the text of the + * corresponding label fields on function calls. * @param {string} text The label text. * @private */ -Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_ = function(text) { +Blockly.ScratchBlocks.ProcedureUtils.addLabelEditor_ = function(text) { if (text) { this.appendDummyInput(Blockly.utils.genUid()). appendField(new Blockly.FieldTextInput(text)); @@ -294,15 +295,17 @@ Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ = function(type) { /** * Create a new shadow block and attach it to the given input. * @param {!Blockly.Input} input The value input to attach a block to. - * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * @param {string} argumentType One of 'b' (boolean), 's' (string) or + * 'n' (number). * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) { - if (inputType == 'n' || inputType == 's') { - var blockType = inputType == 'n' ? 'math_number' : 'text'; +Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, + argumentType) { + if (argumentType == 'n' || argumentType == 's') { + var blockType = argumentType == 'n' ? 'math_number' : 'text'; var newBlock = this.workspace.newBlock(blockType); - if (inputType == 'n') { + if (argumentType == 'n') { newBlock.setFieldValue('99', 'NUM'); } else { newBlock.setFieldValue('hello world', 'TEXT'); @@ -317,19 +320,18 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, inputType) }; /** - * Create a new argument reporter block and attach it to the given input. - * This function is used by the procedures_callnoreturn_internal block. - * TODO (#1213) consider renaming. - * @param {!Blockly.Input} input The value input to attach a block to. - * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * Create a new argument reporter block. + * @param {string} argumentType One of 'b' (boolean), 's' (string) or + * 'n' (number). * @param {string} displayName The name of the argument as provided by the * user, which becomes the text of the label on the argument reporter block. + * @return {!Blockly.BlockSvg} The newly created argument reporter block. * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function(input, - inputType, displayName) { - if (inputType == 'n' || inputType == 's') { +Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_ = function( + argumentType, displayName) { + if (argumentType == 'n' || argumentType == 's') { var blockType = 'argument_reporter_string_number'; } else { var blockType = 'argument_reporter_boolean'; @@ -341,16 +343,16 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function(input, newBlock.initSvg(); newBlock.render(false); } - newBlock.outputConnection.connect(input.connection); + return newBlock; }; /** - * Populate the given input with the correct child block or values. - * This function is used by the procedures_callnoreturn block. + * Populate the argument by attaching the correct child block or shadow to the + * given input. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {number} index The index of this input into the argument id array. + * @param {number} index The index of this argument into the argument id array. * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the + * argument IDs to the blocks that were connected to those IDs at the * beginning of the mutation. * @param {Blockly.BlockSvg} oldBlock The block that was previously connected to * this input, or null. @@ -358,13 +360,14 @@ Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ = function(input, * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_ = function(type, index, - connectionMap, oldBlock, input) { +Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnCaller_ = function(type, + index, connectionMap, oldBlock, input) { if (connectionMap && oldBlock) { // Reattach the old block. connectionMap[input.name] = null; oldBlock.outputConnection.connect(input.connection); if (type != 'b') { + // TODO: Preserve old shadow DOM. input.connection.setShadowDom(this.buildShadowDom_(type)); } } else { @@ -373,47 +376,49 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_ = function(type, index }; /** - * Populate the given input with the correct child block or values. - * This function is used by the procedures_callnoreturn_internal block. - * TODO (#1213) consider renaming. + * Populate the argument by attaching the correct argument reporter to the given + * input. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {number} index The index of this input into the argument id and name - * arrays. + * @param {number} index The index of this argument into the argument ID and + * argument display name arrays. * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the + * argument IDs to the blocks that were connected to those IDs at the * beginning of the mutation. - * @param {Blockly.BlockSvg} oldBlock The block that was previously connected to - * this input, or null. + * @param {Blockly.BlockSvg} oldBlock The argument reporter that was previously + * connected to this input, or null. * @param {!Blockly.Input} input The newly created input to populate. * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(type, - index, connectionMap, oldBlock, input) { +Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnPrototype_ = function( + type, index, connectionMap, oldBlock, input) { var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); var displayName = this.displayNames_[index]; + + // Decide which block to attach. if (connectionMap && oldBlock && oldTypeMatches) { - // Reattach the old block, and update the text if needed. - // The old block is the same type, and on the same input, but the input name - // may have changed. - oldBlock.setFieldValue(displayName, 'VALUE'); + // Update the text if needed. The old argument reporter is the same type, + // and on the same input, but the argument's display name may have changed. + var argumentReporter = oldBlock; + argumentReporter.setFieldValue(displayName, 'VALUE'); connectionMap[input.name] = null; - oldBlock.outputConnection.connect(input.connection); } else { - this.attachArgumentReporter_(input, type, displayName); + var argumentReporter = this.createArgumentReporter_(type, displayName); } + + // Attach the block. + input.connection.connect(argumentReporter.outputConnection); }; /** - * Populate the given input with the correct child block or values. - * This function is used by the procedures_callnoreturn_internal block. - * TODO (#1213) consider renaming. + * Populate the argument by attaching the correct argument editor to the given + * input. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {number} index The index of this input into the argument id and name - * arrays. + * @param {number} index The index of this argument into the argument id and + * argument display name arrays. * @param {!Object.} connectionMap An object mapping - * parameter IDs to the blocks that were connected to those IDs at the + * argument IDs to the blocks that were connected to those IDs at the * beginning of the mutation. * @param {Blockly.BlockSvg} oldBlock The block that was previously connected to * this input, or null. @@ -421,25 +426,33 @@ Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_ = function(typ * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.populateInputDeclarationRoot_ = function(type, - index, connectionMap, oldBlock, input) { +Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_ = function( + type, index, connectionMap, oldBlock, input) { + // TODO: This always returns false, because it checks for argument reporter + // blocks instead of argument editor blocks. Create a new version for argument + // editors. var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); - var displayName = this.displayNames_[index]; + + // Decide which block to attach. if (connectionMap && oldBlock && oldTypeMatches) { + var argumentEditor = oldBlock; oldBlock.setFieldValue(displayName, 'TEXT'); connectionMap[input.name] = null; - oldBlock.outputConnection.connect(input.connection); } else { - this.attachShadow_(input, type, displayName); + var argumentEditor = this.createArgumentEditor_(type, displayName); } + + // Attach the block. + input.connection.connect(argumentEditor.outputConnection); }; /** - * Check whether the type of the old block corresponds to the given input type. + * Check whether the type of the old block corresponds to the given argument + * type. * @param {Blockly.BlockSvg} oldBlock The old block to check. - * @param {string} type The input type. One of 'n', 'n', or 's'. + * @param {string} type The argument type. One of 'n', 'n', or 's'. * @return {boolean} True if the type matches, false otherwise. */ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, @@ -458,22 +471,23 @@ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, }; /** - * Create a new shadow block and attach it to the given input. - * The shadow block has a single text field which is used to set the display - * name of the argument. - * @param {!Blockly.Input} input The value input to attach a block to. - * @param {string} inputType One of 'b' (boolean), 's' (string) or 'n' (number). + * Create an argument editor. + * An argument editor is a shadow block with a single text field, which is used + * to set the display name of the argument. + * @param {string} argumentType One of 'b' (boolean), 's' (string) or + * 'n' (number). * @param {string} displayName The display name of this argument, which is the * text of the field on the shadow block. + * @return {!Blockly.BlockSvg} The newly created argument editor block. * @private * @this Blockly.Block */ -Blockly.ScratchBlocks.ProcedureUtils.attachShadowDeclarationRoot_ = function( - input, inputType, displayName) { - if (inputType == 'n' || inputType == 's') { - var newBlock = this.workspace.newBlock('text'); +Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_ = function( + argumentType, displayName) { + if (argumentType == 'n' || argumentType == 's') { + var newBlock = this.workspace.newBlock('argument_editor_string_number'); } else { - var newBlock = this.workspace.newBlock('boolean_textinput'); + var newBlock = this.workspace.newBlock('argument_editor_boolean'); } newBlock.setFieldValue(displayName, 'TEXT'); newBlock.setShadow(true); @@ -481,14 +495,14 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadowDeclarationRoot_ = function( newBlock.initSvg(); newBlock.render(false); } - newBlock.outputConnection.connect(input.connection); + return newBlock; }; /** * Update the serializable information on the block based on the existing inputs * and their text. */ -Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeDeclarationRoot_ = function() { +Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { this.procCode_ = ''; this.displayNames_ = []; this.argumentIds_ = []; @@ -500,10 +514,11 @@ Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeDeclarationRoot_ = function() if (input.type == Blockly.DUMMY_INPUT) { this.procCode_ += input.fieldRow[0].getValue(); } else if (input.type == Blockly.INPUT_VALUE) { + // Inspect the argument editor. var target = input.connection.targetBlock(); this.displayNames_.push(target.getFieldValue('TEXT')); this.argumentIds_.push(input.name); - if (target.type == 'boolean_textinput') { + if (target.type == 'argument_editor_boolean') { this.procCode_ += '%b'; } else { this.procCode_ += '%s'; @@ -516,7 +531,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeDeclarationRoot_ = function() }; /** - * Externally-visible function to add a label field to the mutator root block. + * Externally-visible function to add a label to the procedure declaration. * @public */ Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { @@ -525,7 +540,8 @@ Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { }; /** - * Externally-visible function to add a boolean field to the mutator root block. + * Externally-visible function to add a boolean argument to the procedure + * declaration. * @public */ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { @@ -537,8 +553,8 @@ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { }; /** - * Externally-visible function to add a string/number field to the mutator root - * block. + * Externally-visible function to add a string/number argument to the procedure + * declaration. * @public */ Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { @@ -549,7 +565,7 @@ Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { this.updateDisplay_(); }; -Blockly.Blocks['procedures_defnoreturn'] = { +Blockly.Blocks['procedures_definition'] = { /** * Block for defining a procedure with no return value. * @this Blockly.Block @@ -568,7 +584,7 @@ Blockly.Blocks['procedures_defnoreturn'] = { } }; -Blockly.Blocks['procedures_callnoreturn'] = { +Blockly.Blocks['procedures_call'] = { /** * Block for calling a procedure with no return value. * @this Blockly.Block @@ -592,17 +608,15 @@ Blockly.Blocks['procedures_callnoreturn'] = { // Exist on all three blocks, but have different implementations. mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation, - populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputCaller_, - addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_, - - // Shared with procedures_declaration_root, but with a different implementation. - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_, + populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnCaller_, + addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_, // Only exists on the external caller. + attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_, buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ }; -Blockly.Blocks['procedures_callnoreturn_internal'] = { +Blockly.Blocks['procedures_prototype'] = { /** * Block for calling a procedure with no return value, for rendering inside * define block. @@ -631,11 +645,49 @@ Blockly.Blocks['procedures_callnoreturn_internal'] = { // Exist on all three blocks, but have different implementations. mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, - populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputCallerInternal_, - addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelCaller_, + populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnPrototype_, + addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_, + + // Only exists on procedures_prototype. + createArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_ +}; + +Blockly.Blocks['procedures_declaration'] = { + /** + * The root block in the procedure declaration editor. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "extensions": ["colours_more", "shape_statement"] + }); + /* Data known about the procedure. */ + this.procCode_ = ''; + this.displayNames_ = []; + this.argumentIds_ = []; + this.argumentDefaults_ = []; + this.warp_ = false; + }, + // Shared. + getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, + removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, + disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, + deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, + createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, + updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, + + // Exist on all three blocks, but have different implementations. + mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, + domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, + populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_, + addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelEditor_, - // Only exists on the internal caller. - attachArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.attachArgumentReporter_ + // Only exist on procedures_declaration. + createArgumentEditor_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_, + addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, + addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, + addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, + onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ }; Blockly.Blocks['argument_reporter_boolean'] = { @@ -668,7 +720,7 @@ Blockly.Blocks['argument_reporter_string_number'] = { } }; -Blockly.Blocks['boolean_textinput'] = { +Blockly.Blocks['argument_editor_boolean'] = { init: function() { this.jsonInit({ "message0": " %1", "args0": [ @@ -686,42 +738,21 @@ Blockly.Blocks['boolean_textinput'] = { } }; -Blockly.Blocks['procedures_declaration_root'] = { - /** - * The root block in the procedure editing workspace. - * @this Blockly.Block - */ +Blockly.Blocks['argument_editor_string_number'] = { init: function() { - this.jsonInit({ - "extensions": ["colours_more", "shape_statement"] + this.jsonInit({ "message0": " %1", + "args0": [ + { + "type": "field_input", + "name": "TEXT", + "text": "foo" + } + ], + "colour": Blockly.Colours.textField, + "colourSecondary": Blockly.Colours.textField, + "colourTertiary": Blockly.Colours.textField, + "extensions": ["output_number", "output_string"] }); - /* Data known about the procedure. */ - this.procCode_ = ''; - this.displayNames_ = []; - this.argumentIds_ = []; - this.argumentDefaults_ = []; - this.warp_ = false; - }, - // Shared. - getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, - disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, - deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, - createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - - // Exist on all three blocks, but have different implementations. - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, - domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, - populateInput_: Blockly.ScratchBlocks.ProcedureUtils.populateInputDeclarationRoot_, - addLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelMutatorRoot_, - - // Shared with procedures_callnoreturn, but with a different implementation. - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadowDeclarationRoot_, - - // Only exist on the mutator root. - addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, - addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, - addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, - onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateProcCodeDeclarationRoot_ + } }; + diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js index 5e02301324..eb2ff1ca04 100644 --- a/core/block_render_svg_vertical.js +++ b/core/block_render_svg_vertical.js @@ -490,12 +490,6 @@ Blockly.BlockSvg.TOP_RIGHT_CORNER_DEFINE_HAT = */ Blockly.BlockSvg.DEFINE_BLOCK_PADDING_RIGHT = 2 * Blockly.BlockSvg.GRID_UNIT; -/** - * The type of all define blocks, which have custom rendering. - * @cost - */ -Blockly.BlockSvg.DEFINE_BLOCK_TYPE = 'procedures_defnoreturn'; - /** * Change the colour of a block. */ @@ -1159,7 +1153,7 @@ Blockly.BlockSvg.prototype.renderClassify_ = function() { */ Blockly.BlockSvg.prototype.renderDrawTop_ = function(steps, rightEdge) { /* eslint-disable indent */ - if (this.type == Blockly.BlockSvg.DEFINE_BLOCK_TYPE) { + if (this.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { steps.push('m 0, 0'); steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_DEFINE_HAT); } else { @@ -1276,7 +1270,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, // Move to the start of the notch. cursorX = inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH; - if (this.type == Blockly.BlockSvg.DEFINE_BLOCK_TYPE) { + if (this.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { this.renderDefineBlock_(steps, inputRows, input, row); } else { Blockly.BlockSvg.drawStatementInputFromTopRight_(steps, cursorX, @@ -1290,7 +1284,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, this.width = Math.max(this.width, inputRows.statementEdge + input.connection.targetBlock().getHeightWidth().width); } - if (this.type != Blockly.BlockSvg.DEFINE_BLOCK_TYPE && + if (this.type != Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE && (y == inputRows.length - 1 || inputRows[y + 1].type == Blockly.NEXT_STATEMENT)) { // If the final input is a statement stack, add a small row underneath. diff --git a/core/connection.js b/core/connection.js index 8cc1bda521..fedacf4e74 100644 --- a/core/connection.js +++ b/core/connection.js @@ -310,8 +310,8 @@ Blockly.Connection.prototype.canConnectWithReason_ = function(target) { return Blockly.Connection.REASON_CHECKS_FAILED; } else if (blockA.isShadow() && !blockB.isShadow()) { return Blockly.Connection.REASON_SHADOW_PARENT; - } else if (blockA.type == 'procedures_defnoreturn' && - blockB.type != 'procedures_callnoreturn_internal' && + } else if (blockA.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE && + blockB.type != 'procedures_prototype' && superiorConn == blockA.getInput('custom_block').connection ) { // Hack to fix #1127: Fail attempts to connect to the custom_block input // on a defnoreturn block, unless the connecting block is a specific type. diff --git a/core/constants.js b/core/constants.js index 323900ee45..eb61bb11ab 100644 --- a/core/constants.js +++ b/core/constants.js @@ -326,3 +326,21 @@ Blockly.RENAME_VARIABLE_ID = 'RENAME_VARIABLE_ID'; * @const {string} */ Blockly.DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID'; + +/** + * The type of all procedure definition blocks. + * @const {string} + */ +Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE = 'procedures_definition'; + +/** + * The type of all procedure prototype blocks. + * @const {string} + */ +Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE = 'procedures_prototype'; + +/** + * The type of all procedure call blocks. + * @const {string} + */ +Blockly.PROCEDURES_CALL_BLOCK_TYPE = 'procedures_call'; diff --git a/core/procedures.js b/core/procedures.js index 57b63b2c34..7d7a66b56c 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -179,12 +179,12 @@ Blockly.Procedures.rename = function(name) { */ Blockly.Procedures.flyoutCategory = function(workspace) { var xmlList = []; - if (Blockly.Blocks['procedures_defnoreturn']) { - // + if (Blockly.Blocks[Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE]) { + // // do something // var block = goog.dom.createDom('block'); - block.setAttribute('type', 'procedures_defnoreturn'); + block.setAttribute('type', Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE); block.setAttribute('gap', 16); var nameField = goog.dom.createDom('field', null, Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE); @@ -221,7 +221,7 @@ Blockly.Procedures.flyoutCategory = function(workspace) { for (var i = 0; i < procedureList.length; i++) { var name = procedureList[i][0]; var args = procedureList[i][1]; - // + // // // // @@ -243,7 +243,7 @@ Blockly.Procedures.flyoutCategory = function(workspace) { var tuple = Blockly.Procedures.allProcedures(workspace); populateProcedures(tuple[0], 'procedures_callnoreturn'); - populateProcedures(tuple[1], 'procedures_callreturn'); + populateProcedures(tuple[1], 'procedures_call'); return xmlList; }; @@ -276,7 +276,7 @@ Blockly.Procedures.getCallers = function(name, ws, definitionRoot, var callers = []; for (var i = 0; i < allBlocks.length; i++) { var block = allBlocks[i]; - if (block.type == 'procedures_callnoreturn') { + if (block.type == Blockly.PROCEDURES_CALL_BLOCK_TYPE ) { var procCode = block.getProcCode(); if (procCode && procCode == name) { callers.push(block); @@ -342,7 +342,7 @@ Blockly.Procedures.getDefinition = function(name, workspace) { * @private */ Blockly.Procedures.editProcedureCallback_ = function(block) { - if (block.type == 'procedures_defnoreturn') { + if (block.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { var input = block.getInput('custom_block'); if (!input) { alert('Bad input'); // TODO: Decide what to do about this. @@ -354,7 +354,8 @@ Blockly.Procedures.editProcedureCallback_ = function(block) { return; } var innerBlock = conn.targetBlock(); - if (!innerBlock || !innerBlock.type == 'procedures_callnoreturn_internal') { + if (!innerBlock || + !innerBlock.type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { alert('Bad inner block'); // TODO: Decide what to do about this. return; } diff --git a/core/workspace.js b/core/workspace.js index a47ee29d45..7ffcfbd7b2 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -357,7 +357,7 @@ Blockly.Workspace.prototype.deleteVariable = function(name) { // Check whether this variable is a function parameter before deleting. var uses = this.getVariableUses(name); for (var i = 0, block; block = uses[i]; i++) { - if (block.type == 'procedures_defnoreturn' || + if (block.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE || block.type == 'procedures_defreturn') { var procedureName = block.getFieldValue('NAME'); Blockly.alert( diff --git a/tests/custom_procedure_playground.html b/tests/custom_procedure_playground.html index dc676563a8..835cfe73b4 100644 --- a/tests/custom_procedure_playground.html +++ b/tests/custom_procedure_playground.html @@ -67,20 +67,20 @@ diff --git a/tests/jsunit/connection_test.js b/tests/jsunit/connection_test.js index 663aa9e14a..480817da03 100644 --- a/tests/jsunit/connection_test.js +++ b/tests/jsunit/connection_test.js @@ -348,7 +348,7 @@ function test_canConnectWithReason_Procedures_WrongBlockType() { var sharedWorkspace = {}; var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT); one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); - one.sourceBlock_.type = 'procedures_defnoreturn'; + one.sourceBlock_.type = Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE; // Make one be the connection on its source block's input. one.sourceBlock_.getInput = function() { return { @@ -368,7 +368,7 @@ function test_canConnectWithReason_Procedures_Pass() { var sharedWorkspace = {}; var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT); one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); - one.sourceBlock_.type = 'procedures_defnoreturn'; + one.sourceBlock_.type = Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE; // Make one be the connection on its source block's input. one.sourceBlock_.getInput = function() { return { @@ -377,7 +377,7 @@ function test_canConnectWithReason_Procedures_Pass() { }; var two = helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT); two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); - two.sourceBlock_.type = 'procedures_callnoreturn_internal'; + two.sourceBlock_.type = Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE; assertEquals(Blockly.Connection.CAN_CONNECT, one.canConnectWithReason_(two)); } @@ -386,7 +386,7 @@ function test_canConnectWithReason_Procedures_NextConnection() { var sharedWorkspace = {}; var one = helper_createConnection(0, 0, Blockly.NEXT_STATEMENT); one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace); - one.sourceBlock_.type = 'procedures_defnoreturn'; + one.sourceBlock_.type = Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE; // One is the next connection, not an input connection one.sourceBlock_.nextConnection = one; one.sourceBlock_.getInput = function() { diff --git a/tests/jsunit/procedure_test.js b/tests/jsunit/procedure_test.js index f3d7b133cb..71b324e057 100644 --- a/tests/jsunit/procedure_test.js +++ b/tests/jsunit/procedure_test.js @@ -26,7 +26,7 @@ var workspace; //var mockControl_; function procedureTest_setUp() { - Blockly.Blocks['procedures_callnoreturn'] = { + Blockly.Blocks[Blockly.PROCEDURES_CALL_BLOCK_TYPE] = { init: function() { this.procCode_ = ''; this.setPreviousStatement(true); @@ -64,7 +64,7 @@ function procedureTest_setUp() { } function procedureTest_tearDown() { - delete Blockly.Blocks['procedures_callnoreturn']; + delete Blockly.Blocks[Blockly.PROCEDURES_CALL_BLOCK_TYPE]; delete Blockly.Blocks['foo']; delete Blockly.Blocks['loop']; //mockControl_.$tearDown(); @@ -74,7 +74,8 @@ function procedureTest_tearDown() { function test_findCallers_simple_oneCaller() { var xml = '' + '' + - '' + + '' + '' + ''; procedureTest_setUp(); @@ -94,7 +95,8 @@ function test_findCallers_simple_oneCaller() { function test_findCallers_noRecursion() { var xml = '' + '' + - '' + + '' + '' + ''; procedureTest_setUp(); @@ -117,7 +119,8 @@ function test_findCallers_noRecursion() { function test_findCallers_allowRecursion() { var xml = '' + '' + - '' + + '' + '' + ''; procedureTest_setUp(); @@ -161,7 +164,8 @@ function test_findCallers_simple_noCallers() { function test_findCallers_wrongProcCode() { var xml = '' + '' + - '' + + '' + '' + ''; procedureTest_setUp(); @@ -185,7 +189,8 @@ function test_findCallers_onStatementInput() { '' + '' + '' + - '' + + '' + '' + '' + '' + @@ -209,7 +214,8 @@ function test_findCallers_onStatementInput() { function test_findCallers_multipleStacks() { var xml = '' + '' + - ''+ + ''+ '' + ''; procedureTest_setUp(); @@ -230,8 +236,10 @@ function test_findCallers_multipleStacks() { function test_findCallers_multipleCallers() { var xml = '' + - '' + - ''+ + '' + + ''+ ''; procedureTest_setUp(); try { @@ -256,7 +264,8 @@ function test_deleteProcedure_noCallers() { // If there are no callers, the stack should be deleted. procedureTest_setUp(); var xml = '' + - '' + + '' + '' + '' + '' + @@ -286,7 +295,8 @@ function test_deleteProcedure_recursiveCaller() { '' + '' + '' + - '' + + '' + '' + '' + '' + @@ -311,7 +321,8 @@ function test_deleteProcedure_nonRecursiveCaller() { procedureTest_setUp(); var xml = '' + - '' + + '' + '' + '' + ''; From 20aaefe810ae4eae7db463d467a2f6a5b1c27aa9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 15 Nov 2017 13:05:39 -0500 Subject: [PATCH 0232/2135] Update blocks for the procedure flyout category --- blocks_vertical/default_toolbox.js | 2 + core/procedures.js | 109 +++++++++++++---------------- msg/messages.js | 4 ++ 3 files changed, 55 insertions(+), 60 deletions(-) diff --git a/blocks_vertical/default_toolbox.js b/blocks_vertical/default_toolbox.js index f8c375c318..0cc8bceabf 100644 --- a/blocks_vertical/default_toolbox.js +++ b/blocks_vertical/default_toolbox.js @@ -644,4 +644,6 @@ Blockly.Blocks.defaultToolbox = '' + '' + + '' + + '' + ''; diff --git a/core/procedures.js b/core/procedures.js index 7d7a66b56c..c9998e7232 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -73,6 +73,25 @@ Blockly.Procedures.allProcedures = function(root) { return [proceduresNoReturn, proceduresReturn]; }; +/** + * Find all user-created procedure definition mutations in a workspace. + * @param {!Blockly.Workspace} root Root workspace. + * @return {!Array.} Array of mutation xml elements. + */ +Blockly.Procedures.allProcedureMutations = function(root) { + var blocks = root.getAllBlocks(); + var mutations = []; + for (var i = 0; i < blocks.length; i++) { + if (blocks[i].type === 'procedures_callnoreturn_internal') { + var mutation = blocks[i].mutationToDom(); + if (mutation) { + mutations.push(mutation); + } + } + } + return mutations; +}; + /** * Comparison function for case-insensitive sorting of the first element of * a tuple. @@ -179,71 +198,32 @@ Blockly.Procedures.rename = function(name) { */ Blockly.Procedures.flyoutCategory = function(workspace) { var xmlList = []; - if (Blockly.Blocks[Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE]) { - // - // do something - // - var block = goog.dom.createDom('block'); - block.setAttribute('type', Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE); - block.setAttribute('gap', 16); - var nameField = goog.dom.createDom('field', null, - Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE); - nameField.setAttribute('name', 'NAME'); - block.appendChild(nameField); - xmlList.push(block); - } - if (Blockly.Blocks['procedures_defreturn']) { - // - // do something + + // New procedure button + var button = goog.dom.createDom('button'); + var msg = Blockly.Msg.NEW_PROCEDURE; + var callbackKey = 'CREATE_PROCEDURE'; + var callback = function(button) { + Blockly.Procedures.createProcedureDefCallback_(); + }; + button.setAttribute('text', msg); + button.setAttribute('callbackKey', callbackKey); + workspace.registerButtonCallback(callbackKey, callback); + xmlList.push(button); + + // Create call blocks for each procedure defined in the workspace + var mutations = Blockly.Procedures.allProcedureMutations(workspace); + for (var i = 0; i < mutations.length; i++) { + var mutation = mutations[i]; + // + // // var block = goog.dom.createDom('block'); - block.setAttribute('type', 'procedures_defreturn'); - block.setAttribute('gap', 16); - var nameField = goog.dom.createDom('field', null, - Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE); - nameField.setAttribute('name', 'NAME'); - block.appendChild(nameField); - xmlList.push(block); - } - if (Blockly.Blocks['procedures_report']) { - // - var block = goog.dom.createDom('block'); - block.setAttribute('type', 'procedures_report'); + block.setAttribute('type', 'procedures_call'); block.setAttribute('gap', 16); + block.appendChild(mutation); xmlList.push(block); } - if (xmlList.length) { - // Add slightly larger gap between system blocks and user calls. - xmlList[xmlList.length - 1].setAttribute('gap', 24); - } - - function populateProcedures(procedureList, templateName) { - for (var i = 0; i < procedureList.length; i++) { - var name = procedureList[i][0]; - var args = procedureList[i][1]; - // - // - // - // - // - var block = goog.dom.createDom('block'); - block.setAttribute('type', templateName); - block.setAttribute('gap', 16); - var mutation = goog.dom.createDom('mutation'); - mutation.setAttribute('name', name); - block.appendChild(mutation); - for (var j = 0; j < args.length; j++) { - var arg = goog.dom.createDom('arg'); - arg.setAttribute('name', args[j]); - mutation.appendChild(arg); - } - xmlList.push(block); - } - } - - var tuple = Blockly.Procedures.allProcedures(workspace); - populateProcedures(tuple[0], 'procedures_callnoreturn'); - populateProcedures(tuple[1], 'procedures_call'); return xmlList; }; @@ -335,6 +315,15 @@ Blockly.Procedures.getDefinition = function(name, workspace) { return null; }; +/** + * Callback to create a new procedure custom command block. + * TODO(#1299): Implement. + * @private + */ +Blockly.Procedures.createProcedureDefCallback_ = function() { + alert('TODO(#1299) Implement procedure creation callback.'); +}; + /** * Callback to open the modal for editing custom procedures. * TODO(#603): Implement. diff --git a/msg/messages.js b/msg/messages.js index 27effae304..5c30284ec9 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -136,6 +136,10 @@ Blockly.Msg.VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE = 'A variable named "%1" al /// alert - Tells the user that the name they entered is already in use for a procedure. Blockly.Msg.PROCEDURE_ALREADY_EXISTS = 'A procedure named "%1" already exists.'; +// Custom procedure creation +/// button text - Text on the button used to launch the procedure creation dialogue. +Blockly.Msg.NEW_PROCEDURE = 'Make a Block...'; + // List creation /// button text - Text on the button used to launch the list creation dialogue. Blockly.Msg.NEW_LIST = 'Create list...'; From 08c975299aaea36717f4443954a7296dbf39026f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 16 Nov 2017 16:01:02 -0500 Subject: [PATCH 0233/2135] Refactor create button code --- core/procedures.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index c9998e7232..5222008e02 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -82,7 +82,7 @@ Blockly.Procedures.allProcedureMutations = function(root) { var blocks = root.getAllBlocks(); var mutations = []; for (var i = 0; i < blocks.length; i++) { - if (blocks[i].type === 'procedures_callnoreturn_internal') { + if (blocks[i].type === 'procedures_prototype') { var mutation = blocks[i].mutationToDom(); if (mutation) { mutations.push(mutation); @@ -199,17 +199,7 @@ Blockly.Procedures.rename = function(name) { Blockly.Procedures.flyoutCategory = function(workspace) { var xmlList = []; - // New procedure button - var button = goog.dom.createDom('button'); - var msg = Blockly.Msg.NEW_PROCEDURE; - var callbackKey = 'CREATE_PROCEDURE'; - var callback = function(button) { - Blockly.Procedures.createProcedureDefCallback_(); - }; - button.setAttribute('text', msg); - button.setAttribute('callbackKey', callbackKey); - workspace.registerButtonCallback(callbackKey, callback); - xmlList.push(button); + Blockly.Procedures.addCreateButton_(xmlList); // Create call blocks for each procedure defined in the workspace var mutations = Blockly.Procedures.allProcedureMutations(workspace); @@ -227,6 +217,24 @@ Blockly.Procedures.flyoutCategory = function(workspace) { return xmlList; }; +/** + * Create the "Make a Block..." button. + * @param {!Array.} xmlList Array of XML block elements to add to. + * @private + */ +Blockly.Procedures.addCreateButton_ = function(xmlList) { + var button = goog.dom.createDom('button'); + var msg = Blockly.Msg.NEW_PROCEDURE; + var callbackKey = 'CREATE_PROCEDURE'; + var callback = function(button) { + Blockly.Procedures.createProcedureDefCallback_(); + }; + button.setAttribute('text', msg); + button.setAttribute('callbackKey', callbackKey); + workspace.registerButtonCallback(callbackKey, callback); + xmlList.push(button); +}; + /** * Find all callers of a named procedure. * @param {string} name Name of procedure (procCode in scratch-blocks). From 010668a0825068b23d41547401370d7e84b56179 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 16 Nov 2017 16:30:47 -0500 Subject: [PATCH 0234/2135] Fix tests! --- core/procedures.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index 5222008e02..aa4a5e69e7 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -199,7 +199,7 @@ Blockly.Procedures.rename = function(name) { Blockly.Procedures.flyoutCategory = function(workspace) { var xmlList = []; - Blockly.Procedures.addCreateButton_(xmlList); + Blockly.Procedures.addCreateButton_(workspace, xmlList); // Create call blocks for each procedure defined in the workspace var mutations = Blockly.Procedures.allProcedureMutations(workspace); @@ -219,14 +219,15 @@ Blockly.Procedures.flyoutCategory = function(workspace) { /** * Create the "Make a Block..." button. + * @param {!Blockly.Workspace} workspace The workspace contianing procedures. * @param {!Array.} xmlList Array of XML block elements to add to. * @private */ -Blockly.Procedures.addCreateButton_ = function(xmlList) { +Blockly.Procedures.addCreateButton_ = function(workspace, xmlList) { var button = goog.dom.createDom('button'); var msg = Blockly.Msg.NEW_PROCEDURE; var callbackKey = 'CREATE_PROCEDURE'; - var callback = function(button) { + var callback = function() { Blockly.Procedures.createProcedureDefCallback_(); }; button.setAttribute('text', msg); From ed072e258c4ed680ef2527b834c0ecaa581269f4 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 16 Nov 2017 14:27:31 -0800 Subject: [PATCH 0235/2135] Return early from field init if the field has already been initialized. --- core/field_colour.js | 4 ++++ core/field_colour_slider.js | 4 ++++ core/field_iconmenu.js | 4 ++++ core/field_textdropdown.js | 4 ++++ core/field_textinput.js | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/core/field_colour.js b/core/field_colour.js index 65a4d91290..088b8024d8 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -70,6 +70,10 @@ Blockly.FieldColour.prototype.columns_ = 0; * @param {!Blockly.Block} block The block containing this field. */ Blockly.FieldColour.prototype.init = function(block) { + if (this.fieldGroup_) { + // Colour field has already been initialized once. + return; + } Blockly.FieldColour.superClass_.init.call(this, block); this.setValue(this.getValue()); }; diff --git a/core/field_colour_slider.js b/core/field_colour_slider.js index 9564ca428e..13de3230b3 100644 --- a/core/field_colour_slider.js +++ b/core/field_colour_slider.js @@ -71,6 +71,10 @@ Blockly.FieldColourSlider.EYEDROPPER_PATH = 'eyedropper.svg'; * @param {!Blockly.Block} block The block containing this field. */ Blockly.FieldColourSlider.prototype.init = function(block) { + if (this.fieldGroup_) { + // Colour slider has already been initialized once. + return; + } Blockly.FieldColourSlider.superClass_.init.call(this, block); this.setValue(this.getValue()); }; diff --git a/core/field_iconmenu.js b/core/field_iconmenu.js index 7f43beb8ff..5cb2381f49 100644 --- a/core/field_iconmenu.js +++ b/core/field_iconmenu.js @@ -69,6 +69,10 @@ Blockly.FieldIconMenu.savedPrimary_ = null; * @param {Block} block The owning block. */ Blockly.FieldIconMenu.prototype.init = function(block) { + if (this.fieldGroup_) { + // Icon menu has already been initialized once. + return; + } // Render the arrow icon // Fixed sizes in px. Saved for creating the flip transform of the menu renders above the button. var arrowSize = 12; diff --git a/core/field_textdropdown.js b/core/field_textdropdown.js index c2e6f9642e..a9350fb210 100644 --- a/core/field_textdropdown.js +++ b/core/field_textdropdown.js @@ -59,6 +59,10 @@ goog.inherits(Blockly.FieldTextDropdown, Blockly.FieldTextInput); * Install this text drop-down field on a block. */ Blockly.FieldTextDropdown.prototype.init = function() { + if (this.fieldGroup_) { + // Text input + dropdown has already been initialized once. + return; + } Blockly.FieldTextDropdown.superClass_.init.call(this); // Add dropdown arrow: "option ▾" (LTR) or "▾ אופציה" (RTL) // Positioned on render, after text size is calculated. diff --git a/core/field_textinput.js b/core/field_textinput.js index 1eb6f8fe5a..8f4a573239 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -91,6 +91,10 @@ Blockly.FieldTextInput.prototype.spellcheck_ = true; * Install this text field on a block. */ Blockly.FieldTextInput.prototype.init = function() { + if (this.fieldGroup_) { + // Field has already been initialized once. + return; + } Blockly.FieldTextInput.superClass_.init.call(this); // If not in a shadow block, draw a box. From ab788e21c1aa79a76e2c91566d27d5686da485ab Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 16 Nov 2017 15:34:39 -0800 Subject: [PATCH 0236/2135] Tweak text input rendering when not on a shadow block --- core/field_textinput.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index 1eb6f8fe5a..8452ded138 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -96,14 +96,12 @@ Blockly.FieldTextInput.prototype.init = function() { // If not in a shadow block, draw a box. if (!this.sourceBlock_.isShadow()) { this.box_ = Blockly.utils.createSvgElement('rect', { - 'rx': Blockly.BlockSvg.CORNER_RADIUS, - 'ry': Blockly.BlockSvg.CORNER_RADIUS, 'x': 0, 'y': 0, 'width': this.size_.width, 'height': this.size_.height, 'fill': Blockly.Colours.textField, - 'stroke': this.sourceBlock_.getColourTertiary() + 'fill-opacity': 0.3 }); this.fieldGroup_.insertBefore(this.box_, this.textElement_); } @@ -186,7 +184,7 @@ Blockly.FieldTextInput.prototype.setRestrictor = function(restrictor) { * @private */ Blockly.FieldTextInput.prototype.showEditor_ = function( - opt_quietInput, opt_readOnly, opt_withArrow, opt_arrowCallback) { + opt_quietInput, opt_readOnly, opt_withArrow, opt_arrowCallback) { this.workspace_ = this.sourceBlock_.workspace; var quietInput = opt_quietInput || false; var readOnly = opt_readOnly || false; From 6eafe6ed51f9b3605d05be51d07ff86acddb8bfd Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Nov 2017 10:49:46 -0500 Subject: [PATCH 0237/2135] If a field dropdown is used to create a new message, the field then gets updated with the name of the newly created message. There are also some todo comments here for future work that has been documented in two github issues (#1244 and #1245) . --- core/field_variable.js | 7 +++++-- core/variable_map.js | 5 +++++ core/variables.js | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/field_variable.js b/core/field_variable.js index 22ea3ed0c7..218dc812d7 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -257,8 +257,11 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) { workspace.deleteVariable(this.getText()); return; } else if (id == Blockly.NEW_MESSAGE_ID) { - Blockly.Variables.createVariable(workspace, null, 'broadcast_msg'); - // TODO update sourceblock's variable name... + var thisField = this; + var setName = function(newName) { + thisField.setValue(newName); + }; + Blockly.Variables.createVariable(workspace, setName, 'broadcast_msg'); return; } diff --git a/core/variable_map.js b/core/variable_map.js index c6ffe57486..9d2063fa7a 100644 --- a/core/variable_map.js +++ b/core/variable_map.js @@ -119,6 +119,11 @@ Blockly.VariableMap.prototype.createVariable = function(name, opt_type, opt_id) var variable = this.getVariable(name); if (variable) { if (opt_type && variable.type != opt_type) { + // TODO (#1245) + // We want to be able to create new broadcast messages that have the same + // name as variables or lists... and vice-versa + // also need to make sure that all the places where variables are looked + // up by name also do the right thing with their types... throw Error('Variable "' + name + '" is already in use and its type is "' + variable.type + '" which conflicts with the passed in ' + 'type, "' + opt_type + '".'); diff --git a/core/variables.js b/core/variables.js index f1e5cbb20f..30579f427c 100644 --- a/core/variables.js +++ b/core/variables.js @@ -173,6 +173,10 @@ Blockly.Variables.generateUniqueName = function(workspace) { * @param {string} opt_type Optional type of variable, like 'string' or 'list'. */ Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) { + // (karishma) TODO (#1244) Modal message should change depending on what type + // (opt_type above) of variable we are creating. If opt_type is not provided, + // we should default to the original 'NEW_VARIABLE_TITLE' message for + // scalar variables. // This function needs to be named so it can be called recursively. var promptAndCheckWithAlert = function(defaultName) { Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName, @@ -227,6 +231,8 @@ Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) { */ Blockly.Variables.renameVariable = function(workspace, variable, opt_callback) { + // (karishma) TODO (#1244) Modal message should change depending on what type + // of variable is getting renamed. // This function needs to be named so it can be called recursively. var promptAndCheckWithAlert = function(defaultName) { Blockly.Variables.promptName( From fc42d581e6873c4fb9804d02e5312516503e4358 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 17 Nov 2017 11:21:56 -0500 Subject: [PATCH 0238/2135] Fix field_textinput width when not in a shadow --- core/field_textinput.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index 1eb6f8fe5a..3aed0d9524 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -419,6 +419,13 @@ Blockly.FieldTextInput.prototype.resizeEditor_ = function() { var scale = this.sourceBlock_.workspace.scale; var div = Blockly.WidgetDiv.DIV; + var initialWidth; + if (this.sourceBlock_.isShadow()) { + initialWidth = this.sourceBlock_.getHeightWidth().width * scale; + } else { + initialWidth = this.size_.width * scale; + } + var width; if (Blockly.BlockSvg.FIELD_TEXTINPUT_EXPAND_PAST_TRUNCATION) { // Resize the box based on the measured width of the text, pre-truncation @@ -434,7 +441,7 @@ Blockly.FieldTextInput.prototype.resizeEditor_ = function() { width = textWidth; } else { // Set width to (truncated) block size. - width = this.sourceBlock_.getHeightWidth().width * scale; + width = initialWidth; } // The width must be at least FIELD_WIDTH and at most FIELD_WIDTH_MAX_EDIT width = Math.max(width, Blockly.BlockSvg.FIELD_WIDTH_MIN_EDIT * scale); @@ -447,10 +454,7 @@ Blockly.FieldTextInput.prototype.resizeEditor_ = function() { // Use margin-left to animate repositioning of the box (value is unscaled). // This is the difference between the default position and the positioning // after growing the box. - var fieldWidth = this.sourceBlock_.getHeightWidth().width; - var initialWidth = fieldWidth * scale; - var finalWidth = width; - div.style.marginLeft = -0.5 * (finalWidth - initialWidth) + 'px'; + div.style.marginLeft = -0.5 * (width - initialWidth) + 'px'; // Add 0.5px to account for slight difference between SVG and CSS border var borderRadius = this.getBorderRadius() + 0.5; @@ -522,9 +526,14 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() { div.style.boxShadow = ''; // Resize to actual size of final source block. if (thisField.sourceBlock_) { - var size = thisField.sourceBlock_.getHeightWidth(); - div.style.width = (size.width + 1) + 'px'; - div.style.height = (size.height + 1) + 'px'; + if (thisField.sourceBlock_.isShadow()) { + var size = thisField.sourceBlock_.getHeightWidth(); + div.style.width = (size.width + 1) + 'px'; + div.style.height = (size.height + 1) + 'px'; + } else { + div.style.width = (thisField.size_.width + 1) + 'px'; + div.style.height = (Blockly.BlockSvg.FIELD_HEIGHT_MAX_EDIT + 1) + 'px'; + } } div.style.marginLeft = 0; }; From ee3aee18ee64c8a48878107d8231d1693ce57a0f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 17 Nov 2017 13:45:10 -0500 Subject: [PATCH 0239/2135] Fix rendering for solitary fields --- core/field.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/field.js b/core/field.js index 7764e1b778..72019587b9 100644 --- a/core/field.js +++ b/core/field.js @@ -696,9 +696,9 @@ Blockly.Field.prototype.setTooltip = function(/*newTip*/) { * Select the element to bind the click handler to. When this element is * clicked on an editable field, the editor will open. * - *

If the block has multiple fields, this is just the group containing the - * field. If the block has only one field, we handle clicks over the whole - * block. + * If the block has only one field and no output connection, we handle clicks + * over the whole block. Otherwise, handle clicks over the the group containing + * the field. * * @return {!Element} Element to bind click handler to. * @private @@ -709,8 +709,7 @@ Blockly.Field.prototype.getClickTarget_ = function() { for (var i = 0, input; input = this.sourceBlock_.inputList[i]; i++) { nFields += input.fieldRow.length; } - - if (nFields <= 1) { + if (nFields <= 1 && this.sourceBlock_.outputConnection) { return this.sourceBlock_.getSvgRoot(); } else { return this.getSvgRoot(); From ebd3978cde50fc085570795d6c1a976001ac68f7 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Nov 2017 13:54:35 -0500 Subject: [PATCH 0240/2135] Make one of the comments clearer. --- core/field_variable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/field_variable.js b/core/field_variable.js index 218dc812d7..a897a5b66e 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -84,7 +84,7 @@ Blockly.FieldVariable.prototype.initModel = function() { // Check if there was exactly one element specified in the // variableTypes list. This is the list that specifies which types of // variables to include in the dropdown. - // If there is exactly one element specified, make the default variable + // If there is exactly one element specified, make the variable // being created this specified type. Else, default behavior is to create // a scalar variable if (this.getVariableTypes_().length === 1) { From cd5fe76811623d6ed428b57b46264f1e0a11acc7 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Fri, 17 Nov 2017 13:59:27 -0500 Subject: [PATCH 0241/2135] Another comment clarification. --- core/xml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/xml.js b/core/xml.js index a8344ebb11..83ce8adc37 100644 --- a/core/xml.js +++ b/core/xml.js @@ -120,7 +120,7 @@ Blockly.Xml.blockToDom = function(block, opt_noId) { // Above works well for untyped variables, but we need to correctly // set the type for blocks that exist by default in the toolbox // (e.g. broadcast messages) - // TODO figure out if we ever need to do something where there's + // TODO figure out if we need to do something different when there's // more than one element in variableTypes field // must check that field is an instance of FieldVariable because From c1044047f0420ee06a42b255a3310ffebc1d6a7c Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 20 Nov 2017 11:45:27 -0500 Subject: [PATCH 0242/2135] Changing new message constant name to be more useful because Blockly.Msg.NEW_MESSAGE will probably get confusing. --- core/constants.js | 2 +- core/field_variable.js | 4 ++-- msg/messages.js | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/constants.js b/core/constants.js index 5d96685918..4e347372be 100644 --- a/core/constants.js +++ b/core/constants.js @@ -334,4 +334,4 @@ Blockly.DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID'; * and if selected, should trigger the prompt to create a new message. * @const {string} */ -Blockly.NEW_MESSAGE_ID = 'NEW_MESSAGE_ID'; +Blockly.NEW_BROADCAST_MESSAGE_ID = 'NEW_BROADCAST_MESSAGE_ID'; diff --git a/core/field_variable.js b/core/field_variable.js index a897a5b66e..7db4fb3399 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -216,7 +216,7 @@ Blockly.FieldVariable.dropdownCreate = function() { options[i] = [variableModelList[i].name, variableModelList[i].getId()]; } if (isBroadcastType) { - options.push([Blockly.Msg.NEW_MESSAGE, Blockly.NEW_MESSAGE_ID]); + options.push([Blockly.Msg.NEW_BROADCAST_MESSAGE, Blockly.NEW_BROADCAST_MESSAGE_ID]); } else { options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]); if (Blockly.Msg.DELETE_VARIABLE) { @@ -256,7 +256,7 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) { // Delete variable. workspace.deleteVariable(this.getText()); return; - } else if (id == Blockly.NEW_MESSAGE_ID) { + } else if (id == Blockly.NEW_BROADCAST_MESSAGE_ID) { var thisField = this; var setName = function(newName) { thisField.setValue(newName); diff --git a/msg/messages.js b/msg/messages.js index 3845d815e2..5bcb6c616d 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -140,6 +140,10 @@ Blockly.Msg.PROCEDURE_ALREADY_EXISTS = 'A procedure named "%1" already exists.'; /// button text - Text on the button used to launch the list creation dialogue. Blockly.Msg.NEW_LIST = 'Create list...'; +// Broadcast Message creation +/// dropdown choice - Create a new message. +Blockly.Msg.NEW_BROADCAST_MESSAGE = 'New message...'; + // Variable deletion. /// confirm - Ask the user to confirm their deletion of multiple uses of a variable. Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = 'Delete %1 uses of the "%2" variable?'; @@ -148,10 +152,6 @@ Blockly.Msg.CANNOT_DELETE_VARIABLE_PROCEDURE = 'Can\'t delete the variable "%1" /// dropdown choice - Delete the currently selected variable. Blockly.Msg.DELETE_VARIABLE = 'Delete the "%1" variable'; -// Broadcast Message creation -/// dropdown choice - Create a new message. -Blockly.Msg.NEW_MESSAGE = 'New message...'; - // Colour Blocks. /// url - Information about colour. Blockly.Msg.COLOUR_PICKER_HELPURL = 'https://en.wikipedia.org/wiki/Color'; From 2d0d9dba0bd49d661436b6b700c042929607acd2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 20 Nov 2017 12:05:55 -0500 Subject: [PATCH 0243/2135] Making default name for broadcast messages amenable to translation. --- blocks_vertical/event.js | 8 ++++---- msg/messages.js | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/blocks_vertical/event.js b/blocks_vertical/event.js index a9d303e81a..82dc59e222 100644 --- a/blocks_vertical/event.js +++ b/blocks_vertical/event.js @@ -80,7 +80,7 @@ Blockly.Blocks['event_whenbroadcastreceived'] = { "type": "field_variable", "name": "BROADCAST_OPTION", "variableTypes": ["broadcast_msg"], - "variable": "message1" + "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], "category": Blockly.Categories.event, @@ -153,7 +153,7 @@ Blockly.Blocks['event_broadcast_menu'] = { "type": "field_variable", "name": "BROADCAST_OPTION", "variableTypes":["broadcast_msg"], - "variable":"message1" + "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], "colour": Blockly.Colours.event.secondary, @@ -178,7 +178,7 @@ Blockly.Blocks['event_broadcast'] = { "type": "field_variable", "name": "BROADCAST_OPTION", "variableTypes": ["broadcast_msg"], - "variable": "message1" + "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], "category": Blockly.Categories.event, @@ -200,7 +200,7 @@ Blockly.Blocks['event_broadcastandwait'] = { "type": "field_variable", "name": "BROADCAST_OPTION", "variableTypes": ["broadcast_msg"], - "variable":"message1" + "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], "category": Blockly.Categories.event, diff --git a/msg/messages.js b/msg/messages.js index 5bcb6c616d..85ebde2a88 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -143,6 +143,9 @@ Blockly.Msg.NEW_LIST = 'Create list...'; // Broadcast Message creation /// dropdown choice - Create a new message. Blockly.Msg.NEW_BROADCAST_MESSAGE = 'New message...'; +/// default broadcast message name +/// (default option in broadcast message dropdown menus) +Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME = 'message1'; // Variable deletion. /// confirm - Ask the user to confirm their deletion of multiple uses of a variable. From efc74609ac5e3b9215ffa2e92a7ac15d67160b26 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 20 Nov 2017 16:53:58 -0500 Subject: [PATCH 0244/2135] Refactoring 'broadcast_msg' to be referenced via a constant in core/constants instead of being hard-coded everywhere. --- blocks_vertical/event.js | 8 ++++---- core/constants.js | 8 ++++++++ core/field_variable.js | 4 ++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/blocks_vertical/event.js b/blocks_vertical/event.js index 82dc59e222..6f8d52057a 100644 --- a/blocks_vertical/event.js +++ b/blocks_vertical/event.js @@ -79,7 +79,7 @@ Blockly.Blocks['event_whenbroadcastreceived'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes": ["broadcast_msg"], + "variableTypes": [Blockly.BROADCAST_MESSAGE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], @@ -152,7 +152,7 @@ Blockly.Blocks['event_broadcast_menu'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes":["broadcast_msg"], + "variableTypes":[Blockly.BROADCAST_MESSAGE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], @@ -177,7 +177,7 @@ Blockly.Blocks['event_broadcast'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes": ["broadcast_msg"], + "variableTypes": [Blockly.BROADCAST_MESSAGE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], @@ -199,7 +199,7 @@ Blockly.Blocks['event_broadcastandwait'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes": ["broadcast_msg"], + "variableTypes": [Blockly.BROADCAST_MESSAGE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], diff --git a/core/constants.js b/core/constants.js index d40a91306d..3b9bbfbb14 100644 --- a/core/constants.js +++ b/core/constants.js @@ -336,6 +336,14 @@ Blockly.DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID'; */ Blockly.NEW_BROADCAST_MESSAGE_ID = 'NEW_BROADCAST_MESSAGE_ID'; +/** + * String representing the variable type of broadcast message blocks. + * This string, for use in differentiating between types of variables, + * indicates that the current variable is a broadcast message. + * @const {string} + */ +Blockly.BROADCAST_MESSAGE_TYPE = 'broadcast_msg'; + /** * The type of all procedure definition blocks. * @const {string} diff --git a/core/field_variable.js b/core/field_variable.js index 7db4fb3399..1fe1287164 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -190,7 +190,7 @@ Blockly.FieldVariable.dropdownCreate = function() { // doesn't modify the workspace's list. for (var i = 0; i < variableTypes.length; i++) { var variableType = variableTypes[i]; - if (variableType === 'broadcast_msg'){ + if (variableType === Blockly.BROADCAST_MESSAGE_TYPE){ isBroadcastType = true; } var variables = workspace.getVariablesOfType(variableType); @@ -261,7 +261,7 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) { var setName = function(newName) { thisField.setValue(newName); }; - Blockly.Variables.createVariable(workspace, setName, 'broadcast_msg'); + Blockly.Variables.createVariable(workspace, setName, Blockly.BROADCAST_MESSAGE_TYPE); return; } From d9b5dc09ce1e05e00b49e84bc925e9a43f624bd8 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 20 Nov 2017 17:01:42 -0500 Subject: [PATCH 0245/2135] Changing === to == --- core/field_variable.js | 4 ++-- core/xml.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/field_variable.js b/core/field_variable.js index 1fe1287164..894a7338d2 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -87,7 +87,7 @@ Blockly.FieldVariable.prototype.initModel = function() { // If there is exactly one element specified, make the variable // being created this specified type. Else, default behavior is to create // a scalar variable - if (this.getVariableTypes_().length === 1) { + if (this.getVariableTypes_().length == 1) { this.sourceBlock_.workspace.createVariable(this.getValue(), this.getVariableTypes_()[0]); } else { @@ -190,7 +190,7 @@ Blockly.FieldVariable.dropdownCreate = function() { // doesn't modify the workspace's list. for (var i = 0; i < variableTypes.length; i++) { var variableType = variableTypes[i]; - if (variableType === Blockly.BROADCAST_MESSAGE_TYPE){ + if (variableType == Blockly.BROADCAST_MESSAGE_TYPE){ isBroadcastType = true; } var variables = workspace.getVariablesOfType(variableType); diff --git a/core/xml.js b/core/xml.js index 83ce8adc37..3672dc9967 100644 --- a/core/xml.js +++ b/core/xml.js @@ -127,7 +127,7 @@ Blockly.Xml.blockToDom = function(block, opt_noId) { // FieldVariableGetter doesn't have getVariableTypes_ function if (field instanceof Blockly.FieldVariable) { var variableTypes = field.getVariableTypes_(); - if (variableTypes.length === 1) { + if (variableTypes.length == 1) { container.setAttribute('variabletype', variableTypes[0]); } } From f84e7649511b8a5706d625ec1650a5d8008661e2 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 20 Nov 2017 17:15:04 -0500 Subject: [PATCH 0246/2135] Adding some TODO comments referencing existing issues also discussed in this PR. --- core/variables.js | 4 ++++ core/xml.js | 1 + 2 files changed, 5 insertions(+) diff --git a/core/variables.js b/core/variables.js index 30579f427c..7715f4f92b 100644 --- a/core/variables.js +++ b/core/variables.js @@ -182,6 +182,8 @@ Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) { Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName, function(text) { if (text) { + // TODO (#1245) use separate namespaces for lists, variables, and + // broadcast messages if (workspace.getVariable(text)) { Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1', text.toLowerCase()), @@ -240,6 +242,8 @@ Blockly.Variables.renameVariable = function(workspace, variable, function(newName) { if (newName) { var newVariable = workspace.getVariable(newName); + // TODO (#1245) use separate namespaces for lists, variables, and + // broadcast messages if (newVariable && newVariable.type != variable.type) { Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE.replace('%1', newName.toLowerCase()).replace('%2', newVariable.type), diff --git a/core/xml.js b/core/xml.js index 3672dc9967..702041cd8d 100644 --- a/core/xml.js +++ b/core/xml.js @@ -112,6 +112,7 @@ Blockly.Xml.blockToDom = function(block, opt_noId) { container.setAttribute('name', field.name); if (field instanceof Blockly.FieldVariable || field instanceof Blockly.FieldVariableGetter) { + // TODO (#1253) Lookup variable by id instead of name var variable = block.workspace.getVariable(field.getValue()); if (variable) { container.setAttribute('id', variable.getId()); From 92460319032cfc5c1b7d3825c1bea52bd8e431e6 Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 20 Nov 2017 22:41:12 -0400 Subject: [PATCH 0247/2135] Sort the More category by procedure procCodes --- core/procedures.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/procedures.js b/core/procedures.js index aa4a5e69e7..24ef03db28 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -92,6 +92,31 @@ Blockly.Procedures.allProcedureMutations = function(root) { return mutations; }; +/** + * Sorts an array of procedure definition mutations alphabetically. + * (Does not mutate the given array.) + * @param {!Array.} mutations Array of mutation xml elements. + * @return {!Array.} Sorted array of mutation xml elements. + */ +Blockly.Procedures.sortProcedureMutations = function(mutations) { + var newMutations = mutations.slice(); + + newMutations.sort(function(a, b) { + var procCodeA = a.getAttribute('proccode'); + var procCodeB = b.getAttribute('proccode'); + + if (procCodeA < procCodeB) { + return -1; + } else if (procCodeA > procCodeB) { + return 1; + } else { + return 0; + } + }); + + return newMutations; +}; + /** * Comparison function for case-insensitive sorting of the first element of * a tuple. @@ -203,6 +228,7 @@ Blockly.Procedures.flyoutCategory = function(workspace) { // Create call blocks for each procedure defined in the workspace var mutations = Blockly.Procedures.allProcedureMutations(workspace); + mutations = Blockly.Procedures.sortProcedureMutations(mutations); for (var i = 0; i < mutations.length; i++) { var mutation = mutations[i]; // From 208d1892fc00571425339061f598af34f51cd1fb Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Mon, 20 Nov 2017 14:09:17 -0500 Subject: [PATCH 0248/2135] Fix for #1244 --- core/variables.js | 16 +++++++++++----- msg/messages.js | 4 ++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/core/variables.js b/core/variables.js index 7715f4f92b..0ec8a91db3 100644 --- a/core/variables.js +++ b/core/variables.js @@ -173,13 +173,19 @@ Blockly.Variables.generateUniqueName = function(workspace) { * @param {string} opt_type Optional type of variable, like 'string' or 'list'. */ Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) { - // (karishma) TODO (#1244) Modal message should change depending on what type - // (opt_type above) of variable we are creating. If opt_type is not provided, - // we should default to the original 'NEW_VARIABLE_TITLE' message for - // scalar variables. + // Decide on a modal message based on the opt_type. If opt_type was not + // provided, default to the original message for scalar variables. + var newMsg = ''; + if (opt_type === 'list') { + newMsg = Blockly.Msg.NEW_LIST_TITLE; + } else if (opt_type === 'broadcast_msg') { + newMsg = Blockly.Msg.NEW_BROADCAST_MESSAGE_TITLE; + } else { + newMsg = Blockly.Msg.NEW_VARIABLE_TITLE; + } // This function needs to be named so it can be called recursively. var promptAndCheckWithAlert = function(defaultName) { - Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName, + Blockly.Variables.promptName(newMsg, defaultName, function(text) { if (text) { // TODO (#1245) use separate namespaces for lists, variables, and diff --git a/msg/messages.js b/msg/messages.js index d153b4ccb5..2858813c3d 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -143,10 +143,14 @@ Blockly.Msg.NEW_PROCEDURE = 'Make a Block...'; // List creation /// button text - Text on the button used to launch the list creation dialogue. Blockly.Msg.NEW_LIST = 'Create list...'; +/// prompt - Prompts the user to enter the name for a new list +Blockly.Msg.NEW_LIST_TITLE = 'New list name:'; // Broadcast Message creation /// dropdown choice - Create a new message. Blockly.Msg.NEW_BROADCAST_MESSAGE = 'New message...'; +/// prompt - Prompts the user to enter the name for a new broadcast message +Blockly.Msg.NEW_BROADCAST_MESSAGE_TITLE = 'New message name:'; /// default broadcast message name /// (default option in broadcast message dropdown menus) Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME = 'message1'; From 91beeb9674caf04fb23ed30241aa39260dd0c72f Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 21 Nov 2017 10:41:25 -0500 Subject: [PATCH 0249/2135] Render label with secondary color background and white text --- core/css.js | 4 ++++ core/field_textinput.js | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/core/css.js b/core/css.js index 9f8eaaa46e..7302ce2f36 100644 --- a/core/css.js +++ b/core/css.js @@ -452,6 +452,10 @@ Blockly.Css.CONTENT = [ 'fill: $colour_text;', '}', + '.blocklyEditableText>.blocklyEditableLabel {', + 'fill: #fff;', + '}', + '.blocklyDropdownText {', 'fill: #fff !important;', '}', diff --git a/core/field_textinput.js b/core/field_textinput.js index fc48eef44c..fe2510921c 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -96,16 +96,22 @@ Blockly.FieldTextInput.prototype.init = function() { return; } + var notInShadow = !this.sourceBlock_.isShadow(); + + if (notInShadow) { + this.className_ += ' blocklyEditableLabel'; + } + Blockly.FieldTextInput.superClass_.init.call(this); + // If not in a shadow block, draw a box. - if (!this.sourceBlock_.isShadow()) { + if (notInShadow) { this.box_ = Blockly.utils.createSvgElement('rect', { 'x': 0, 'y': 0, 'width': this.size_.width, 'height': this.size_.height, - 'fill': Blockly.Colours.textField, - 'fill-opacity': 0.3 + 'fill': this.sourceBlock_.getColourTertiary() }); this.fieldGroup_.insertBefore(this.box_, this.textElement_); } From a16fc794c2e4e65c6f51c056bb9acac8890b5705 Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Tue, 21 Nov 2017 13:49:47 -0500 Subject: [PATCH 0250/2135] Making 'list' specifiying variable type for list blocks a constant instead of a hardcoded value. Refactoring existing code to use this new constant. Updated name of constant referring to broadcast message variable type to make it more specific/match new name convention for variable type names. --- blocks_vertical/event.js | 8 ++++---- core/constants.js | 10 +++++++++- core/data_category.js | 4 ++-- core/field_variable.js | 4 ++-- core/variables.js | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/blocks_vertical/event.js b/blocks_vertical/event.js index 6f8d52057a..5c1a6dc753 100644 --- a/blocks_vertical/event.js +++ b/blocks_vertical/event.js @@ -79,7 +79,7 @@ Blockly.Blocks['event_whenbroadcastreceived'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes": [Blockly.BROADCAST_MESSAGE_TYPE], + "variableTypes": [Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], @@ -152,7 +152,7 @@ Blockly.Blocks['event_broadcast_menu'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes":[Blockly.BROADCAST_MESSAGE_TYPE], + "variableTypes":[Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], @@ -177,7 +177,7 @@ Blockly.Blocks['event_broadcast'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes": [Blockly.BROADCAST_MESSAGE_TYPE], + "variableTypes": [Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], @@ -199,7 +199,7 @@ Blockly.Blocks['event_broadcastandwait'] = { { "type": "field_variable", "name": "BROADCAST_OPTION", - "variableTypes": [Blockly.BROADCAST_MESSAGE_TYPE], + "variableTypes": [Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME } ], diff --git a/core/constants.js b/core/constants.js index 3b9bbfbb14..90eb777822 100644 --- a/core/constants.js +++ b/core/constants.js @@ -342,7 +342,15 @@ Blockly.NEW_BROADCAST_MESSAGE_ID = 'NEW_BROADCAST_MESSAGE_ID'; * indicates that the current variable is a broadcast message. * @const {string} */ -Blockly.BROADCAST_MESSAGE_TYPE = 'broadcast_msg'; +Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE = 'broadcast_msg'; + +/** + * String representing the variable type of list blocks. + * This string, for use in differentiating between types of variables, + * indicates that the current variable is a list. + * @const {string} + */ +Blockly.LIST_VARIABLE_TYPE = 'list'; /** * The type of all procedure definition blocks. diff --git a/core/data_category.js b/core/data_category.js index af50fb146e..7fb791c23c 100644 --- a/core/data_category.js +++ b/core/data_category.js @@ -63,7 +63,7 @@ Blockly.DataCategory = function(workspace) { // Now add list variables to the flyout Blockly.DataCategory.addCreateButton(xmlList, workspace, 'LIST'); - variableModelList = workspace.getVariablesOfType('list'); + variableModelList = workspace.getVariablesOfType(Blockly.LIST_VARIABLE_TYPE); variableModelList.sort(Blockly.VariableModel.compareByName); for (var i = 0; i < variableModelList.length; i++) { Blockly.DataCategory.addDataList(xmlList, variableModelList[i]); @@ -359,7 +359,7 @@ Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) { callbackKey = 'CREATE_LIST'; callback = function(button) { Blockly.Variables.createVariable(button.getTargetWorkspace(), null, - 'list');}; + Blockly.LIST_VARIABLE_TYPE);}; } button.setAttribute('text', msg); button.setAttribute('callbackKey', callbackKey); diff --git a/core/field_variable.js b/core/field_variable.js index 894a7338d2..4942a030e7 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -190,7 +190,7 @@ Blockly.FieldVariable.dropdownCreate = function() { // doesn't modify the workspace's list. for (var i = 0; i < variableTypes.length; i++) { var variableType = variableTypes[i]; - if (variableType == Blockly.BROADCAST_MESSAGE_TYPE){ + if (variableType == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE){ isBroadcastType = true; } var variables = workspace.getVariablesOfType(variableType); @@ -261,7 +261,7 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) { var setName = function(newName) { thisField.setValue(newName); }; - Blockly.Variables.createVariable(workspace, setName, Blockly.BROADCAST_MESSAGE_TYPE); + Blockly.Variables.createVariable(workspace, setName, Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE); return; } diff --git a/core/variables.js b/core/variables.js index 0ec8a91db3..a96fbd6ad1 100644 --- a/core/variables.js +++ b/core/variables.js @@ -176,9 +176,9 @@ Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) { // Decide on a modal message based on the opt_type. If opt_type was not // provided, default to the original message for scalar variables. var newMsg = ''; - if (opt_type === 'list') { + if (opt_type === Blockly.LIST_VARIABLE_TYPE) { newMsg = Blockly.Msg.NEW_LIST_TITLE; - } else if (opt_type === 'broadcast_msg') { + } else if (opt_type === Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) { newMsg = Blockly.Msg.NEW_BROADCAST_MESSAGE_TITLE; } else { newMsg = Blockly.Msg.NEW_VARIABLE_TITLE; From 944ac2ce3e3b63d54b3cbd33cc1704b60b77a114 Mon Sep 17 00:00:00 2001 From: Florrie Date: Tue, 21 Nov 2017 18:03:45 -0400 Subject: [PATCH 0251/2135] Make sortProcedureMutations private --- core/procedures.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index 24ef03db28..0ca602a492 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -97,8 +97,9 @@ Blockly.Procedures.allProcedureMutations = function(root) { * (Does not mutate the given array.) * @param {!Array.} mutations Array of mutation xml elements. * @return {!Array.} Sorted array of mutation xml elements. + * @Private */ -Blockly.Procedures.sortProcedureMutations = function(mutations) { +Blockly.Procedures.sortProcedureMutations_ = function(mutations) { var newMutations = mutations.slice(); newMutations.sort(function(a, b) { @@ -228,7 +229,7 @@ Blockly.Procedures.flyoutCategory = function(workspace) { // Create call blocks for each procedure defined in the workspace var mutations = Blockly.Procedures.allProcedureMutations(workspace); - mutations = Blockly.Procedures.sortProcedureMutations(mutations); + mutations = Blockly.Procedures.sortProcedureMutations_(mutations); for (var i = 0; i < mutations.length; i++) { var mutation = mutations[i]; // From abcb8388a550ce61749fadff4a744b4a40e383fc Mon Sep 17 00:00:00 2001 From: Florrie Date: Tue, 21 Nov 2017 18:06:02 -0400 Subject: [PATCH 0252/2135] Change '@Private' to '@private' in jsdoc for sortProcedureMutations --- core/procedures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/procedures.js b/core/procedures.js index 0ca602a492..19d1da58d4 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -97,7 +97,7 @@ Blockly.Procedures.allProcedureMutations = function(root) { * (Does not mutate the given array.) * @param {!Array.} mutations Array of mutation xml elements. * @return {!Array.} Sorted array of mutation xml elements. - * @Private + * @private */ Blockly.Procedures.sortProcedureMutations_ = function(mutations) { var newMutations = mutations.slice(); From 37341daca3ab6378e8b87d5ef6732f4cfef48555 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 22 Nov 2017 14:29:12 -0500 Subject: [PATCH 0253/2135] Add logic for edit/create custom procedures, wire up the playground --- core/procedures.js | 119 ++++++++++++++++++++++--- tests/custom_procedure_playground.html | 89 ++++++++++-------- 2 files changed, 161 insertions(+), 47 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index aa4a5e69e7..8d64662713 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -228,7 +228,7 @@ Blockly.Procedures.addCreateButton_ = function(workspace, xmlList) { var msg = Blockly.Msg.NEW_PROCEDURE; var callbackKey = 'CREATE_PROCEDURE'; var callback = function() { - Blockly.Procedures.createProcedureDefCallback_(); + Blockly.Procedures.createProcedureDefCallback_(workspace); }; button.setAttribute('text', msg); button.setAttribute('callbackKey', callbackKey); @@ -306,17 +306,36 @@ Blockly.Procedures.mutateCallers = function(defBlock) { /** * Find the definition block for the named procedure. - * @param {string} name Name of procedure. + * @param {string} procCode The identifier of the procedure to delete. * @param {!Blockly.Workspace} workspace The workspace to search. * @return {Blockly.Block} The procedure definition block, or null not found. */ -Blockly.Procedures.getDefinition = function(name, workspace) { +Blockly.Procedures.getDefineBlock = function(procCode, workspace) { // 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(); - if (tuple && Blockly.Names.equals(tuple[0], name)) { + var input = blocks[i].getInput('custom_block'); + if (input && input.connection) { + var prototypeBlock = input.connection.targetBlock(); + if (prototypeBlock.getProcCode && prototypeBlock.getProcCode() === procCode) { + return blocks[i]; + } + } + } + return null; +}; + +/** + * Find the prototype block for the named procedure. + * @param {string} procCode The identifier of the procedure to delete. + * @param {!Blockly.Workspace} workspace The workspace to search. + * @return {Blockly.Block} The procedure prototype block, or null not found. + */ +Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { + var blocks = workspace.getAllBlocks(); + for (var i = 0; i < blocks.length; i++) { + if (blocks[i].type === Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { + if (blocks[i].getProcCode && blocks[i].getProcCode() == procCode) { return blocks[i]; } } @@ -324,13 +343,50 @@ Blockly.Procedures.getDefinition = function(name, workspace) { return null; }; +/** + * Create a mutation for a brand new custom procedure. + * @return {Element} The mutation for a new custom procedure + */ +Blockly.Procedures.newProcedureMutation = function() { + // TODO Is this the "default new block" we want? Scratch 2 has a blank label. + var mutation = goog.dom.createDom('mutation'); + mutation.setAttribute('proccode', 'block name'); + mutation.setAttribute('argumentids', JSON.stringify([])); + mutation.setAttribute('argumentnames', JSON.stringify([])); + mutation.setAttribute('argumentdefaults', JSON.stringify([])); + mutation.setAttribute('warp', false); + return mutation; +}; + /** * Callback to create a new procedure custom command block. - * TODO(#1299): Implement. + * @param {!Blockly.Workspace} workspace The workspace to create the new procedure on. * @private */ -Blockly.Procedures.createProcedureDefCallback_ = function() { - alert('TODO(#1299) Implement procedure creation callback.'); +Blockly.Procedures.createProcedureDefCallback_ = function(workspace) { + Blockly.Procedures.externalProcedureDefCallback_( + Blockly.Procedures.newProcedureMutation(), + function(newMutation) { + if (newMutation) { + + // TODO Is this the best way to assemble the new procedure definition? + var definitionBlock = workspace.newBlock('procedures_definition'); + var prototypeBlock = workspace.newBlock('procedures_prototype'); + prototypeBlock.setShadow(true); + definitionBlock.getInput('custom_block') + .connection.connect(prototypeBlock.previousConnection); + definitionBlock.initSvg(); + definitionBlock.render(); + prototypeBlock.domToMutation(newMutation); + prototypeBlock.initSvg(); + prototypeBlock.render(); + + // TODO how to position the new block in the right place? + definitionBlock.moveBy(30, 30); + definitionBlock.scheduleSnapAndBump(); + } + } + ); }; /** @@ -340,6 +396,8 @@ Blockly.Procedures.createProcedureDefCallback_ = function() { * @private */ Blockly.Procedures.editProcedureCallback_ = function(block) { + // Edit can come from one of three block types (call, define, prototype) + // Normalize by setting the block to the prototype block for the procedure. if (block.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { var input = block.getInput('custom_block'); if (!input) { @@ -358,9 +416,48 @@ Blockly.Procedures.editProcedureCallback_ = function(block) { return; } block = innerBlock; + } else if (!block.type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE){ + // This is a call block, find the prototype corresponding to the procCode + block = Blockly.Procedures.getPrototypeBlock( + block.getProcCode(), block.workspace); } - alert('TODO(#603): implement function editing (procCode was "' + - block.procCode_ + '")'); + + // Block now refers to the procedure prototype block, it is safe to proceed. + Blockly.Procedures.externalProcedureDefCallback_( + block.mutationToDom(), + function(newMutation) { + if (newMutation) { + // Update all the callers + var defineBlock = Blockly.Procedures.getDefineBlock(block.getProcCode(), + block.workspace); + if (defineBlock) { + var callers = Blockly.Procedures.getCallers(block.getProcCode(), + defineBlock.workspace, defineBlock, true /* allowRecursive */); + for (var i = 0, caller; caller = callers[i]; i++) { + var oldMutationDom = caller.mutationToDom(); + var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); + caller.domToMutation(newMutation); + if (oldMutation != newMutation) { + Blockly.Events.fire(new Blockly.Events.BlockChange( + caller, 'mutation', null, oldMutation, newMutation)); + } + } + } else { + alert('No define block on workspace'); // TODO decide what to do about this. + } + // And update the prototype block + block.domToMutation(newMutation); + } + } + ); +}; + +/** + * Callback to create a new procedure custom command block. + * @private + */ +Blockly.Procedures.externalProcedureDefCallback_ = function(/** mutator, callback */) { + alert('External procedure editor must be override Blockly.Procedures.externalProcedureDefCallback_'); }; /** diff --git a/tests/custom_procedure_playground.html b/tests/custom_procedure_playground.html index 835cfe73b4..3b4afdca96 100644 --- a/tests/custom_procedure_playground.html +++ b/tests/custom_procedure_playground.html @@ -32,40 +32,29 @@ - -

This page has two workspaces. The workspace on the left (primaryWorkspace) will be the custom procedure editing workspace, which will hold a single non-deletable block. The workspace on the right will hold the resulting procedure definition and call blocks.

- - - + -
-
+
-
+
+ - +
- - + From 9ff1aabca39c6ff29deb814e144c26f9d6d099f1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 27 Nov 2017 14:57:46 -0500 Subject: [PATCH 0254/2135] Change argument default values and automatically focus on new inputs --- blocks_vertical/procedures.js | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 2eb5fafbbe..a9b1d95e37 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -279,11 +279,11 @@ Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ = function(type) { if (type == 'n') { var shadowType = 'math_number'; var fieldName = 'NUM'; - var fieldValue = '10'; + var fieldValue = '1'; } else { var shadowType = 'text'; var fieldName = 'TEXT'; - var fieldValue = 'hello world'; + var fieldValue = ''; } shadowDom.setAttribute('type', shadowType); var fieldDom = goog.dom.createDom('field', null, fieldValue); @@ -306,9 +306,9 @@ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, var blockType = argumentType == 'n' ? 'math_number' : 'text'; var newBlock = this.workspace.newBlock(blockType); if (argumentType == 'n') { - newBlock.setFieldValue('99', 'NUM'); + newBlock.setFieldValue('1', 'NUM'); } else { - newBlock.setFieldValue('hello world', 'TEXT'); + newBlock.setFieldValue('', 'TEXT'); } newBlock.setShadow(true); if (!this.isInsertionMarker()) { @@ -530,13 +530,31 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { } }; +/** + * Focus on the final input of the block + */ +Blockly.ScratchBlocks.ProcedureUtils.focusLastInput_ = function() { + if (this.inputList.length > 0) { + var newInput = this.inputList[this.inputList.length - 1]; + if (newInput.type == Blockly.DUMMY_INPUT) { + newInput.fieldRow[0].showEditor_(); + } else if (newInput.type == Blockly.INPUT_VALUE) { + // Inspect the argument editor. + var target = newInput.connection.targetBlock(); + target.getField('TEXT').showEditor_(); + } + } +}; + /** * Externally-visible function to add a label to the procedure declaration. * @public */ Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { + Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' label text'; this.updateDisplay_(); + this.focusLastInput_(); }; /** @@ -545,11 +563,13 @@ Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { * @public */ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { + Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' %b'; this.displayNames_.push('boolean'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); this.updateDisplay_(); + this.focusLastInput_(); }; /** @@ -558,11 +578,13 @@ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { * @public */ Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { + Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' %s'; this.displayNames_.push('string or number'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); this.updateDisplay_(); + this.focusLastInput_(); }; Blockly.Blocks['procedures_definition'] = { @@ -684,6 +706,7 @@ Blockly.Blocks['procedures_declaration'] = { // Only exist on procedures_declaration. createArgumentEditor_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_, + focusLastInput_: Blockly.ScratchBlocks.ProcedureUtils.focusLastInput_, addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, @@ -755,4 +778,3 @@ Blockly.Blocks['argument_editor_string_number'] = { }); } }; - From a5db373f0b79983c5e6b1dce64e6c2730e5d00e4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 27 Nov 2017 15:13:24 -0500 Subject: [PATCH 0255/2135] Add @private jsdoc to helper --- blocks_vertical/procedures.js | 1 + 1 file changed, 1 insertion(+) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index a9b1d95e37..78e5b3c383 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -532,6 +532,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { /** * Focus on the final input of the block + * @private */ Blockly.ScratchBlocks.ProcedureUtils.focusLastInput_ = function() { if (this.inputList.length > 0) { From 5036677a78a9f0c5b6b4d397d1c11f6053a1a4b7 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 27 Nov 2017 15:15:11 -0500 Subject: [PATCH 0256/2135] Fix wording in focusLastInput_ jsdoc --- blocks_vertical/procedures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 78e5b3c383..f08bce2866 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -531,7 +531,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { }; /** - * Focus on the final input of the block + * Focus on the last argument editor or label editor on the block. * @private */ Blockly.ScratchBlocks.ProcedureUtils.focusLastInput_ = function() { From 75f20c5705f060e8cba6dce957c0d60c2cfc3381 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 27 Nov 2017 15:56:07 -0500 Subject: [PATCH 0257/2135] Change wording to lastEditor --- blocks_vertical/procedures.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index f08bce2866..494cb7ae87 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -534,7 +534,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { * Focus on the last argument editor or label editor on the block. * @private */ -Blockly.ScratchBlocks.ProcedureUtils.focusLastInput_ = function() { +Blockly.ScratchBlocks.ProcedureUtils.focusLastEditor_ = function() { if (this.inputList.length > 0) { var newInput = this.inputList[this.inputList.length - 1]; if (newInput.type == Blockly.DUMMY_INPUT) { @@ -555,7 +555,7 @@ Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' label text'; this.updateDisplay_(); - this.focusLastInput_(); + this.focusLastEditor_(); }; /** @@ -570,7 +570,7 @@ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); this.updateDisplay_(); - this.focusLastInput_(); + this.focusLastEditor_(); }; /** @@ -585,7 +585,7 @@ Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('todo'); this.updateDisplay_(); - this.focusLastInput_(); + this.focusLastEditor_(); }; Blockly.Blocks['procedures_definition'] = { @@ -707,7 +707,7 @@ Blockly.Blocks['procedures_declaration'] = { // Only exist on procedures_declaration. createArgumentEditor_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_, - focusLastInput_: Blockly.ScratchBlocks.ProcedureUtils.focusLastInput_, + focusLastEditor_: Blockly.ScratchBlocks.ProcedureUtils.focusLastEditor_, addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, From 9763a2d5f184f7b65cc14328b46ce16377e937c5 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 22 Nov 2017 12:56:31 -0800 Subject: [PATCH 0258/2135] Merge pull request #1477 from kchadha/varcreate-bugfix VarCreate bugfix --- core/flyout_base.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/core/flyout_base.js b/core/flyout_base.js index 6dcbc3380a..3ff671c1be 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -684,6 +684,33 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { } }; +/** + * Helper function to get the list of variables that have been added to the + * workspace after adding a new block, using the given list of variables that + * were in the workspace before the new block was added. + * @param {!Array.} originalVariables The array of + * variables that existed in the workspace before adding the new block. + * @return {!Array.} The new array of variables that were + * freshly added to the workspace after creating the new block, or [] if no + * new variables were added to the workspace. + * @private + */ +Blockly.Flyout.prototype.getAddedVariables_ = function(originalVariables) { + var allCurrentVariables = this.targetWorkspace_.getAllVariables(); + var addedVariables = []; + if (originalVariables.length != allCurrentVariables.length) { + for (var i = 0; i < allCurrentVariables.length; i++) { + var variable = allCurrentVariables[i]; + // For any variable that is present in allCurrentVariables but not + // present in originalVariables, add the variable to addedVariables. + if (!originalVariables.includes(variable)) { + addedVariables.push(variable); + } + } + } + return addedVariables; +}; + /** * Create a copy of this block on the workspace. * @param {!Blockly.BlockSvg} originalBlock The block to copy from the flyout. @@ -694,6 +721,7 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { Blockly.Flyout.prototype.createBlock = function(originalBlock) { var newBlock = null; Blockly.Events.disable(); + var variablesBeforeCreation = this.targetWorkspace_.getAllVariables(); this.targetWorkspace_.setResizesEnabled(false); try { newBlock = this.placeNewBlock_(originalBlock); @@ -703,9 +731,16 @@ Blockly.Flyout.prototype.createBlock = function(originalBlock) { Blockly.Events.enable(); } + var newVariables = this.getAddedVariables_(variablesBeforeCreation); + if (Blockly.Events.isEnabled()) { Blockly.Events.setGroup(true); Blockly.Events.fire(new Blockly.Events.Create(newBlock)); + // Fire a VarCreate event for each (if any) new variable created. + for(var i = 0; i < newVariables.length; i++) { + var thisVariable = newVariables[i]; + Blockly.Events.fire(new Blockly.Events.VarCreate(thisVariable)); + } } if (this.autoClose) { this.hide(); From 69e5f0e5ecf488b47ac4c7ad36860aff81242456 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 28 Nov 2017 10:28:35 -0500 Subject: [PATCH 0259/2135] Cancel workspace gestures on toolbox mousedown. Fixes the problem where dragging a duplicated block onto the category menu didn't properly delete it. --- core/toolbox.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/toolbox.js b/core/toolbox.js index 2196838fea..87c1592d10 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -117,6 +117,8 @@ Blockly.Toolbox.prototype.init = function() { // Clicking on toolbox closes popups. Blockly.bindEventWithChecks_(this.HtmlDiv, 'mousedown', this, function(e) { + // Cancel any gestures in progress. + this.workspace_.cancelCurrentGesture(); if (Blockly.utils.isRightButton(e) || e.target == this.HtmlDiv) { // Close flyout. Blockly.hideChaff(false); From ed745b717aba79b800854cf43e694ac7e1db3e69 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 28 Nov 2017 13:22:04 -0500 Subject: [PATCH 0260/2135] Move callbacks into callback factory. --- core/procedures.js | 108 ++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index 8d64662713..3e5fea1094 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -82,7 +82,7 @@ Blockly.Procedures.allProcedureMutations = function(root) { var blocks = root.getAllBlocks(); var mutations = []; for (var i = 0; i < blocks.length; i++) { - if (blocks[i].type === 'procedures_prototype') { + if (blocks[i].type == 'procedures_prototype') { var mutation = blocks[i].mutationToDom(); if (mutation) { mutations.push(mutation); @@ -317,7 +317,7 @@ Blockly.Procedures.getDefineBlock = function(procCode, workspace) { var input = blocks[i].getInput('custom_block'); if (input && input.connection) { var prototypeBlock = input.connection.targetBlock(); - if (prototypeBlock.getProcCode && prototypeBlock.getProcCode() === procCode) { + if (prototypeBlock.getProcCode && prototypeBlock.getProcCode() == procCode) { return blocks[i]; } } @@ -334,7 +334,7 @@ Blockly.Procedures.getDefineBlock = function(procCode, workspace) { Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { var blocks = workspace.getAllBlocks(); for (var i = 0; i < blocks.length; i++) { - if (blocks[i].type === Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { + if (blocks[i].type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { if (blocks[i].getProcCode && blocks[i].getProcCode() == procCode) { return blocks[i]; } @@ -366,32 +366,40 @@ Blockly.Procedures.newProcedureMutation = function() { Blockly.Procedures.createProcedureDefCallback_ = function(workspace) { Blockly.Procedures.externalProcedureDefCallback_( Blockly.Procedures.newProcedureMutation(), - function(newMutation) { - if (newMutation) { - - // TODO Is this the best way to assemble the new procedure definition? - var definitionBlock = workspace.newBlock('procedures_definition'); - var prototypeBlock = workspace.newBlock('procedures_prototype'); - prototypeBlock.setShadow(true); - definitionBlock.getInput('custom_block') - .connection.connect(prototypeBlock.previousConnection); - definitionBlock.initSvg(); - definitionBlock.render(); - prototypeBlock.domToMutation(newMutation); - prototypeBlock.initSvg(); - prototypeBlock.render(); - - // TODO how to position the new block in the right place? - definitionBlock.moveBy(30, 30); - definitionBlock.scheduleSnapAndBump(); - } - } + Blockly.Procedures.createProcedureCallbackFactory_(workspace) ); }; +/** + * Callback factory for adding a new custom procedure from a mutation. + * @param {!Blockly.Workspace} workspace The workspace to create the new procedure on. + * @return {function(?Element)} callback for creating the new custom procedure. + * @private + */ +Blockly.Procedures.createProcedureCallbackFactory_ = function(workspace) { + return function(mutation) { + if (mutation) { + // Create new procedure definition and prototype blocks with the mutation. + var definitionBlock = workspace.newBlock('procedures_definition'); + var prototypeBlock = workspace.newBlock('procedures_prototype'); + prototypeBlock.setShadow(true); + definitionBlock.getInput('custom_block') + .connection.connect(prototypeBlock.previousConnection); + definitionBlock.initSvg(); + definitionBlock.render(); + prototypeBlock.domToMutation(mutation); + prototypeBlock.initSvg(); + prototypeBlock.render(); + + // Position the new block on the workspace. + definitionBlock.moveBy(30, 30); + definitionBlock.scheduleSnapAndBump(); + } + }; +}; + /** * Callback to open the modal for editing custom procedures. - * TODO(#603): Implement. * @param {!Blockly.Block} block The block that was right-clicked. * @private */ @@ -425,31 +433,41 @@ Blockly.Procedures.editProcedureCallback_ = function(block) { // Block now refers to the procedure prototype block, it is safe to proceed. Blockly.Procedures.externalProcedureDefCallback_( block.mutationToDom(), - function(newMutation) { - if (newMutation) { - // Update all the callers - var defineBlock = Blockly.Procedures.getDefineBlock(block.getProcCode(), - block.workspace); - if (defineBlock) { - var callers = Blockly.Procedures.getCallers(block.getProcCode(), - defineBlock.workspace, defineBlock, true /* allowRecursive */); - for (var i = 0, caller; caller = callers[i]; i++) { - var oldMutationDom = caller.mutationToDom(); - var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); - caller.domToMutation(newMutation); - if (oldMutation != newMutation) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - caller, 'mutation', null, oldMutation, newMutation)); - } + Blockly.Procedures.editProcedureCallbackFactory_(block) + ); +}; + +/** + * Callback factory for editing an existing custom procedure. + * @param {!Blockly.Block} block The procedure prototype block being edited. + * @return {function(?Element)} Callback for editing the custom procedure. + * @private + */ +Blockly.Procedures.editProcedureCallbackFactory_ = function(block) { + return function(mutation) { + if (mutation) { + // Update all the callers + var defineBlock = Blockly.Procedures.getDefineBlock(block.getProcCode(), + block.workspace); + if (defineBlock) { + var callers = Blockly.Procedures.getCallers(block.getProcCode(), + defineBlock.workspace, defineBlock, true /* allowRecursive */); + for (var i = 0, caller; caller = callers[i]; i++) { + var oldMutationDom = caller.mutationToDom(); + var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); + caller.domToMutation(mutation); + if (oldMutation != mutation) { + Blockly.Events.fire(new Blockly.Events.BlockChange( + caller, 'mutation', null, oldMutation, mutation)); } - } else { - alert('No define block on workspace'); // TODO decide what to do about this. } - // And update the prototype block - block.domToMutation(newMutation); + } else { + alert('No define block on workspace'); // TODO decide what to do about this. } + // And update the prototype block + block.domToMutation(mutation); } - ); + }; }; /** From 5b5704161ae2f4a17d82a69a20bf1af15e930df4 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 28 Nov 2017 13:58:43 -0500 Subject: [PATCH 0261/2135] Simplify block creation and definition and prototype lookups --- core/procedures.js | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index 3e5fea1094..27db815cdb 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -314,9 +314,8 @@ Blockly.Procedures.getDefineBlock = function(procCode, workspace) { // Assume that a procedure definition is a top block. var blocks = workspace.getTopBlocks(false); for (var i = 0; i < blocks.length; i++) { - var input = blocks[i].getInput('custom_block'); - if (input && input.connection) { - var prototypeBlock = input.connection.targetBlock(); + if (blocks[i].type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { + var prototypeBlock = blocks[i].getInput('custom_block').connection.targetBlock(); if (prototypeBlock.getProcCode && prototypeBlock.getProcCode() == procCode) { return blocks[i]; } @@ -332,13 +331,9 @@ Blockly.Procedures.getDefineBlock = function(procCode, workspace) { * @return {Blockly.Block} The procedure prototype block, or null not found. */ Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { - var blocks = workspace.getAllBlocks(); - for (var i = 0; i < blocks.length; i++) { - if (blocks[i].type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { - if (blocks[i].getProcCode && blocks[i].getProcCode() == procCode) { - return blocks[i]; - } - } + var defineBlock = Blockly.Procedures.getDefineBlock(procCode, workspace); + if (defineBlock) { + return defineBlock.getInput('custom_block').connection.targetBlock(); } return null; }; @@ -348,7 +343,6 @@ Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { * @return {Element} The mutation for a new custom procedure */ Blockly.Procedures.newProcedureMutation = function() { - // TODO Is this the "default new block" we want? Scratch 2 has a blank label. var mutation = goog.dom.createDom('mutation'); mutation.setAttribute('proccode', 'block name'); mutation.setAttribute('argumentids', JSON.stringify([])); @@ -379,21 +373,20 @@ Blockly.Procedures.createProcedureDefCallback_ = function(workspace) { Blockly.Procedures.createProcedureCallbackFactory_ = function(workspace) { return function(mutation) { if (mutation) { - // Create new procedure definition and prototype blocks with the mutation. - var definitionBlock = workspace.newBlock('procedures_definition'); - var prototypeBlock = workspace.newBlock('procedures_prototype'); - prototypeBlock.setShadow(true); - definitionBlock.getInput('custom_block') - .connection.connect(prototypeBlock.previousConnection); - definitionBlock.initSvg(); - definitionBlock.render(); - prototypeBlock.domToMutation(mutation); - prototypeBlock.initSvg(); - prototypeBlock.render(); - - // Position the new block on the workspace. - definitionBlock.moveBy(30, 30); - definitionBlock.scheduleSnapAndBump(); + var blockText = '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + var blockDom = Blockly.Xml.textToDom(blockText).firstChild; + var block = Blockly.Xml.domToBlock(blockDom, workspace); + block.getInput('custom_block').connection.targetBlock() + .domToMutation(mutation); + block.moveBy(30, 30); + block.scheduleSnapAndBump(); } }; }; From a6fc0b36d5f2ee49f926262755669aff92da79f6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 09:04:24 -0500 Subject: [PATCH 0262/2135] Update mutate callers. Included updating the prototype block there so that the undos could all be grouped together. --- core/procedures.js | 78 ++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index 27db815cdb..bc53495b25 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -276,31 +276,33 @@ Blockly.Procedures.getCallers = function(name, ws, definitionRoot, }; /** - * When a procedure definition changes its parameters, find and edit all its - * callers. - * @param {!Blockly.Block} defBlock Procedure definition block. + * Find and edit all callers with a procCode using a new mutation. + * @param {string} name Name of procedure (procCode in scratch-blocks). + * @param {!Blockly.Workspace} ws The workspace to find callers in. + * @param {!Element} mutation New mutation for the callers. */ -Blockly.Procedures.mutateCallers = function(defBlock) { - // TODO(#1143) Update this for scratch procedures. - var oldRecordUndo = Blockly.Events.recordUndo; - var name = defBlock.getProcedureDef()[0]; - var xmlElement = defBlock.mutationToDom(true); - var callers = Blockly.Procedures.getCallers(name, defBlock.workspace); - for (var i = 0, caller; caller = callers[i]; i++) { - var oldMutationDom = caller.mutationToDom(); - var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); - caller.domToMutation(xmlElement); - var newMutationDom = caller.mutationToDom(); - var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom); - if (oldMutation != newMutation) { - // Fire a mutation on every caller block. But don't record this as an - // undo action since it is deterministically tied to the procedure's - // definition mutation. - Blockly.Events.recordUndo = false; - Blockly.Events.fire(new Blockly.Events.BlockChange( - caller, 'mutation', null, oldMutation, newMutation)); - Blockly.Events.recordUndo = oldRecordUndo; +Blockly.Procedures.mutateCallersAndPrototype = function(name, ws, mutation) { + var defineBlock = Blockly.Procedures.getDefineBlock(name, ws); + var prototypeBlock = Blockly.Procedures.getPrototypeBlock(name, ws); + if (defineBlock && prototypeBlock) { + var callers = Blockly.Procedures.getCallers(name, + defineBlock.workspace, defineBlock, true /* allowRecursive */); + callers.push(prototypeBlock); + Blockly.Events.setGroup(true); + for (var i = 0, caller; caller = callers[i]; i++) { + var oldMutationDom = caller.mutationToDom(); + var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); + caller.domToMutation(mutation); + var newMutationDom = caller.mutationToDom(); + var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom); + if (oldMutation != newMutation) { + Blockly.Events.fire(new Blockly.Events.BlockChange( + caller, 'mutation', null, oldMutation, newMutation)); + } } + Blockly.Events.setGroup(false); + } else { + alert('No define block on workspace'); // TODO decide what to do about this. } }; @@ -377,16 +379,17 @@ Blockly.Procedures.createProcedureCallbackFactory_ = function(workspace) { '' + '' + '' + + Blockly.Xml.domToText(mutation) + '' + '' + '' + ''; var blockDom = Blockly.Xml.textToDom(blockText).firstChild; + Blockly.Events.setGroup(true); var block = Blockly.Xml.domToBlock(blockDom, workspace); - block.getInput('custom_block').connection.targetBlock() - .domToMutation(mutation); block.moveBy(30, 30); block.scheduleSnapAndBump(); + Blockly.Events.setGroup(false); } }; }; @@ -417,12 +420,11 @@ Blockly.Procedures.editProcedureCallback_ = function(block) { return; } block = innerBlock; - } else if (!block.type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE){ + } else if (block.type == Blockly.PROCEDURES_CALL_BLOCK_TYPE) { // This is a call block, find the prototype corresponding to the procCode block = Blockly.Procedures.getPrototypeBlock( block.getProcCode(), block.workspace); } - // Block now refers to the procedure prototype block, it is safe to proceed. Blockly.Procedures.externalProcedureDefCallback_( block.mutationToDom(), @@ -439,26 +441,8 @@ Blockly.Procedures.editProcedureCallback_ = function(block) { Blockly.Procedures.editProcedureCallbackFactory_ = function(block) { return function(mutation) { if (mutation) { - // Update all the callers - var defineBlock = Blockly.Procedures.getDefineBlock(block.getProcCode(), - block.workspace); - if (defineBlock) { - var callers = Blockly.Procedures.getCallers(block.getProcCode(), - defineBlock.workspace, defineBlock, true /* allowRecursive */); - for (var i = 0, caller; caller = callers[i]; i++) { - var oldMutationDom = caller.mutationToDom(); - var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); - caller.domToMutation(mutation); - if (oldMutation != mutation) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - caller, 'mutation', null, oldMutation, mutation)); - } - } - } else { - alert('No define block on workspace'); // TODO decide what to do about this. - } - // And update the prototype block - block.domToMutation(mutation); + Blockly.Procedures.mutateCallersAndPrototype(block.getProcCode(), + block.workspace, mutation); } }; }; From f6006971f70c21443ff1f0a42426eb7f545b07d0 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 09:28:41 -0500 Subject: [PATCH 0263/2135] Update "public" annotation for external procedure def callback. --- core/procedures.js | 10 +++++----- tests/custom_procedure_playground.html | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index bc53495b25..f245949ae2 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -360,7 +360,7 @@ Blockly.Procedures.newProcedureMutation = function() { * @private */ Blockly.Procedures.createProcedureDefCallback_ = function(workspace) { - Blockly.Procedures.externalProcedureDefCallback_( + Blockly.Procedures.externalProcedureDefCallback( Blockly.Procedures.newProcedureMutation(), Blockly.Procedures.createProcedureCallbackFactory_(workspace) ); @@ -426,7 +426,7 @@ Blockly.Procedures.editProcedureCallback_ = function(block) { block.getProcCode(), block.workspace); } // Block now refers to the procedure prototype block, it is safe to proceed. - Blockly.Procedures.externalProcedureDefCallback_( + Blockly.Procedures.externalProcedureDefCallback( block.mutationToDom(), Blockly.Procedures.editProcedureCallbackFactory_(block) ); @@ -449,10 +449,10 @@ Blockly.Procedures.editProcedureCallbackFactory_ = function(block) { /** * Callback to create a new procedure custom command block. - * @private + * @public */ -Blockly.Procedures.externalProcedureDefCallback_ = function(/** mutator, callback */) { - alert('External procedure editor must be override Blockly.Procedures.externalProcedureDefCallback_'); +Blockly.Procedures.externalProcedureDefCallback = function(/** mutator, callback */) { + alert('External procedure editor must be override Blockly.Procedures.externalProcedureDefCallback'); }; /** diff --git a/tests/custom_procedure_playground.html b/tests/custom_procedure_playground.html index 3b4afdca96..12510624f2 100644 --- a/tests/custom_procedure_playground.html +++ b/tests/custom_procedure_playground.html @@ -153,7 +153,7 @@ Blockly.Xml.clearWorkspaceAndLoadFromXml(document.getElementById('main_ws_blocks_simplest'), definitionWorkspace); - Blockly.Procedures.externalProcedureDefCallback_ = function (mutation, cb) { + Blockly.Procedures.externalProcedureDefCallback = function (mutation, cb) { editorActions.style.visibility = 'visible'; callback = cb; declarationWorkspace.clear(); From ca266f63cb0b27a9c31785d0dd164c2a0c4386ca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 09:35:00 -0500 Subject: [PATCH 0264/2135] Fix comments --- core/procedures.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index f245949ae2..d2a4db0041 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -308,7 +308,7 @@ Blockly.Procedures.mutateCallersAndPrototype = function(name, ws, mutation) { /** * Find the definition block for the named procedure. - * @param {string} procCode The identifier of the procedure to delete. + * @param {string} procCode The identifier of the procedure. * @param {!Blockly.Workspace} workspace The workspace to search. * @return {Blockly.Block} The procedure definition block, or null not found. */ @@ -328,7 +328,7 @@ Blockly.Procedures.getDefineBlock = function(procCode, workspace) { /** * Find the prototype block for the named procedure. - * @param {string} procCode The identifier of the procedure to delete. + * @param {string} procCode The identifier of the procedure. * @param {!Blockly.Workspace} workspace The workspace to search. * @return {Blockly.Block} The procedure prototype block, or null not found. */ From 2e40d706c6723d53aec686a8ef97e95c3ace7fba Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 09:35:18 -0500 Subject: [PATCH 0265/2135] Use textToDom for creating default mutation. --- core/procedures.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index d2a4db0041..951b97059d 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -345,13 +345,16 @@ Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { * @return {Element} The mutation for a new custom procedure */ Blockly.Procedures.newProcedureMutation = function() { - var mutation = goog.dom.createDom('mutation'); - mutation.setAttribute('proccode', 'block name'); - mutation.setAttribute('argumentids', JSON.stringify([])); - mutation.setAttribute('argumentnames', JSON.stringify([])); - mutation.setAttribute('argumentdefaults', JSON.stringify([])); - mutation.setAttribute('warp', false); - return mutation; + var mutationText = '' + + '' + + '' + + ''; + return Blockly.Xml.textToDom(mutationText).firstChild; }; /** From 42e3e520f42a250eb1c05ec6289dcf75c992473d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 11:33:13 -0500 Subject: [PATCH 0266/2135] Fix enable without corresponding disable --- core/gesture.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/gesture.js b/core/gesture.js index d85557b3d0..38fefbf01a 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -834,6 +834,7 @@ Blockly.Gesture.prototype.forceStartBlockDrag = function(fakeEvent, block) { */ Blockly.Gesture.prototype.duplicateOnDrag_ = function() { var newBlock = null; + Blockly.Events.disable(); try { // Note: targetBlock_ should have no children. If it has children we would // need to update shadow block IDs to avoid problems in the VM. From b2b7f7d2ffb27597f847fc035ae9513b45ba71cf Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Wed, 29 Nov 2017 11:57:55 -0500 Subject: [PATCH 0267/2135] Changing color of list blocks to differentiate from varaible blocks (according to color chart in issue #600). --- blocks_vertical/data.js | 20 ++++++++++---------- blocks_vertical/vertical_extensions.js | 4 ++-- core/colours.js | 7 +++++++ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/blocks_vertical/data.js b/blocks_vertical/data.js index f5de984092..14c73c93b7 100644 --- a/blocks_vertical/data.js +++ b/blocks_vertical/data.js @@ -166,7 +166,7 @@ Blockly.Blocks['data_listcontents'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "output_string"], + "extensions": ["colours_data_lists", "output_string"], "checkboxInFlyout": true }); } @@ -248,7 +248,7 @@ Blockly.Blocks['data_addtolist'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] + "extensions": ["colours_data_lists", "shape_statement"] }); } }; @@ -273,7 +273,7 @@ Blockly.Blocks['data_deleteoflist'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] + "extensions": ["colours_data_lists", "shape_statement"] }); } }; @@ -302,7 +302,7 @@ Blockly.Blocks['data_insertatlist'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] + "extensions": ["colours_data_lists", "shape_statement"] }); } }; @@ -331,7 +331,7 @@ Blockly.Blocks['data_replaceitemoflist'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] + "extensions": ["colours_data_lists", "shape_statement"] }); } }; @@ -357,7 +357,7 @@ Blockly.Blocks['data_itemoflist'] = { ], "output": null, "category": Blockly.Categories.data, - "extensions": ["colours_data"], + "extensions": ["colours_data_lists"], "outputShape": Blockly.OUTPUT_SHAPE_ROUND }); } @@ -379,7 +379,7 @@ Blockly.Blocks['data_lengthoflist'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "output_number"] + "extensions": ["colours_data_lists", "output_number"] }); } }; @@ -404,7 +404,7 @@ Blockly.Blocks['data_listcontainsitem'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "output_boolean"] + "extensions": ["colours_data_lists", "output_boolean"] }); } }; @@ -425,7 +425,7 @@ Blockly.Blocks['data_showlist'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] + "extensions": ["colours_data_lists", "shape_statement"] }); } }; @@ -446,7 +446,7 @@ Blockly.Blocks['data_hidelist'] = { } ], "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] + "extensions": ["colours_data_lists", "shape_statement"] }); } }; diff --git a/blocks_vertical/vertical_extensions.js b/blocks_vertical/vertical_extensions.js index 4f5ab8c30f..d25a530415 100644 --- a/blocks_vertical/vertical_extensions.js +++ b/blocks_vertical/vertical_extensions.js @@ -217,8 +217,8 @@ Blockly.ScratchBlocks.VerticalExtensions.PROCEDURE_CALL_CONTEXTMENU = { */ Blockly.ScratchBlocks.VerticalExtensions.registerAll = function() { var categoryNames = - ['control', 'data', 'sounds', 'motion', 'looks', 'event', 'sensing', - 'pen', 'operators', 'more']; + ['control', 'data', 'data_lists', 'sounds', 'motion', 'looks', 'event', + 'sensing', 'pen', 'operators', 'more']; // Register functions for all category colours. for (var i = 0; i < categoryNames.length; i++) { name = categoryNames[i]; diff --git a/core/colours.js b/core/colours.js index 6907ca5835..0c0f3ff91f 100644 --- a/core/colours.js +++ b/core/colours.js @@ -70,6 +70,13 @@ Blockly.Colours = { "secondary": "#FF8000", "tertiary": "#DB6E00" }, + // This is not a new category, but rather for differentiation + // between lists and scalar variables. + "data_lists": { + "primary": "#FF661A", + "secondary": "#FF5500", + "tertiary": "#E64D00" + }, "more": { "primary": "#FF6680", "secondary": "#FF4D6A", From f2972d97b1f0ae0e59b36399a6650b06f22dcaca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 15:23:20 -0500 Subject: [PATCH 0268/2135] Use constant for block type --- core/procedures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/procedures.js b/core/procedures.js index 951b97059d..cc8fe5a513 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -82,7 +82,7 @@ Blockly.Procedures.allProcedureMutations = function(root) { var blocks = root.getAllBlocks(); var mutations = []; for (var i = 0; i < blocks.length; i++) { - if (blocks[i].type == 'procedures_prototype') { + if (blocks[i].type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { var mutation = blocks[i].mutationToDom(); if (mutation) { mutations.push(mutation); From 873c57dc6da97a68f096d8d66a06a8273cc5e3ca Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 15:23:31 -0500 Subject: [PATCH 0269/2135] Add jsdoc annotations --- core/procedures.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/procedures.js b/core/procedures.js index cc8fe5a513..c905cc8460 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -77,6 +77,7 @@ Blockly.Procedures.allProcedures = function(root) { * Find all user-created procedure definition mutations in a workspace. * @param {!Blockly.Workspace} root Root workspace. * @return {!Array.} Array of mutation xml elements. + * @package */ Blockly.Procedures.allProcedureMutations = function(root) { var blocks = root.getAllBlocks(); @@ -280,6 +281,7 @@ Blockly.Procedures.getCallers = function(name, ws, definitionRoot, * @param {string} name Name of procedure (procCode in scratch-blocks). * @param {!Blockly.Workspace} ws The workspace to find callers in. * @param {!Element} mutation New mutation for the callers. + * @package */ Blockly.Procedures.mutateCallersAndPrototype = function(name, ws, mutation) { var defineBlock = Blockly.Procedures.getDefineBlock(name, ws); @@ -311,6 +313,7 @@ Blockly.Procedures.mutateCallersAndPrototype = function(name, ws, mutation) { * @param {string} procCode The identifier of the procedure. * @param {!Blockly.Workspace} workspace The workspace to search. * @return {Blockly.Block} The procedure definition block, or null not found. + * @package */ Blockly.Procedures.getDefineBlock = function(procCode, workspace) { // Assume that a procedure definition is a top block. @@ -331,6 +334,7 @@ Blockly.Procedures.getDefineBlock = function(procCode, workspace) { * @param {string} procCode The identifier of the procedure. * @param {!Blockly.Workspace} workspace The workspace to search. * @return {Blockly.Block} The procedure prototype block, or null not found. + * @package */ Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { var defineBlock = Blockly.Procedures.getDefineBlock(procCode, workspace); @@ -343,6 +347,7 @@ Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { /** * Create a mutation for a brand new custom procedure. * @return {Element} The mutation for a new custom procedure + * @package */ Blockly.Procedures.newProcedureMutation = function() { var mutationText = '' + From 04f409167d070f540be42d60cb0e7944796a8d05 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 29 Nov 2017 15:27:49 -0500 Subject: [PATCH 0270/2135] Fix indentation --- core/procedures.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/procedures.js b/core/procedures.js index c905cc8460..00b1939c12 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -369,8 +369,8 @@ Blockly.Procedures.newProcedureMutation = function() { */ Blockly.Procedures.createProcedureDefCallback_ = function(workspace) { Blockly.Procedures.externalProcedureDefCallback( - Blockly.Procedures.newProcedureMutation(), - Blockly.Procedures.createProcedureCallbackFactory_(workspace) + Blockly.Procedures.newProcedureMutation(), + Blockly.Procedures.createProcedureCallbackFactory_(workspace) ); }; @@ -431,12 +431,12 @@ Blockly.Procedures.editProcedureCallback_ = function(block) { } else if (block.type == Blockly.PROCEDURES_CALL_BLOCK_TYPE) { // This is a call block, find the prototype corresponding to the procCode block = Blockly.Procedures.getPrototypeBlock( - block.getProcCode(), block.workspace); + block.getProcCode(), block.workspace); } // Block now refers to the procedure prototype block, it is safe to proceed. Blockly.Procedures.externalProcedureDefCallback( - block.mutationToDom(), - Blockly.Procedures.editProcedureCallbackFactory_(block) + block.mutationToDom(), + Blockly.Procedures.editProcedureCallbackFactory_(block) ); }; @@ -450,7 +450,7 @@ Blockly.Procedures.editProcedureCallbackFactory_ = function(block) { return function(mutation) { if (mutation) { Blockly.Procedures.mutateCallersAndPrototype(block.getProcCode(), - block.workspace, mutation); + block.workspace, mutation); } }; }; From 07904ede5ed56977e133c44116574a6eb0b7a70e Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 30 Nov 2017 11:35:04 -0500 Subject: [PATCH 0271/2135] Fix issue #1271 by checking for workspace before gettings gesture. --- core/block_svg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/block_svg.js b/core/block_svg.js index a45c0a3ed3..8c11f065fd 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -626,7 +626,7 @@ Blockly.BlockSvg.prototype.createTabList_ = function() { * @private */ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) { - var gesture = this.workspace.getGesture(e); + var gesture = this.workspace && this.workspace.getGesture(e); if (gesture) { gesture.handleBlockStart(e, this); } From 9841b8b1de7e8bdb773bfdd253e5c0ec8011f4be Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 30 Nov 2017 11:37:03 -0500 Subject: [PATCH 0272/2135] Wait to emit new block events until positioned and shadows are fixed. This follows the pattern from workspace.paste. --- core/block_svg.js | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 8c11f065fd..44673cec3a 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -670,25 +670,36 @@ Blockly.BlockSvg.prototype.duplicateAndDragCallback_ = function() { // will lead to weird jumps. // Resizing will be enabled when the drag ends. ws.setResizesEnabled(false); - // Using domToBlock instead of domToWorkspace means that the new block - // will be placed at position (0, 0) in main workspace units. - var newBlock = Blockly.Xml.domToBlock(xml, ws); - // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste - Blockly.utils.changeObscuredShadowIds(newBlock); + // Disable events and manually emit events after the block has been + // positioned and has had its shadow IDs fixed (Scratch-specific). + Blockly.Events.disable(); + try { + // Using domToBlock instead of domToWorkspace means that the new block + // will be placed at position (0, 0) in main workspace units. + var newBlock = Blockly.Xml.domToBlock(xml, ws); - var svgRootNew = newBlock.getSvgRoot(); - if (!svgRootNew) { - throw new Error('newBlock is not rendered.'); - } + // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste + Blockly.utils.changeObscuredShadowIds(newBlock); + + var svgRootNew = newBlock.getSvgRoot(); + if (!svgRootNew) { + throw new Error('newBlock is not rendered.'); + } - // The position of the old block in workspace coordinates. - var oldBlockPosWs = oldBlock.getRelativeToSurfaceXY(); + // The position of the old block in workspace coordinates. + var oldBlockPosWs = oldBlock.getRelativeToSurfaceXY(); - // Place the new block as the same position as the old block. - // TODO: Offset by the difference between the mouse position and the upper - // left corner of the block. - newBlock.moveBy(oldBlockPosWs.x, oldBlockPosWs.y); + // Place the new block as the same position as the old block. + // TODO: Offset by the difference between the mouse position and the upper + // left corner of the block. + newBlock.moveBy(oldBlockPosWs.x, oldBlockPosWs.y); + } finally { + Blockly.Events.enable(); + } + if (Blockly.Events.isEnabled()) { + Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); + } // The position of the old block in pixels relative to the main // workspace's origin. From 34476bc96701226f11ad617d4670836829032aa6 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 30 Nov 2017 14:12:28 -0500 Subject: [PATCH 0273/2135] Try fixing travis chrome problem --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bfe37a221f..7902d87d3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ language: node_js node_js: - "4" - "stable" -sudo: false +sudo: required +addons: + chrome: stable cache: directories: - node_modules From 6bb3cdc9e26c88deb63d3fc6a3a8a7388b6f8cc9 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 1 Dec 2017 11:35:51 -0500 Subject: [PATCH 0274/2135] Add extension blocks with icons to playground --- blocks_vertical/default_toolbox.js | 5 ++ blocks_vertical/extensions.js | 92 +++++++++++++++++++++++++++ media/extensions/music-block-icon.svg | 17 +++++ media/extensions/pen-block-icon.svg | 19 ++++++ media/extensions/wedo2-block-icon.svg | 36 +++++++++++ tests/vertical_playground.html | 1 + 6 files changed, 170 insertions(+) create mode 100644 blocks_vertical/extensions.js create mode 100644 media/extensions/music-block-icon.svg create mode 100644 media/extensions/pen-block-icon.svg create mode 100644 media/extensions/wedo2-block-icon.svg diff --git a/blocks_vertical/default_toolbox.js b/blocks_vertical/default_toolbox.js index 3329a76c85..7182b69091 100644 --- a/blocks_vertical/default_toolbox.js +++ b/blocks_vertical/default_toolbox.js @@ -640,4 +640,9 @@ Blockly.Blocks.defaultToolbox = '' + '' + + ''+ + ''+ + ''+ + ''+ + ''+ ''; diff --git a/blocks_vertical/extensions.js b/blocks_vertical/extensions.js new file mode 100644 index 0000000000..3134ee03a7 --- /dev/null +++ b/blocks_vertical/extensions.js @@ -0,0 +1,92 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +goog.provide('Blockly.Blocks.extensions'); + +goog.require('Blockly.Blocks'); +goog.require('Blockly.Colours'); +goog.require('Blockly.constants'); +goog.require('Blockly.ScratchBlocks.VerticalExtensions'); + + +Blockly.Blocks['extension_pen_down'] = { + /** + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": "%1 pen down", + "args0": [ + { + "type": "field_image", + "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/pen-block-icon.svg", + "width": 40, + "height": 40 + } + ], + "category": Blockly.Categories.more, + "extensions": ["colours_more", "shape_statement"] + }); + } +}; + +Blockly.Blocks['extension_music_drum'] = { + /** + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": "%1 play a drum", + "args0": [ + { + "type": "field_image", + "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/music-block-icon.svg", + "width": 40, + "height": 40 + } + ], + "category": Blockly.Categories.more, + "extensions": ["colours_more", "shape_statement"] + }); + } +}; + +Blockly.Blocks['extension_wedo_motor'] = { + /** + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": "%1 turn on a motor", + "args0": [ + { + "type": "field_image", + "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/wedo2-block-icon.svg", + "width": 40, + "height": 40 + } + ], + "category": Blockly.Categories.more, + "extensions": ["colours_more", "shape_statement"] + }); + } +}; diff --git a/media/extensions/music-block-icon.svg b/media/extensions/music-block-icon.svg new file mode 100644 index 0000000000..f13ebe292d --- /dev/null +++ b/media/extensions/music-block-icon.svg @@ -0,0 +1,17 @@ + + + + music-block-icon + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/media/extensions/pen-block-icon.svg b/media/extensions/pen-block-icon.svg new file mode 100644 index 0000000000..148affa331 --- /dev/null +++ b/media/extensions/pen-block-icon.svg @@ -0,0 +1,19 @@ + + + + pen-icon + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/media/extensions/wedo2-block-icon.svg b/media/extensions/wedo2-block-icon.svg new file mode 100644 index 0000000000..51cb7710db --- /dev/null +++ b/media/extensions/wedo2-block-icon.svg @@ -0,0 +1,36 @@ + + + + wedo2-block-icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/vertical_playground.html b/tests/vertical_playground.html index 8647274a06..ebd758bcff 100644 --- a/tests/vertical_playground.html +++ b/tests/vertical_playground.html @@ -22,6 +22,7 @@ + '); // Load fresh Closure Library. document.write(''); + '/../google-closure-library/closure/goog/base.js">'); document.write(''); } """) @@ -197,26 +198,38 @@ def gen_core(self, vertical): search_paths = self.search_paths_horizontal # Define the parameters for the POST request. params = [ - ("compilation_level", "SIMPLE_OPTIMIZATIONS"), - ("use_closure_library", "true"), - ("output_format", "json"), - ("output_info", "compiled_code"), - ("output_info", "warnings"), - ("output_info", "errors"), - ("output_info", "statistics"), + ("compilation_level", "SIMPLE"), + # ("dependency_mode", "LOOSE"), + ("language_in", "ECMASCRIPT_2017"), + ("language_out", "ECMASCRIPT5"), + ("inject_libraries", "false"), + ("define", "goog.DEBUG=false"), + # ("assume_function_wrapper", ""), + # ("charset", "UTF-8"), + # ("output_wrapper", "%output%"), + # ("isolation_mode", "IIFE"), + # ("use_closure_library", "true"), + # ("output_format", "json"), + # ("output_info", "compiled_code"), + # ("output_info", "warnings"), + # ("output_info", "errors"), + # ("output_info", "statistics"), ] # Read in all the source files. filenames = calcdeps.CalculateDependencies(search_paths, [os.path.join("core", "blockly.js")]) filenames.sort() # Deterministic build. + # print str(filenames) + # exit() for filename in filenames: # Filter out the Closure files (the compiler will add them). - if filename.startswith(os.pardir + os.sep): # '../' - continue - f = open(filename) - params.append(("js_code", "".join(f.readlines()))) - f.close() + # if filename.startswith(CLOSURE_ROOT + os.sep): # '../' + # continue + # f = open(filename) + # params.append(("js_code", "".join(f.readlines()))) + params.append(("js_file", filename)) + # f.close() self.do_compile(params, target_filename, filenames, "") @@ -232,24 +245,33 @@ def gen_blocks(self, block_type): filenames = glob.glob(os.path.join("blocks_common", "*.js")) # Define the parameters for the POST request. params = [ - ("compilation_level", "SIMPLE_OPTIMIZATIONS"), - ("output_format", "json"), - ("output_info", "compiled_code"), - ("output_info", "warnings"), - ("output_info", "errors"), - ("output_info", "statistics"), + ("compilation_level", "SIMPLE"), + # ("dependency_mode", "LOOSE"), + # ("process_closure_primitives", "0"), + # ("jscomp_off", "JSC_LATE_PROVIDE_ERROR"), + # ("defines", "goog.DEBUG=false"), + # ("output_format", "json"), + # ("output_info", "compiled_code"), + # ("output_info", "warnings"), + # ("output_info", "errors"), + # ("output_info", "statistics"), + # ("entry_point", "goog:Blockly.Blocks"), + # ("output_manifest", target_filename + ".MF"), + # ("output_module_dependencies", ""), ] # Read in all the source files. # Add Blockly.Blocks to be compatible with the compiler. - params.append(("js_code", "goog.provide('Blockly.Blocks');")) + # params.append(("js_code", "goog.provide('Blockly.Blocks');")) + params.append(("js_file", os.path.join("build", "gen_blocks.js"))) # Add Blockly.Colours for use of centralized colour bank filenames.append(os.path.join("core", "colours.js")) filenames.append(os.path.join("core", "constants.js")) for filename in filenames: - f = open(filename) - params.append(("js_code", "".join(f.readlines()))) - f.close() + # f = open(filename) + # params.append(("js_code", "".join(f.readlines()))) + params.append(("js_file", filename)) + # f.close() # Remove Blockly.Blocks to be compatible with Blockly. remove = "var Blockly={Blocks:{}};" @@ -259,25 +281,29 @@ def gen_generator(self, language): target_filename = language + "_compressed.js" # Define the parameters for the POST request. params = [ - ("compilation_level", "SIMPLE_OPTIMIZATIONS"), - ("output_format", "json"), - ("output_info", "compiled_code"), - ("output_info", "warnings"), - ("output_info", "errors"), - ("output_info", "statistics"), + ("compilation_level", "SIMPLE"), + # ("dependency_mode", "LOOSE"), + # ("defines", "goog.DEBUG=false"), + # ("output_format", "json"), + # ("output_info", "compiled_code"), + # ("output_info", "warnings"), + # ("output_info", "errors"), + # ("output_info", "statistics"), ] # Read in all the source files. # Add Blockly.Generator to be compatible with the compiler. - params.append(("js_code", "goog.provide('Blockly.Generator');")) + # params.append(("js_code", "goog.provide('Blockly.Generator');")) + params.append(("js_file", os.path.join("build", "gen_generator.js"))) filenames = glob.glob( os.path.join("generators", language, "*.js")) filenames.sort() # Deterministic build. filenames.insert(0, os.path.join("generators", language + ".js")) for filename in filenames: - f = open(filename) - params.append(("js_code", "".join(f.readlines()))) - f.close() + # f = open(filename) + # params.append(("js_code", "".join(f.readlines()))) + params.append(("js_file", filename)) + # f.close() filenames.insert(0, "[goog.provide]") # Remove Blockly.Generator to be compatible with Blockly. @@ -286,15 +312,33 @@ def gen_generator(self, language): def do_compile(self, params, target_filename, filenames, remove): # Send the request to Google. - headers = {"Content-type": "application/x-www-form-urlencoded"} - conn = httplib.HTTPSConnection("closure-compiler.appspot.com") - conn.request("POST", "/compile", urllib.urlencode(params), headers) - response = conn.getresponse() - json_str = response.read() - conn.close() + + underWord = re.compile(r'_([a-z])') + # camel = lambda s: underWord.sub(lambda t: t.groups()[0].upper(), s) + camel = lambda s: s + camelParams = [("--" + camel(arg), value) if arg != "js_file" else (value,) for (arg, value) in params] + camelArgs = [item for pair in camelParams for item in pair] + args = [item for group in [["google-closure-compiler"], camelArgs] for item in group if item] + json.dump(args, open(target_filename + ".json", "w")) + json.dump(params, open(target_filename + ".raw.json", "w")) + proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + (stdout, stderr) = proc.communicate() + # proc.wait() + # print "boop" + # exit() + + # headers = {"Content-type": "application/x-www-form-urlencoded"} + # conn = httplib.HTTPSConnection("closure-compiler.appspot.com") + # conn.request("POST", "/compile", urllib.urlencode(params), headers) + # response = conn.getresponse() + # json_str = response.read() + # conn.close() + # json_str = stdout # Parse the JSON response. - json_data = json.loads(json_str) + # json_data = json.loads(json_str) + json_data = dict(compiledCode=stdout) + print target_filename, len(stdout) def file_lookup(name): if not name.startswith("Input_"): @@ -361,22 +405,22 @@ def file_lookup(name): \\*/""") code = re.sub(LICENSE, "", code) - stats = json_data["statistics"] - original_b = stats["originalSize"] - compressed_b = stats["compressedSize"] - if original_b > 0 and compressed_b > 0: - f = open(target_filename, "w") - f.write(code) - f.close() - - original_kb = int(original_b / 1024 + 0.5) - compressed_kb = int(compressed_b / 1024 + 0.5) - ratio = int(float(compressed_b) / float(original_b) * 100 + 0.5) - print("SUCCESS: " + target_filename) - print("Size changed from %d KB to %d KB (%d%%)." % ( - original_kb, compressed_kb, ratio)) - else: - print("UNKNOWN ERROR") + # stats = json_data["statistics"] + # original_b = stats["originalSize"] + # compressed_b = stats["compressedSize"] + # if original_b > 0 and compressed_b > 0: + f = open(target_filename, "w") + f.write(code) + f.close() + + # original_kb = int(original_b / 1024 + 0.5) + # compressed_kb = int(compressed_b / 1024 + 0.5) + # ratio = int(float(compressed_b) / float(original_b) * 100 + 0.5) + print("SUCCESS: " + target_filename) + # print("Size changed from %d KB to %d KB (%d%%)." % ( + # original_kb, compressed_kb, ratio)) + # else: + # print("UNKNOWN ERROR") class Gen_langfiles(threading.Thread): @@ -465,17 +509,17 @@ def exclude_horizontal(item): if __name__ == "__main__": try: calcdeps = import_path(os.path.join( - os.path.pardir, "closure-library", "closure", "bin", "calcdeps.py")) + CLOSURE_ROOT, "google-closure-library", "closure", "bin", "calcdeps.py")) except ImportError: - if os.path.isdir(os.path.join(os.path.pardir, "closure-library-read-only")): + if os.path.isdir(os.path.join(CLOSURE_ROOT, "closure-library-read-only")): # Dir got renamed when Closure moved from Google Code to GitHub in 2014. print("Error: Closure directory needs to be renamed from" "'closure-library-read-only' to 'closure-library'.\n" "Please rename this directory.") - elif os.path.isdir(os.path.join(os.path.pardir, "google-closure-library")): + elif os.path.isdir(os.path.join(CLOSURE_ROOT, "google-closure-library")): # When Closure is installed by npm, it is named "google-closure-library". #calcdeps = import_path(os.path.join( - # os.path.pardir, "google-closure-library", "closure", "bin", "calcdeps.py")) + # CLOSURE_ROOT, "google-closure-library", "closure", "bin", "calcdeps.py")) print("Error: Closure directory needs to be renamed from" "'google-closure-library' to 'closure-library'.\n" "Please rename this directory.") @@ -485,7 +529,7 @@ def exclude_horizontal(item): sys.exit(1) search_paths = calcdeps.ExpandDirectories( - ["core", os.path.join(os.path.pardir, "closure-library")]) + ["core", os.path.join(CLOSURE_ROOT, "google-closure-library")]) search_paths_horizontal = filter(exclude_vertical, search_paths) search_paths_vertical = filter(exclude_horizontal, search_paths) diff --git a/build/gen_blocks.js b/build/gen_blocks.js new file mode 100644 index 0000000000..2ab6e688b5 --- /dev/null +++ b/build/gen_blocks.js @@ -0,0 +1 @@ +goog.provide('Blockly.Blocks'); diff --git a/build/gen_generator.js b/build/gen_generator.js new file mode 100644 index 0000000000..0a97ce217a --- /dev/null +++ b/build/gen_generator.js @@ -0,0 +1 @@ +goog.provide('Blockly.Generator'); diff --git a/package.json b/package.json index fa10778e76..e56b96d4cd 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "gh-pages": "0.12.0", "glob": "7.1.2", "google-closure-compiler": "20180402.0.0", - "google-closure-library": "20170910.0.0", + "google-closure-library": "20180204.0.0", "graceful-fs": "4.1.11", "imports-loader": "0.6.5", "json": "9.0.4", From 130aa3b10e0d829e78c709ef1ed4251bbc3c2995 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Wed, 18 Apr 2018 17:39:12 -0400 Subject: [PATCH 0471/2135] build.py local support clean up --- .eslintignore | 1 + .gitignore | 1 + build.py | 374 +++++++++++++++++++++++++++---------------- build/test_expect.js | 1 + build/test_input.js | 1 + 5 files changed, 242 insertions(+), 136 deletions(-) create mode 100644 build/test_expect.js create mode 100644 build/test_input.js diff --git a/.eslintignore b/.eslintignore index 36cd9c958f..0d41495303 100644 --- a/.eslintignore +++ b/.eslintignore @@ -15,3 +15,4 @@ /dist/* /gh-pages/* /webpack.config.js +/build/* diff --git a/.gitignore b/.gitignore index d0981d9c36..ca2946dded 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ common.pyc /blocks_compressed_horizontal.js /blocks_compressed_vertical.js /blocks_compressed.js +/gh-pages/main.js /gh-pages/playgrounds /gh-pages/Gemfile.lock /gh-pages/closure-library diff --git a/build.py b/build.py index 2ab056ecd4..28cd47c097 100755 --- a/build.py +++ b/build.py @@ -41,7 +41,17 @@ import errno, glob, httplib, json, os, re, subprocess, threading, urllib -CLOSURE_ROOT = os.path.join("node_modules") +REMOTE_COMPILER = "remote" + +CLOSURE_DIR = os.path.pardir +CLOSURE_ROOT = os.path.pardir +CLOSURE_LIBRARY = "closure-library" +CLOSURE_COMPILER = REMOTE_COMPILER + +CLOSURE_DIR_NPM = "node_modules" +CLOSURE_ROOT_NPM = os.path.join("node_modules") +CLOSURE_LIBRARY_NPM = "google-closure-library" +CLOSURE_COMPILER_NPM = "google-closure-compiler" def import_path(fullpath): """Import a file with full path specification. @@ -61,6 +71,11 @@ def import_path(fullpath): del sys.path[-1] return module +def read(filename): + f = open(filename) + content = "".join(f.readlines()) + f.close() + return content HEADER = ("// Do not edit this file; automatically generated by build.py.\n" "'use strict';\n") @@ -70,10 +85,11 @@ class Gen_uncompressed(threading.Thread): """Generate a JavaScript file that loads Blockly's raw files. Runs in a separate thread. """ - def __init__(self, search_paths, vertical): + def __init__(self, search_paths, vertical, closure_env): threading.Thread.__init__(self) self.search_paths = search_paths self.vertical = vertical + self.closure_env = closure_env def run(self): if self.vertical: @@ -82,13 +98,13 @@ def run(self): target_filename = 'blockly_uncompressed_horizontal.js' f = open(target_filename, 'w') f.write(HEADER) - f.write(""" + f.write(self.format_js(""" var isNodeJS = !!(typeof module !== 'undefined' && module.exports && typeof window === 'undefined'); if (isNodeJS) { var window = {}; - require('google-closure-library'); + require('{closure_library}'); } window.BLOCKLY_DIR = (function() { @@ -110,7 +126,7 @@ def run(self): window.BLOCKLY_BOOT = function() { var dir = ''; if (isNodeJS) { - require('google-closure-library'); + require('{closure_library}'); dir = 'blockly'; } else { // Execute after Closure has loaded. @@ -118,9 +134,13 @@ def run(self): alert('Error: Closure not found. Read this:\\n' + 'developers.google.com/blockly/guides/modify/web/closure'); } - dir = window.BLOCKLY_DIR.match(/[^\\/]+$/)[0]; + if (window.BLOCKLY_DIR.search(/node_modules/)) { + dir = '..'; + } else { + dir = window.BLOCKLY_DIR.match(/[^\\/]+$/)[0]; + } } -""") +""")) add_dependency = [] base_path = calcdeps.FindClosureBasePath(self.search_paths) for dep in calcdeps.BuildDependenciesFromFiles(self.search_paths): @@ -137,7 +157,8 @@ def run(self): provides = [] for dep in calcdeps.BuildDependenciesFromFiles(self.search_paths): - if not dep.filename.startswith(CLOSURE_ROOT + os.sep): # '../' + # starts with '../' or 'node_modules/' + if not dep.filename.startswith(self.closure_env["closure_root"] + os.sep): provides.extend(dep.provides) provides.sort() # Deterministic build. f.write('\n') @@ -145,7 +166,7 @@ def run(self): for provide in provides: f.write("goog.require('%s');\n" % provide) - f.write(""" + f.write(self.format_js(""" delete this.BLOCKLY_DIR; delete this.BLOCKLY_BOOT; }; @@ -158,13 +179,37 @@ def run(self): document.write(''); // Load fresh Closure Library. document.write(''); + '/{closure_dir}/{closure_library}/closure/goog/base.js">'); document.write(''); } -""") +""")) f.close() print("SUCCESS: " + target_filename) + def format_js(self, code): + """Format JS in a way that python's format method can work with to not + consider brace-wrapped sections to be format replacements while still + replacing known keys. + """ + + key_whitelist = self.closure_env.keys() + + keys_pipe_separated = reduce(lambda accum, key: accum + "|" + key, key_whitelist) + begin_brace = re.compile(r"\{(?!%s)" % (keys_pipe_separated,)) + + end_brace = re.compile(r"\}") + def end_replacement(match): + try: + maybe_key = match.string[match.string[:match.start()].rindex("{") + 1:match.start()] + except ValueError: + return "}}" + + if maybe_key and maybe_key in key_whitelist: + return "}" + else: + return "}}" + + return begin_brace.sub("{{", end_brace.sub(end_replacement, code)).format(**self.closure_env) class Gen_compressed(threading.Thread): """Generate a JavaScript file that contains all of Blockly's core and all @@ -172,10 +217,11 @@ class Gen_compressed(threading.Thread): Uses the Closure Compiler's online API. Runs in a separate thread. """ - def __init__(self, search_paths_vertical, search_paths_horizontal): + def __init__(self, search_paths_vertical, search_paths_horizontal, closure_env): threading.Thread.__init__(self) self.search_paths_vertical = search_paths_vertical self.search_paths_horizontal = search_paths_horizontal + self.closure_env = closure_env def run(self): self.gen_core(True) @@ -198,38 +244,26 @@ def gen_core(self, vertical): search_paths = self.search_paths_horizontal # Define the parameters for the POST request. params = [ - ("compilation_level", "SIMPLE"), - # ("dependency_mode", "LOOSE"), - ("language_in", "ECMASCRIPT_2017"), - ("language_out", "ECMASCRIPT5"), - ("inject_libraries", "false"), - ("define", "goog.DEBUG=false"), - # ("assume_function_wrapper", ""), - # ("charset", "UTF-8"), - # ("output_wrapper", "%output%"), - # ("isolation_mode", "IIFE"), - # ("use_closure_library", "true"), - # ("output_format", "json"), - # ("output_info", "compiled_code"), - # ("output_info", "warnings"), - # ("output_info", "errors"), - # ("output_info", "statistics"), - ] + ("compilation_level", "SIMPLE"), + + # remote will filter this out + ("language_in", "ECMASCRIPT_2017"), + ("language_out", "ECMASCRIPT5"), + ("rewrite_polyfills", "false"), + ("define", "goog.DEBUG=false"), + + # local will filter this out + ("use_closure_library", "true"), + ] # Read in all the source files. filenames = calcdeps.CalculateDependencies(search_paths, - [os.path.join("core", "blockly.js")]) + [os.path.join("core", "blockly.js")]) filenames.sort() # Deterministic build. - # print str(filenames) - # exit() for filename in filenames: - # Filter out the Closure files (the compiler will add them). - # if filename.startswith(CLOSURE_ROOT + os.sep): # '../' - # continue - # f = open(filename) - # params.append(("js_code", "".join(f.readlines()))) + # Append filenames as false arguments the step before compiling will + # either transform them into arguments for local or remote compilation params.append(("js_file", filename)) - # f.close() self.do_compile(params, target_filename, filenames, "") @@ -245,33 +279,19 @@ def gen_blocks(self, block_type): filenames = glob.glob(os.path.join("blocks_common", "*.js")) # Define the parameters for the POST request. params = [ - ("compilation_level", "SIMPLE"), - # ("dependency_mode", "LOOSE"), - # ("process_closure_primitives", "0"), - # ("jscomp_off", "JSC_LATE_PROVIDE_ERROR"), - # ("defines", "goog.DEBUG=false"), - # ("output_format", "json"), - # ("output_info", "compiled_code"), - # ("output_info", "warnings"), - # ("output_info", "errors"), - # ("output_info", "statistics"), - # ("entry_point", "goog:Blockly.Blocks"), - # ("output_manifest", target_filename + ".MF"), - # ("output_module_dependencies", ""), - ] + ("compilation_level", "SIMPLE"), + ] # Read in all the source files. # Add Blockly.Blocks to be compatible with the compiler. - # params.append(("js_code", "goog.provide('Blockly.Blocks');")) params.append(("js_file", os.path.join("build", "gen_blocks.js"))) # Add Blockly.Colours for use of centralized colour bank filenames.append(os.path.join("core", "colours.js")) filenames.append(os.path.join("core", "constants.js")) for filename in filenames: - # f = open(filename) - # params.append(("js_code", "".join(f.readlines()))) + # Append filenames as false arguments the step before compiling will + # either transform them into arguments for local or remote compilation params.append(("js_file", filename)) - # f.close() # Remove Blockly.Blocks to be compatible with Blockly. remove = "var Blockly={Blocks:{}};" @@ -281,29 +301,20 @@ def gen_generator(self, language): target_filename = language + "_compressed.js" # Define the parameters for the POST request. params = [ - ("compilation_level", "SIMPLE"), - # ("dependency_mode", "LOOSE"), - # ("defines", "goog.DEBUG=false"), - # ("output_format", "json"), - # ("output_info", "compiled_code"), - # ("output_info", "warnings"), - # ("output_info", "errors"), - # ("output_info", "statistics"), - ] + ("compilation_level", "SIMPLE"), + ] # Read in all the source files. # Add Blockly.Generator to be compatible with the compiler. - # params.append(("js_code", "goog.provide('Blockly.Generator');")) params.append(("js_file", os.path.join("build", "gen_generator.js"))) filenames = glob.glob( - os.path.join("generators", language, "*.js")) + os.path.join("generators", language, "*.js")) filenames.sort() # Deterministic build. filenames.insert(0, os.path.join("generators", language + ".js")) for filename in filenames: - # f = open(filename) - # params.append(("js_code", "".join(f.readlines()))) + # Append filenames as false arguments the step before compiling will + # either transform them into arguments for local or remote compilation params.append(("js_file", filename)) - # f.close() filenames.insert(0, "[goog.provide]") # Remove Blockly.Generator to be compatible with Blockly. @@ -311,35 +322,87 @@ def gen_generator(self, language): self.do_compile(params, target_filename, filenames, remove) def do_compile(self, params, target_filename, filenames, remove): - # Send the request to Google. - - underWord = re.compile(r'_([a-z])') - # camel = lambda s: underWord.sub(lambda t: t.groups()[0].upper(), s) - camel = lambda s: s - camelParams = [("--" + camel(arg), value) if arg != "js_file" else (value,) for (arg, value) in params] - camelArgs = [item for pair in camelParams for item in pair] - args = [item for group in [["google-closure-compiler"], camelArgs] for item in group if item] - json.dump(args, open(target_filename + ".json", "w")) - json.dump(params, open(target_filename + ".raw.json", "w")) - proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - # proc.wait() - # print "boop" - # exit() - - # headers = {"Content-type": "application/x-www-form-urlencoded"} - # conn = httplib.HTTPSConnection("closure-compiler.appspot.com") - # conn.request("POST", "/compile", urllib.urlencode(params), headers) - # response = conn.getresponse() - # json_str = response.read() - # conn.close() - # json_str = stdout - - # Parse the JSON response. - # json_data = json.loads(json_str) - json_data = dict(compiledCode=stdout) - print target_filename, len(stdout) + do_compile = self.do_compile_remote if self.closure_env == REMOTE_COMPILER else self.do_compile_local + json_data = do_compile(params, target_filename) + + if self.report_errors(target_filename, filenames, json_data): + self.write_output(target_filename, remove, json_data) + self.report_stats(target_filename, json_data) + + def do_compile_local(self, params, target_filename): + filter_keys = ["use_closure_library"] + + # Drop arg if arg is js_file else add dashes + dash_params = [] + for (arg, value) in params: + dash_params.append((value,) if arg == "js_file" else ("--" + arg, value)) + + # Flatten dash_params into dash_args if their keys are not in filter_keys + dash_args = [] + for pair in dash_params: + if pair[0][2:] not in filter_keys: + dash_args.extend(pair) + + # Build the final args array by prepending google-closure-compiler to + # dash_args and dropping any falsy members + args = [] + for group in [["google-closure-compiler"], dash_args]: + args.extend(filter(lambda item: item, group)) + + proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + (stdout, stderr) = proc.communicate() + + # Build the JSON response. + filesizes = [os.path.getsize(value) for (arg, value) in params if arg == "js_file"] + return dict( + compiledCode=stdout, + statistics=dict( + originalSize=reduce(lambda v, size: v + size, filesizes, 0), + compressedSize=len(stdout), + ) + ) + + def do_compile_remote(self, params, target_filename): + filter_keys = [ + "language_in", + "language_out", + "rewrite_polyfills", + "define", + ] + params.extend([ + ("output_format", "json"), + ("output_info", "compiled_code"), + ("output_info", "warnings"), + ("output_info", "errors"), + ("output_info", "statistics"), + ]) + + # Send the request to Google. + remoteParams = [] + for (arg, value) in params: + if not arg in filter_keys: + if arg == "js_file": + if not value.startswith(self.closure_env["closure_root"] + os.sep): + remoteParams.append(("js_code", read(value))) + # Change the normal compilation_level value SIMPLE to the remove + # service's SIMPLE_OPTIMIZATIONS + elif arg == "compilation_level" and value == "SIMPLE": + remoteParams.append((arg, "SIMPLE_OPTIMIZATIONS")) + else: + remoteParams.append((arg, value)) + + headers = {"Content-type": "application/x-www-form-urlencoded"} + conn = httplib.HTTPSConnection("closure-compiler.appspot.com") + conn.request("POST", "/compile", urllib.urlencode(remoteParams), headers) + response = conn.getresponse() + json_str = response.read() + conn.close() + + # Parse the JSON response. + return json.loads(json_str) + + def report_errors(self, target_filename, filenames, json_data): def file_lookup(name): if not name.startswith("Input_"): return "???" @@ -375,6 +438,11 @@ def file_lookup(name): print((" " * warning["charno"]) + "^") print() + return True + + return False + + def write_output(self, target_filename, remove, json_data): if not json_data.has_key("compiledCode"): print("FATAL ERROR: Compiler did not return compiledCode.") sys.exit(1) @@ -405,22 +473,27 @@ def file_lookup(name): \\*/""") code = re.sub(LICENSE, "", code) - # stats = json_data["statistics"] - # original_b = stats["originalSize"] - # compressed_b = stats["compressedSize"] - # if original_b > 0 and compressed_b > 0: - f = open(target_filename, "w") - f.write(code) - f.close() - - # original_kb = int(original_b / 1024 + 0.5) - # compressed_kb = int(compressed_b / 1024 + 0.5) - # ratio = int(float(compressed_b) / float(original_b) * 100 + 0.5) - print("SUCCESS: " + target_filename) - # print("Size changed from %d KB to %d KB (%d%%)." % ( - # original_kb, compressed_kb, ratio)) - # else: - # print("UNKNOWN ERROR") + stats = json_data["statistics"] + original_b = stats["originalSize"] + compressed_b = stats["compressedSize"] + if original_b > 0 and compressed_b > 0: + f = open(target_filename, "w") + f.write(code) + f.close() + + def report_stats(self, target_filename, json_data): + stats = json_data["statistics"] + original_b = stats["originalSize"] + compressed_b = stats["compressedSize"] + if original_b > 0 and compressed_b > 0: + original_kb = int(original_b / 1024 + 0.5) + compressed_kb = int(compressed_b / 1024 + 0.5) + ratio = int(float(compressed_b) / float(original_b) * 100 + 0.5) + print("SUCCESS: " + target_filename) + print("Size changed from %d KB to %d KB (%d%%)." % ( + original_kb, compressed_kb, ratio)) + else: + print("UNKNOWN ERROR") class Gen_langfiles(threading.Thread): @@ -508,42 +581,71 @@ def exclude_horizontal(item): if __name__ == "__main__": try: + closure_dir = CLOSURE_DIR_NPM + closure_root = CLOSURE_ROOT_NPM + closure_library = CLOSURE_LIBRARY_NPM + closure_compiler = CLOSURE_COMPILER_NPM + + # Load calcdeps from the local library calcdeps = import_path(os.path.join( - CLOSURE_ROOT, "google-closure-library", "closure", "bin", "calcdeps.py")) - except ImportError: - if os.path.isdir(os.path.join(CLOSURE_ROOT, "closure-library-read-only")): - # Dir got renamed when Closure moved from Google Code to GitHub in 2014. - print("Error: Closure directory needs to be renamed from" - "'closure-library-read-only' to 'closure-library'.\n" - "Please rename this directory.") - elif os.path.isdir(os.path.join(CLOSURE_ROOT, "google-closure-library")): - # When Closure is installed by npm, it is named "google-closure-library". - #calcdeps = import_path(os.path.join( - # CLOSURE_ROOT, "google-closure-library", "closure", "bin", "calcdeps.py")) - print("Error: Closure directory needs to be renamed from" - "'google-closure-library' to 'closure-library'.\n" - "Please rename this directory.") - else: - print("""Error: Closure not found. Read this: -developers.google.com/blockly/guides/modify/web/closure""") - sys.exit(1) + closure_root, closure_library, "closure", "bin", "calcdeps.py")) + + # Sanity check the local compiler + test_args = [closure_compiler, os.path.join("build", "test_input.js")] + test_proc = subprocess.Popen(test_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + (stdout, _) = test_proc.communicate() + assert stdout == read(os.path.join("build", "test_expect.js")) + + print("Using local compiler: google-closure-compiler ...\n") + except (ImportError, AssertionError): + print("Using remote compiler: closure-compiler.appspot.com ...\n") + + try: + closure_dir = CLOSURE_DIR + closure_root = CLOSURE_ROOT + closure_library = CLOSURE_LIBRARY + closure_compiler = CLOSURE_COMPILER + + calcdeps = import_path(os.path.join( + closure_root, closure_library, "closure", "bin", "calcdeps.py")) + except ImportError: + if os.path.isdir(os.path.join(os.path.pardir, "closure-library-read-only")): + # Dir got renamed when Closure moved from Google Code to GitHub in 2014. + print("Error: Closure directory needs to be renamed from" + "'closure-library-read-only' to 'closure-library'.\n" + "Please rename this directory.") + elif os.path.isdir(os.path.join(os.path.pardir, "google-closure-library")): + print("Error: Closure directory needs to be renamed from" + "'google-closure-library' to 'closure-library'.\n" + "Please rename this directory.") + else: + print("""Error: Closure not found. Read this: + developers.google.com/blockly/guides/modify/web/closure""") + sys.exit(1) search_paths = calcdeps.ExpandDirectories( - ["core", os.path.join(CLOSURE_ROOT, "google-closure-library")]) + ["core", os.path.join(closure_root, closure_library)]) search_paths_horizontal = filter(exclude_vertical, search_paths) search_paths_vertical = filter(exclude_horizontal, search_paths) + closure_env = { + "closure_dir": closure_dir, + "closure_root": closure_root, + "closure_library": closure_library, + "closure_compiler": closure_compiler, + } + # Run all tasks in parallel threads. # Uncompressed is limited by processor speed. # Compressed is limited by network and server speed. # Vertical: - Gen_uncompressed(search_paths_vertical, True).start() + Gen_uncompressed(search_paths_vertical, True, closure_env).start() # Horizontal: - Gen_uncompressed(search_paths_horizontal, False).start() + Gen_uncompressed(search_paths_horizontal, False, closure_env).start() # Compressed forms of vertical and horizontal. - Gen_compressed(search_paths_vertical, search_paths_horizontal).start() + Gen_compressed(search_paths_vertical, search_paths_horizontal, closure_env).start() # This is run locally in a separate thread. # Gen_langfiles().start() diff --git a/build/test_expect.js b/build/test_expect.js new file mode 100644 index 0000000000..555c3f7c54 --- /dev/null +++ b/build/test_expect.js @@ -0,0 +1 @@ +var Blockly={Blocks:{}}; diff --git a/build/test_input.js b/build/test_input.js new file mode 100644 index 0000000000..2ab6e688b5 --- /dev/null +++ b/build/test_input.js @@ -0,0 +1 @@ +goog.provide('Blockly.Blocks'); From 269f396ee9a61d80d2a105cb081495c6d2386ad3 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 30 Apr 2018 13:20:39 -0700 Subject: [PATCH 0472/2135] More cleanup --- core/comment.js | 3 ++- core/flyout_base.js | 2 +- core/warning.js | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/comment.js b/core/comment.js index fe0dbc8380..be8ef81379 100644 --- a/core/comment.js +++ b/core/comment.js @@ -79,7 +79,8 @@ Blockly.Comment.prototype.drawIcon_ = function(group) { 'class': 'blocklyIconSymbol', 'd': 'm6.8,10h2c0.003,-0.617 0.271,-0.962 0.633,-1.266 2.875,-2.405' + '0.607,-5.534 -3.765,-3.874v1.7c3.12,-1.657 3.698,0.118 2.336,1.25' + - '-1.201,0.998 -1.201,1.528 -1.204,2.19z'}, + '-1.201,0.998 -1.201,1.528 -1.204,2.19z' + }, group); // Dot of question mark. Blockly.utils.createSvgElement('rect', diff --git a/core/flyout_base.js b/core/flyout_base.js index c6d2c95285..3b05a07d37 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -290,7 +290,7 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) { // Dragging the flyout up and down (or left and right). Array.prototype.push.apply(this.eventWrappers_, Blockly.bindEventWithChecks_( - this.svgBackground_, 'mousedown', this, this.onMouseDown_)); + this.svgGroup_, 'mousedown', this, this.onMouseDown_)); // A flyout connected to a workspace doesn't have its own current gesture. this.workspace_.getGesture = diff --git a/core/warning.js b/core/warning.js index 0216c7c7cb..b3b13e15ab 100644 --- a/core/warning.js +++ b/core/warning.js @@ -75,7 +75,10 @@ Blockly.Warning.prototype.drawIcon_ = function(group) { Blockly.utils.createSvgElement('rect', { 'class': 'blocklyIconSymbol', - 'x': '7', 'y': '11', 'height': '2', 'width': '2' + 'x': '7', + 'y': '11', + 'height': '2', + 'width': '2' }, group); }; From e9c4e5df53d26fc4965a47cdfcb7c05c4782d178 Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 30 Apr 2018 17:46:33 -0300 Subject: [PATCH 0473/2135] Add scroll compat blocks --- blocks_vertical/motion.js | 107 ++++++++++++++++++++++++++++++++++++++ msg/messages.js | 10 ++++ 2 files changed, 117 insertions(+) diff --git a/blocks_vertical/motion.js b/blocks_vertical/motion.js index f30b1ac946..2a9ca54e3b 100644 --- a/blocks_vertical/motion.js +++ b/blocks_vertical/motion.js @@ -478,3 +478,110 @@ Blockly.Blocks['motion_direction'] = { }); } }; + +Blockly.Blocks['motion_scroll_right'] = { + /** + * Block to scroll the stage right. Does not actually do anything. This is + * an obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.MOTION_SCROLLRIGHT, + "args0": [ + { + "type": "input_value", + "name": "DISTANCE" + } + ], + "category": Blockly.Categories.motion, + "extensions": ["colours_motion", "shape_statement"] + }); + } +}; + +Blockly.Blocks['motion_scroll_up'] = { + /** + * Block to scroll the stage up. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.MOTION_SCROLLUP, + "args0": [ + { + "type": "input_value", + "name": "DISTANCE" + } + ], + "category": Blockly.Categories.motion, + "extensions": ["colours_motion", "shape_statement"] + }); + } +}; + +Blockly.Blocks['motion_align_scene'] = { + /** + * Block to change the stage's scrolling alignment. Does not actually do + * anything. This is an obsolete block that is implemented for compatibility + * with Scratch 2.0 projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.MOTION_ALIGNSCENE, + "args0": [ + { + "type": "field_dropdown", + "name": "ALIGNMENT", + "options": [ + [Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMLEFT, 'bottom-left'], + [Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMRIGHT, 'bottom-right'], + [Blockly.Msg.MOTION_ALIGNSCENE_MIDDLE, 'middle'], + [Blockly.Msg.MOTION_ALIGNSCENE_TOPLEFT, 'top-left'], + [Blockly.Msg.MOTION_ALIGNSCENE_TOPRIGHT, 'top-right'] + ] + } + ], + "category": Blockly.Categories.motion, + "extensions": ["colours_motion", "shape_statement"] + }); + } +}; + +Blockly.Blocks['motion_xscroll'] = { + /** + * Block to report the stage's scroll position's X value. Does not actually + * do anything. This is an obsolete block that is implemented for + * compatibility with Scratch 2.0 projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.MOTION_XSCROLL, + "category": Blockly.Categories.motion, + "checkboxInFlyout": true, + "extensions": ["colours_motion", "output_number"] + }); + } +}; + +Blockly.Blocks['motion_yscroll'] = { + /** + * Block to report the stage's scroll position's Y value. Does not actually + * do anything. This is an obsolete block that is implemented for + * compatibility with Scratch 2.0 projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.MOTION_YSCROLL, + "category": Blockly.Categories.motion, + "checkboxInFlyout": true, + "extensions": ["colours_motion", "output_number"] + }); + } +}; diff --git a/msg/messages.js b/msg/messages.js index 057f5b8d27..b33338e1bb 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -146,6 +146,16 @@ Blockly.Msg.MOTION_SETROTATIONSTYLE_ALLAROUND = "all around"; Blockly.Msg.MOTION_XPOSITION = "x position"; Blockly.Msg.MOTION_YPOSITION = "y position"; Blockly.Msg.MOTION_DIRECTION = "direction"; +Blockly.Msg.MOTION_SCROLLRIGHT = "scroll right %1"; +Blockly.Msg.MOTION_SCROLLUP = "scroll up %1"; +Blockly.Msg.MOTION_ALIGNSCENE = "align scene %1"; +Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMLEFT = "bottom-left"; +Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMRIGHT = "bottom-right"; +Blockly.Msg.MOTION_ALIGNSCENE_MIDDLE = "middle"; +Blockly.Msg.MOTION_ALIGNSCENE_TOPLEFT = "top-left"; +Blockly.Msg.MOTION_ALIGNSCENE_TOPRIGHT = "top-right"; +Blockly.Msg.MOTION_XSCROLL = "x scroll"; +Blockly.Msg.MOTION_YSCROLL = "y scroll"; // Operators blocks Blockly.Msg.OPERATORS_ADD = "%1 + %2"; From 3c992dee6a0ffc6ec0a7cb6499f6afd3b9575dfa Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 30 Apr 2018 15:42:02 -0700 Subject: [PATCH 0474/2135] More lint and small changes from Blockly --- core/block.js | 29 +++++++++++++------------- core/block_dragger.js | 2 +- core/block_svg.js | 17 +++++++-------- core/blockly.js | 47 +++++++++++++++++++++++++----------------- core/bubble.js | 28 ++++++++++++++++++------- core/comment.js | 17 ++++++++------- core/extensions.js | 31 +++++++++++++++------------- core/field.js | 5 ++--- core/field_colour.js | 1 + core/field_date.js | 3 ++- core/field_dropdown.js | 3 ++- core/field_image.js | 12 +++++------ core/field_variable.js | 7 +++---- core/flyout_base.js | 6 +++--- core/flyout_button.js | 27 +++++++++++++++++------- core/mutator.js | 44 +++++++++++++++++++++++++-------------- core/scrollbar.js | 4 ++-- core/touch.js | 5 +++-- core/utils.js | 9 ++++---- 19 files changed, 174 insertions(+), 123 deletions(-) diff --git a/core/block.js b/core/block.js index 5d0ebfa304..4d69b8263d 100644 --- a/core/block.js +++ b/core/block.js @@ -51,7 +51,7 @@ goog.require('goog.string'); * @param {?string} prototypeName Name of the language object containing * type-specific functions for this block. * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise - * create a new id. + * create a new ID. * @constructor */ Blockly.Block = function(workspace, prototypeName, opt_id) { @@ -1188,11 +1188,12 @@ Blockly.Block.prototype.appendDummyInput = function(opt_name) { * @param {!Object} json Structured data describing the block. */ Blockly.Block.prototype.jsonInit = function(json) { + var warningPrefix = json['type'] ? 'Block "' + json['type'] + '": ' : ''; // Validate inputs. - goog.asserts.assert(json['output'] == undefined || - json['previousStatement'] == undefined, - 'Must not have both an output and a previousStatement.'); + goog.asserts.assert( + json['output'] == undefined || json['previousStatement'] == undefined, + warningPrefix + 'Must not have both an output and a previousStatement.'); // Set basic properties of block. if (json['colour'] !== undefined) { @@ -1343,11 +1344,11 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { var token = tokens[i]; if (typeof token == 'number') { if (token <= 0 || token > args.length) { - throw new Error('Block \"' + this.type + '\": ' + + throw new Error('Block "' + this.type + '": ' + 'Message index %' + token + ' out of range.'); } if (indexDup[token]) { - throw new Error('Block \"' + this.type + '\": ' + + throw new Error('Block "' + this.type + '": ' + 'Message index %' + token + ' duplicated.'); } indexDup[token] = true; @@ -1360,14 +1361,14 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { } } } - if(indexCount != args.length) { - throw new Error('Block \"' + this.type + '\": ' + + if (indexCount != args.length) { + throw new Error('Block "' + this.type + '": ' + 'Message does not reference all ' + args.length + ' arg(s).'); } // Add last dummy input if needed. if (elements.length && (typeof elements[elements.length - 1] == 'string' || - goog.string.startsWith(elements[elements.length - 1]['type'], - 'field_'))) { + goog.string.startsWith( + elements[elements.length - 1]['type'], 'field_'))) { var dummyInput = {type: 'input_dummy'}; if (lastDummyAlign) { dummyInput['align'] = lastDummyAlign; @@ -1487,8 +1488,8 @@ Blockly.Block.prototype.moveInputBefore = function(name, refName) { } } goog.asserts.assert(inputIndex != -1, 'Named input "%s" not found.', name); - goog.asserts.assert(refIndex != -1, 'Reference input "%s" not found.', - refName); + goog.asserts.assert( + refIndex != -1, 'Reference input "%s" not found.', refName); this.moveNumberedInputBefore(inputIndex, refIndex); }; @@ -1502,9 +1503,9 @@ Blockly.Block.prototype.moveNumberedInputBefore = function( // Validate arguments. goog.asserts.assert(inputIndex != refIndex, 'Can\'t move input to itself.'); goog.asserts.assert(inputIndex < this.inputList.length, - 'Input index ' + inputIndex + ' out of bounds.'); + 'Input index ' + inputIndex + ' out of bounds.'); goog.asserts.assert(refIndex <= this.inputList.length, - 'Reference input ' + refIndex + ' out of bounds.'); + 'Reference input ' + refIndex + ' out of bounds.'); // Remove input. var input = this.inputList[inputIndex]; this.inputList.splice(inputIndex, 1); diff --git a/core/block_dragger.js b/core/block_dragger.js index 384433947c..75a2e5ab9e 100644 --- a/core/block_dragger.js +++ b/core/block_dragger.js @@ -36,7 +36,7 @@ goog.require('goog.asserts'); /** * Class for a block dragger. It moves blocks around the workspace when they * are being dragged by a mouse or touch. - * @param {!Blockly.Block} block The block to drag. + * @param {!Blockly.BlockSvg} block The block to drag. * @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on. * @constructor */ diff --git a/core/block_svg.js b/core/block_svg.js index d2e6f096d9..04e7c7ad81 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1037,7 +1037,7 @@ Blockly.BlockSvg.prototype.setWarningText = function(text, opt_id) { } this.warning.setText(/** @type {string} */ (text), id); } else { - // Dispose all warnings if no id is given. + // Dispose all warnings if no ID is given. if (this.warning && !id) { this.warning.dispose(); changedState = true; @@ -1113,11 +1113,11 @@ Blockly.BlockSvg.prototype.setMouseThroughStyle = function(letMouseThrough) { */ Blockly.BlockSvg.prototype.setDeleteStyle = function(enable) { if (enable) { - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete'); + Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), + 'blocklyDraggingDelete'); } else { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete'); + Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), + 'blocklyDraggingDelete'); } }; @@ -1164,9 +1164,8 @@ Blockly.BlockSvg.prototype.bringToFront = function() { * @param {(string|Array.|null)=} opt_check Statement type or * list of statement types. Null/undefined if any type could be connected. */ -Blockly.BlockSvg.prototype.setPreviousStatement = - function(newBoolean, opt_check) { - /* eslint-disable indent */ +Blockly.BlockSvg.prototype.setPreviousStatement = function(newBoolean, + opt_check) { Blockly.BlockSvg.superClass_.setPreviousStatement.call(this, newBoolean, opt_check); @@ -1174,7 +1173,7 @@ Blockly.BlockSvg.prototype.setPreviousStatement = this.render(); this.bumpNeighbours_(); } -}; /* eslint-enable indent */ +}; /** * Set whether another block can chain onto the bottom of this block. diff --git a/core/blockly.js b/core/blockly.js index 32d4efdb73..8c65520a4f 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -126,8 +126,10 @@ Blockly.hueToRgb = function(hue) { * @return {!Object} Contains width and height properties. */ Blockly.svgSize = function(svg) { - return {width: svg.cachedWidth_, - height: svg.cachedHeight_}; + return { + width: svg.cachedWidth_, + height: svg.cachedHeight_ + }; }; /** @@ -324,7 +326,6 @@ Blockly.confirm = function(message, callback) { callback(window.confirm(message)); }; -/* eslint-disable no-unused-vars */ /** * Wrapper to window.prompt() that app developers may override to provide * alternatives to the modal browser window. Built-in browser prompts are @@ -333,17 +334,16 @@ Blockly.confirm = function(message, callback) { * @param {string} message The message to display to the user. * @param {string} defaultValue The value to initialize the prompt with. * @param {!function(string)} callback The callback for handling user response. - * @param {?string} opt_title An optional title for the prompt. - * @param {?string} opt_varType An optional variable type for variable specific + * @param {?string} _opt_title An optional title for the prompt. + * @param {?string} _opt_varType An optional variable type for variable specific * prompt behavior. */ -Blockly.prompt = function(message, defaultValue, callback, opt_title, - opt_varType) { +Blockly.prompt = function(message, defaultValue, callback, _opt_title, + _opt_varType) { // opt_title and opt_varType are unused because we only need them to pass // information to the scratch-gui, which overwrites this function callback(window.prompt(message, defaultValue)); }; -/* eslint-enable no-unused-vars */ /** * Helper function for defining a block from JSON. The resulting function has @@ -365,19 +365,28 @@ Blockly.jsonInitFactory_ = function(jsonDef) { * @param {!Array.} jsonArray An array of JSON block definitions. */ Blockly.defineBlocksWithJsonArray = function(jsonArray) { - for (var i = 0, elem; elem = jsonArray[i]; i++) { - var typename = elem.type; - if (typename == null || typename === '') { - console.warn('Block definition #' + i + - ' in JSON array is missing a type attribute. Skipping.'); + for (var i = 0; i < jsonArray.length; i++) { + var elem = jsonArray[i]; + if (!elem) { + console.warn( + 'Block definition #' + i + ' in JSON array is ' + elem + '. ' + + 'Skipping.'); } else { - if (Blockly.Blocks[typename]) { - console.warn('Block definition #' + i + - ' in JSON array overwrites prior definition of "' + typename + '".'); + var typename = elem.type; + if (typename == null || typename === '') { + console.warn( + 'Block definition #' + i + + ' in JSON array is missing a type attribute. Skipping.'); + } else { + if (Blockly.Blocks[typename]) { + console.warn( + 'Block definition #' + i + ' in JSON array' + + ' overwrites prior definition of "' + typename + '".'); + } + Blockly.Blocks[typename] = { + init: Blockly.jsonInitFactory_(elem) + }; } - Blockly.Blocks[typename] = { - init: Blockly.jsonInitFactory_(elem) - }; } } }; diff --git a/core/bubble.js b/core/bubble.js index 56bb8b44bc..b258115f42 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -162,7 +162,7 @@ Blockly.Bubble.bubbleMouseUp_ = function(/*e*/) { Blockly.Bubble.prototype.rendered_ = false; /** - * Absolute coordinate of anchor point, in workspace coordinates + * Absolute coordinate of anchor point, in workspace coordinates. * @type {goog.math.Coordinate} * @private */ @@ -256,10 +256,8 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { Blockly.utils.createSvgElement('line', { 'class': 'blocklyResizeLine', - 'x1': resizeSize / 3, - 'y1': resizeSize - 1, - 'x2': resizeSize - 1, - 'y2': resizeSize / 3 + 'x1': resizeSize / 3, 'y1': resizeSize - 1, + 'x2': resizeSize - 1, 'y2': resizeSize / 3 }, this.resizeGroup_); Blockly.utils.createSvgElement('line', { @@ -371,11 +369,16 @@ Blockly.Bubble.prototype.registerResizeEvent = function(callback) { /** * Move this bubble to the top of the stack. + * @return {!boolean} Whether or not the bubble has been moved. * @private */ Blockly.Bubble.prototype.promote_ = function() { var svgGroup = this.bubbleGroup_.parentNode; - svgGroup.appendChild(this.bubbleGroup_); + if (svgGroup.lastChild !== this.bubbleGroup_) { + svgGroup.appendChild(this.bubbleGroup_); + return true; + } + return false; }; /** @@ -448,8 +451,17 @@ Blockly.Bubble.prototype.positionBubble_ = function() { left += this.relativeLeft_; } var top = this.relativeTop_ + this.anchorXY_.y; - this.bubbleGroup_.setAttribute('transform', - 'translate(' + left + ',' + top + ')'); + this.moveTo(left, top); +}; + +/** + * Move the bubble group to the specified location in workspace coordinates. + * @param {number} x The x position to move to. + * @param {number} y The y position to move to. + * @package + */ +Blockly.Bubble.prototype.moveTo = function(x, y) { + this.bubbleGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')'); }; /** diff --git a/core/comment.js b/core/comment.js index be8ef81379..eab5408c8b 100644 --- a/core/comment.js +++ b/core/comment.js @@ -126,10 +126,10 @@ Blockly.Comment.prototype.createEditor_ = function() { Blockly.bindEventWithChecks_(textarea, 'wheel', this, function(e) { e.stopPropagation(); }); - Blockly.bindEventWithChecks_(textarea, 'change', this, function(/* e */) { + Blockly.bindEventWithChecks_(textarea, 'change', this, function(_e) { if (this.text_ != textarea.value) { Blockly.Events.fire(new Blockly.Events.BlockChange( - this.block_, 'comment', null, this.text_, textarea.value)); + this.block_, 'comment', null, this.text_, textarea.value)); this.text_ = textarea.value; } }); @@ -213,17 +213,18 @@ Blockly.Comment.prototype.setVisible = function(visible) { /** * Bring the comment to the top of the stack when clicked on. - * @param {!Event} e Mouse up event. + * @param {!Event} _e Mouse up event. * @private */ -Blockly.Comment.prototype.textareaFocus_ = function(/*e*/) { +Blockly.Comment.prototype.textareaFocus_ = function(_e) { // Ideally this would be hooked to the focus event for the comment. // However doing so in Firefox swallows the cursor for unknown reasons. // So this is hooked to mouseup instead. No big deal. - this.bubble_.promote_(); - // Since the act of moving this node within the DOM causes a loss of focus, - // we need to reapply the focus. - this.textarea_.focus(); + if (this.bubble_.promote_()) { + // Since the act of moving this node within the DOM causes a loss of focus, + // we need to reapply the focus. + this.textarea_.focus(); + } }; /** diff --git a/core/extensions.js b/core/extensions.js index 983c3876bf..1aabed1be0 100644 --- a/core/extensions.js +++ b/core/extensions.js @@ -76,6 +76,9 @@ Blockly.Extensions.register = function(name, initFn) { * registered. */ Blockly.Extensions.registerMixin = function(name, mixinObj) { + if (!goog.isObject(mixinObj)){ + throw new Error('Error: Mixin "' + name + '" must be a object'); + } Blockly.Extensions.register(name, function() { this.mixin(mixinObj); }); @@ -373,24 +376,24 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, * Emits console warnings when they are not. * @param {!Blockly.Block} block The block containing the dropdown * @param {string} dropdownName The name of the dropdown - * @param {!Object} lookupTable The string lookup table + * @param {!Object.} lookupTable The string lookup table * @private */ -Blockly.Extensions.checkDropdownOptionsInTable_ = - function(block, dropdownName, lookupTable) { - // Validate all dropdown options have values. - var dropdown = block.getField(dropdownName); - if (!dropdown.isOptionListDynamic()) { - var options = dropdown.getOptions(); - for (var i = 0; i < options.length; ++i) { - var optionKey = options[i][1]; // label, then value - if (lookupTable[optionKey] == null) { - console.warn('No tooltip mapping for value ' + optionKey + - ' of field ' + dropdownName + ' of block type ' + block.type); - } +Blockly.Extensions.checkDropdownOptionsInTable_ = function(block, dropdownName, + lookupTable) { + // Validate all dropdown options have values. + var dropdown = block.getField(dropdownName); + if (!dropdown.isOptionListDynamic()) { + var options = dropdown.getOptions(); + for (var i = 0; i < options.length; ++i) { + var optionKey = options[i][1]; // label, then value + if (lookupTable[optionKey] == null) { + console.warn('No tooltip mapping for value ' + optionKey + + ' of field ' + dropdownName + ' of block type ' + block.type); } } - }; + } +}; /** * Builds an extension function that will install a dynamic tooltip. The diff --git a/core/field.js b/core/field.js index a4f3b30635..8a9cf67222 100644 --- a/core/field.js +++ b/core/field.js @@ -733,14 +733,13 @@ Blockly.Field.prototype.onMouseDown_ = function(e) { } }; - /** * Change the tooltip text for this field. - * @param {string|!Element} newTip Text for tooltip or a parent element to + * @param {string|!Element} _newTip Text for tooltip or a parent element to * link to for its tooltip. * @abstract */ -Blockly.Field.prototype.setTooltip = function(/*newTip*/) { +Blockly.Field.prototype.setTooltip = function(_newTip) { // Non-abstract sub-classes may wish to implement this. See FieldLabel. }; diff --git a/core/field_colour.js b/core/field_colour.js index 7f42c7ca0b..58afad2425 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -34,6 +34,7 @@ goog.require('goog.events'); goog.require('goog.style'); goog.require('goog.ui.ColorPicker'); + /** * Class for a colour input field. * @param {string} colour The initial colour in '#rrggbb' format. diff --git a/core/field_date.js b/core/field_date.js index 31f5bbf1a7..253cbb4b83 100644 --- a/core/field_date.js +++ b/core/field_date.js @@ -30,6 +30,7 @@ goog.require('Blockly.Field'); goog.require('Blockly.utils'); goog.require('goog.date'); +goog.require('goog.date.DateTime'); goog.require('goog.dom'); goog.require('goog.events'); goog.require('goog.i18n.DateTimeSymbols'); @@ -156,7 +157,7 @@ Blockly.FieldDate.prototype.createWidget_ = function() { picker.setShowWeekNum(false); var div = Blockly.WidgetDiv.DIV; picker.render(div); - picker.setDate(goog.date.fromIsoString(this.getValue())); + picker.setDate(goog.date.DateTime.fromIsoString(this.getValue())); return picker; }; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 2728b60867..6d12654518 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -367,7 +367,8 @@ Blockly.FieldDropdown.prototype.trimOptions_ = function() { }; /** - * @return {boolean} True if the option list is generated by a function. Otherwise false. + * @return {boolean} True if the option list is generated by a function. + * Otherwise false. */ Blockly.FieldDropdown.prototype.isOptionListDynamic = function() { return goog.isFunction(this.menuGenerator_); diff --git a/core/field_image.js b/core/field_image.js index 733f698ac9..0b9c4a8789 100644 --- a/core/field_image.js +++ b/core/field_image.js @@ -95,12 +95,12 @@ Blockly.FieldImage.prototype.init = function() { } /** @type {SVGElement} */ this.imageElement_ = Blockly.utils.createSvgElement( - 'image', - { - 'height': this.height_ + 'px', - 'width': this.width_ + 'px' - }, - this.fieldGroup_); + 'image', + { + 'height': this.height_ + 'px', + 'width': this.width_ + 'px' + }, + this.fieldGroup_); this.setValue(this.src_); this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); diff --git a/core/field_variable.js b/core/field_variable.js index d510c6de4b..c5ce7b949a 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -359,11 +359,10 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) { }; /** - * Whether this field references any Blockly variables. If true it may need to - * be handled differently during serialization and deserialization. Subclasses - * may override this. - * @return {boolean} True if this field has any variable references. + * Overrides referencesVariables(), indicating this field refers to a variable. + * @return {boolean} True. * @package + * @override */ Blockly.FieldVariable.prototype.referencesVariables = function() { return true; diff --git a/core/flyout_base.js b/core/flyout_base.js index b47c4025d0..15b06baf2d 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -50,7 +50,7 @@ Blockly.Flyout = function(workspaceOptions) { /** * @type {!Blockly.Workspace} - * @private + * @protected */ this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions); this.workspace_.isFlyout = true; @@ -352,7 +352,7 @@ Blockly.Flyout.prototype.getHeight = function() { }; /** - * Get the flyout's workspace. + * Get the workspace inside the flyout. * @return {!Blockly.WorkspaceSvg} The workspace inside the flyout. * @package */ @@ -724,7 +724,7 @@ Blockly.Flyout.prototype.createBlock = function(originalBlock) { Blockly.Events.setGroup(true); Blockly.Events.fire(new Blockly.Events.Create(newBlock)); // Fire a VarCreate event for each (if any) new variable created. - for(var i = 0; i < newVariables.length; i++) { + for (var i = 0; i < newVariables.length; i++) { var thisVariable = newVariables[i]; Blockly.Events.fire(new Blockly.Events.VarCreate(thisVariable)); } diff --git a/core/flyout_button.js b/core/flyout_button.js index e8db40527e..e15fe0a1d0 100644 --- a/core/flyout_button.js +++ b/core/flyout_button.js @@ -145,20 +145,31 @@ Blockly.FlyoutButton.prototype.createDom = function() { if (!this.isLabel_) { // Shadow rectangle (light source does not mirror in RTL). var shadow = Blockly.utils.createSvgElement('rect', - {'class': 'blocklyFlyoutButtonShadow', - 'rx': 4, 'ry': 4, 'x': 1, 'y': 1}, - this.svgGroup_); + { + 'class': 'blocklyFlyoutButtonShadow', + 'rx': 4, + 'ry': 4, + 'x': 1, + 'y': 1 + }, + this.svgGroup_); } // Background rectangle. var rect = Blockly.utils.createSvgElement('rect', - {'class': this.isLabel_ ? - 'blocklyFlyoutLabelBackground' : 'blocklyFlyoutButtonBackground', - 'rx': 4, 'ry': 4}, + { + 'class': this.isLabel_ ? + 'blocklyFlyoutLabelBackground' : 'blocklyFlyoutButtonBackground', + 'rx': 4, 'ry': 4 + }, this.svgGroup_); var svgText = Blockly.utils.createSvgElement('text', - {'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText', - 'x': 0, 'y': 0, 'text-anchor': 'middle'}, + { + 'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText', + 'x': 0, + 'y': 0, + 'text-anchor': 'middle' + }, this.svgGroup_); svgText.textContent = this.text_; diff --git a/core/mutator.js b/core/mutator.js index 548f2c3c05..95cfa6a708 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -65,25 +65,37 @@ Blockly.Mutator.prototype.workspaceHeight_ = 0; Blockly.Mutator.prototype.drawIcon_ = function(group) { // Square with rounded corners. Blockly.utils.createSvgElement('rect', - {'class': 'blocklyIconShape', - 'rx': '4', 'ry': '4', - 'height': '16', 'width': '16'}, - group); + { + 'class': 'blocklyIconShape', + 'rx': '4', + 'ry': '4', + 'height': '16', + 'width': '16' + }, + group); // Gear teeth. Blockly.utils.createSvgElement('path', - {'class': 'blocklyIconSymbol', - 'd': 'm4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 ' + - '0.41,0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 ' + - '1.8,0 0.3,-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 ' + - '0.9,-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,-0.41 ' + - '-0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 -0.127,-1.138 ' + - '-0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,0.682 -1.043,-0.457 ' + - '-0.41,0.11 -0.899,1.559 0.108,0.409z'}, - group); + { + 'class': 'blocklyIconSymbol', + 'd': 'm4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 0.41,' + + '0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,' + + '-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,' + + '-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,' + + '-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 ' + + '-0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,' + + '0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z' + }, + group); // Axle hole. - Blockly.utils.createSvgElement('circle', - {'class': 'blocklyIconShape', 'r': '2.7', 'cx': '8', 'cy': '8'}, - group); + Blockly.utils.createSvgElement( + 'circle', + { + 'class': 'blocklyIconShape', + 'r': '2.7', + 'cx': '8', + 'cy': '8' + }, + group); }; /** diff --git a/core/scrollbar.js b/core/scrollbar.js index 98d0971581..450f12c0c1 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -258,8 +258,8 @@ Blockly.Scrollbar.prototype.origin_ = new goog.math.Coordinate(0, 0); Blockly.Scrollbar.prototype.originHasChanged_ = true; /** - * The size of the area within which the scrollbar handle can move. - * Coordinate system: pixel coordinates. + * The size of the area within which the scrollbar handle can move, in CSS + * pixels. * @type {number} * @private */ diff --git a/core/touch.js b/core/touch.js index ec6f849abd..6de4dcf055 100644 --- a/core/touch.js +++ b/core/touch.js @@ -34,6 +34,7 @@ goog.require('goog.events'); goog.require('goog.events.BrowserFeature'); goog.require('goog.string'); + /** * Which touch events are we currently paying attention to? * @type {DOMString} @@ -63,7 +64,7 @@ Blockly.longPid_ = 0; /** * Context menus on touch devices are activated using a long-press. - * Unfortunately the contextmenu touch event is currently (2015) only suported + * Unfortunately the contextmenu touch event is currently (2015) only supported * by Chrome. This function is fired on any touchstart event, queues a task, * which after about a second opens the context menu. The tasks is killed * if the touch event terminates early. @@ -153,7 +154,7 @@ Blockly.Touch.getTouchIdentifierFromEvent = function(e) { Blockly.Touch.checkTouchIdentifier = function(e) { var identifier = Blockly.Touch.getTouchIdentifierFromEvent(e); - // if (Blockly.touchIdentifier_ )is insufficient because android touch + // if (Blockly.touchIdentifier_ )is insufficient because Android touch // identifiers may be zero. if (Blockly.Touch.touchIdentifier_ != undefined && Blockly.Touch.touchIdentifier_ != null) { diff --git a/core/utils.js b/core/utils.js index b3ba4f45c1..8ad6f7096c 100644 --- a/core/utils.js +++ b/core/utils.js @@ -269,7 +269,7 @@ Blockly.utils.getScale_REGEXP_ = /scale\(\s*([-+\d.e]+)\s*\)/; * @private */ Blockly.utils.getRelativeXY.XY_3D_REGEX_ = - /transform:\s*translate3d\(\s*([-+\d.e]+)px([ ,]\s*([-+\d.e]+)\s*)px([ ,]\s*([-+\d.e]+)\s*)px\)?/; + /transform:\s*translate3d\(\s*([-+\d.e]+)px([ ,]\s*([-+\d.e]+)\s*)px([ ,]\s*([-+\d.e]+)\s*)px\)?/; /** * Static regex to pull the x,y,z values out of a translate3d() style property. @@ -278,7 +278,7 @@ Blockly.utils.getRelativeXY.XY_3D_REGEX_ = * @private */ Blockly.utils.getRelativeXY.XY_2D_REGEX_ = - /transform:\s*translate\(\s*([-+\d.e]+)px([ ,]\s*([-+\d.e]+)\s*)px\)?/; + /transform:\s*translate\(\s*([-+\d.e]+)px([ ,]\s*([-+\d.e]+)\s*)px\)?/; /** * Helper method for creating SVG elements. @@ -491,7 +491,8 @@ Blockly.utils.checkMessageReferences = function(message) { * @return {!Array.} Array of strings and numbers. * @private */ -Blockly.utils.tokenizeInterpolation_ = function(message, parseInterpolationTokens) { +Blockly.utils.tokenizeInterpolation_ = function(message, + parseInterpolationTokens) { var tokens = []; var chars = message.split(''); chars.push(''); // End marker. @@ -499,7 +500,7 @@ Blockly.utils.tokenizeInterpolation_ = function(message, parseInterpolationToken // 0 - Base case. // 1 - % found. // 2 - Digit found. - // 3 - Message ref found + // 3 - Message ref found. var state = 0; var buffer = []; var number = null; From 67403b3a3bc5ebad1dce94abe5b3b1ffa80392eb Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 30 Apr 2018 19:50:56 -0300 Subject: [PATCH 0475/2135] Add loud? block --- blocks_vertical/sensing.js | 16 ++++++++++++++++ msg/messages.js | 1 + 2 files changed, 17 insertions(+) diff --git a/blocks_vertical/sensing.js b/blocks_vertical/sensing.js index 834198f9d4..1a422aedc3 100644 --- a/blocks_vertical/sensing.js +++ b/blocks_vertical/sensing.js @@ -358,6 +358,22 @@ Blockly.Blocks['sensing_loudness'] = { } }; +Blockly.Blocks['sensing_loud'] = { + /** + * Block to report if the loudness is "loud" (greater than 10). This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 and + * 1.4 projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.SENSING_LOUD, + "category": Blockly.Categories.sensing, + "extensions": ["colours_sensing", "output_boolean"] + }); + } +}; + Blockly.Blocks['sensing_timer'] = { /** * Block to report timer diff --git a/msg/messages.js b/msg/messages.js index 057f5b8d27..7bf946af53 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -202,6 +202,7 @@ Blockly.Msg.SENSING_SETDRAGMODE = "set drag mode %1"; Blockly.Msg.SENSING_SETDRAGMODE_DRAGGABLE = "draggable"; Blockly.Msg.SENSING_SETDRAGMODE_NOTDRAGGABLE = "not draggable"; Blockly.Msg.SENSING_LOUDNESS = "loudness"; +Blockly.Msg.SENSING_LOUD = "loud?"; Blockly.Msg.SENSING_TIMER = "timer"; Blockly.Msg.SENSING_RESETTIMER = "reset timer"; Blockly.Msg.SENSING_OF = "%1 of %2"; From 26b82223b4c293f09ca11408dc513133112e3ebe Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 30 Apr 2018 15:55:31 -0700 Subject: [PATCH 0476/2135] Add a file for scratch-blocks-specific util functions --- core/block_render_svg_vertical.js | 14 ++-- core/block_svg.js | 4 +- core/field_textinput.js | 20 +++--- core/gesture.js | 4 +- core/scratch_blocks_utils.js | 103 ++++++++++++++++++++++++++++++ core/utils.js | 67 ------------------- core/workspace_svg.js | 5 +- 7 files changed, 133 insertions(+), 84 deletions(-) create mode 100644 core/scratch_blocks_utils.js diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js index 976ff97aa1..6e20230715 100644 --- a/core/block_render_svg_vertical.js +++ b/core/block_render_svg_vertical.js @@ -27,6 +27,7 @@ goog.provide('Blockly.BlockSvg.render'); goog.require('Blockly.BlockSvg'); +goog.require('Blockly.scratchBlocksUtils'); goog.require('Blockly.utils'); @@ -502,8 +503,8 @@ Blockly.BlockSvg.DEFINE_BLOCK_PADDING_RIGHT = 2 * Blockly.BlockSvg.GRID_UNIT; */ Blockly.BlockSvg.prototype.updateColour = function() { var strokeColour = this.getColourTertiary(); - var renderShadowed = - this.isShadow() && !Blockly.utils.isShadowArgumentReporter(this); + var renderShadowed = this.isShadow() && + !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this); if (renderShadowed && this.parentBlock_) { // Pull shadow block stroke colour from parent block's tertiary if possible. @@ -924,7 +925,8 @@ Blockly.BlockSvg.prototype.computeInputWidth_ = function(input) { Blockly.BlockSvg.prototype.computeInputHeight_ = function(input, row, previousRow) { if (this.inputList.length === 1 && this.outputConnection && - (this.isShadow() && !Blockly.utils.isShadowArgumentReporter(this))) { + (this.isShadow() && + !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this))) { // "Lone" field blocks are smaller. return Blockly.BlockSvg.MIN_BLOCK_Y_SINGLE_FIELD_OUTPUT; } else if (this.outputConnection) { @@ -983,7 +985,8 @@ Blockly.BlockSvg.prototype.computeRightEdge_ = function(curEdge, hasStatement) { // Blocks with notches edge = Math.max(edge, Blockly.BlockSvg.MIN_BLOCK_X); } else if (this.outputConnection) { - if (this.isShadow() && !Blockly.utils.isShadowArgumentReporter(this)) { + if (this.isShadow() && + !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this)) { // Single-fields edge = Math.max(edge, Blockly.BlockSvg.MIN_BLOCK_X_SHADOW_OUTPUT); } else { @@ -1013,7 +1016,8 @@ Blockly.BlockSvg.prototype.computeRightEdge_ = function(curEdge, hasStatement) { Blockly.BlockSvg.prototype.computeOutputPadding_ = function(inputRows) { // Only apply to blocks with outputs and not single fields (shadows). if (!this.getOutputShape() || !this.outputConnection || - (this.isShadow() && !Blockly.utils.isShadowArgumentReporter(this))) { + (this.isShadow() && + !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this))) { return; } // Blocks with outputs must have single row to be padded. diff --git a/core/block_svg.js b/core/block_svg.js index d2e6f096d9..b27816124f 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -31,9 +31,11 @@ goog.require('Blockly.BlockAnimations'); goog.require('Blockly.ContextMenu'); goog.require('Blockly.Grid'); goog.require('Blockly.RenderedConnection'); +goog.require('Blockly.scratchBlocksUtils'); goog.require('Blockly.Tooltip'); goog.require('Blockly.Touch'); goog.require('Blockly.utils'); + goog.require('goog.Timer'); goog.require('goog.asserts'); goog.require('goog.dom'); @@ -682,7 +684,7 @@ Blockly.BlockSvg.prototype.duplicateAndDragCallback_ = function() { var newBlock = Blockly.Xml.domToBlock(xml, ws); // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste - Blockly.utils.changeObscuredShadowIds(newBlock); + Blockly.scratchBlocksUtils.changeObscuredShadowIds(newBlock); var svgRootNew = newBlock.getSvgRoot(); if (!svgRootNew) { diff --git a/core/field_textinput.js b/core/field_textinput.js index e99f644ab0..26119845b1 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -30,7 +30,9 @@ goog.require('Blockly.BlockSvg.render'); goog.require('Blockly.Colours'); goog.require('Blockly.Field'); goog.require('Blockly.Msg'); +goog.require('Blockly.scratchBlocksUtils'); goog.require('Blockly.utils'); + goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); @@ -124,13 +126,15 @@ Blockly.FieldTextInput.prototype.init = function() { // If not in a shadow block, draw a box. if (notInShadow) { - this.box_ = Blockly.utils.createSvgElement('rect', { - 'x': 0, - 'y': 0, - 'width': this.size_.width, - 'height': this.size_.height, - 'fill': this.sourceBlock_.getColourTertiary() - }); + this.box_ = Blockly.utils.createSvgElement('rect', + { + 'x': 0, + 'y': 0, + 'width': this.size_.width, + 'height': this.size_.height, + 'fill': this.sourceBlock_.getColourTertiary() + } + ); this.fieldGroup_.insertBefore(this.box_, this.textElement_); } }; @@ -455,7 +459,7 @@ Blockly.FieldTextInput.prototype.resizeEditor_ = function() { var width; if (Blockly.BlockSvg.FIELD_TEXTINPUT_EXPAND_PAST_TRUNCATION) { // Resize the box based on the measured width of the text, pre-truncation - var textWidth = Blockly.utils.measureText( + var textWidth = Blockly.scratchBlocksUtils.measureText( Blockly.FieldTextInput.htmlInput_.style.fontSize, Blockly.FieldTextInput.htmlInput_.style.fontFamily, Blockly.FieldTextInput.htmlInput_.style.fontWeight, diff --git a/core/gesture.js b/core/gesture.js index 2a0d6041b2..46dd66123e 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -32,6 +32,7 @@ goog.require('Blockly.BlockDragger'); goog.require('Blockly.constants'); goog.require('Blockly.Events'); goog.require('Blockly.FlyoutDragger'); +goog.require('Blockly.scratchBlocksUtils'); goog.require('Blockly.Tooltip'); goog.require('Blockly.Touch'); goog.require('Blockly.WorkspaceDragger'); @@ -699,7 +700,8 @@ Blockly.Gesture.prototype.setStartField = function(field) { Blockly.Gesture.prototype.setStartBlock = function(block) { if (!this.startBlock_) { this.startBlock_ = block; - this.shouldDuplicateOnDrag_ = Blockly.utils.isShadowArgumentReporter(block); + this.shouldDuplicateOnDrag_ = + Blockly.scratchBlocksUtils.isShadowArgumentReporter(block); if (block.isInFlyout && block != block.getRootBlock()) { this.setTargetBlock_(block.getRootBlock()); } else { diff --git a/core/scratch_blocks_utils.js b/core/scratch_blocks_utils.js new file mode 100644 index 0000000000..739c30d215 --- /dev/null +++ b/core/scratch_blocks_utils.js @@ -0,0 +1,103 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Utility methods for Scratch Blocks but not Blockly. + * @author fenichel@google.com (Rachel Fenichel) + */ +'use strict'; + +/** + * @name Blockly.scratchBlocksUtils + * @namespace + **/ +goog.provide('Blockly.scratchBlocksUtils'); + + +/** + * Measure some text using a canvas in-memory. + * Does not exist in Blockly, but needed in scratch-blocks + * @param {string} fontSize E.g., '10pt' + * @param {string} fontFamily E.g., 'Arial' + * @param {string} fontWeight E.g., '600' + * @param {string} text The actual text to measure + * @return {number} Width of the text in px. + * @package + */ +Blockly.scratchBlocksUtils.measureText = function(fontSize, fontFamily, + fontWeight, text) { + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + context.font = fontWeight + ' ' + fontSize + ' ' + fontFamily; + return context.measureText(text).width; +}; + +/** + * Encode a string's HTML entities. + * E.g., -> <a> + * Does not exist in Blockly, but needed in scratch-blocks + * @param {string} rawStr Unencoded raw string to encode. + * @return {string} String with HTML entities encoded. + * @package + */ +Blockly.scratchBlocksUtils.encodeEntities = function(rawStr) { + // CC-BY-SA https://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript + return rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { + return '&#' + i.charCodeAt(0) + ';'; + }); +}; + +/** + * Re-assign obscured shadow blocks new IDs to prevent collisions + * Scratch specific to help the VM handle deleting obscured shadows. + * @param {Blockly.Block} block the root block to be processed. + * @package + */ +Blockly.scratchBlocksUtils.changeObscuredShadowIds = function(block) { + var blocks = block.getDescendants(); + for (var i = blocks.length - 1; i >= 0; i--) { + var descendant = blocks[i]; + for (var j = 0; j < descendant.inputList.length; j++) { + var connection = descendant.inputList[j].connection; + if (connection) { + var shadowDom = connection.getShadowDom(); + if (shadowDom) { + shadowDom.setAttribute('id', Blockly.utils.genUid()); + connection.setShadowDom(shadowDom); + } + } + } + } +}; + +/** + * Whether a block is both a shadow block and an argument reporter. These + * blocks have special behaviour in scratch-blocks: they're duplicated when + * dragged, and they are rendered slightly differently from normal shadow + * blocks. + * @param {!Blockly.BlockSvg} block The block that should be used to make this + * decision. + * @return {boolean} True if the block should be duplicated on drag. + * @package + */ +Blockly.scratchBlocksUtils.isShadowArgumentReporter = function(block) { + return (block.isShadow() && (block.type == 'argument_reporter_boolean' || + block.type == 'argument_reporter_string_number')); +}; diff --git a/core/utils.js b/core/utils.js index b3ba4f45c1..141ac98cd3 100644 --- a/core/utils.js +++ b/core/utils.js @@ -811,36 +811,6 @@ Blockly.utils.wrapToText_ = function(words, wordBreaks) { return text.join(''); }; -/** - * Measure some text using a canvas in-memory. - * Does not exist in Blockly, but needed in scratch-blocks - * @param {string} fontSize E.g., '10pt' - * @param {string} fontFamily E.g., 'Arial' - * @param {string} fontWeight E.g., '600' - * @param {string} text The actual text to measure - * @return {number} Width of the text in px. - */ -Blockly.utils.measureText = function(fontSize, fontFamily, fontWeight, text) { - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - context.font = fontWeight + ' ' + fontSize + ' ' + fontFamily; - return context.measureText(text).width; -}; - -/** - * Encode a string's HTML entities. - * E.g., -> <a> - * Does not exist in Blockly, but needed in scratch-blocks - * @param {string} rawStr Unencoded raw string to encode. - * @return {string} String with HTML entities encoded. - */ -Blockly.utils.encodeEntities = function(rawStr) { - // CC-BY-SA https://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript - return rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { - return '&#' + i.charCodeAt(0) + ';'; - }); -}; - /** * Check if 3D transforms are supported by adding an element * and attempting to set the property. @@ -945,43 +915,6 @@ Blockly.utils.setCssTransform = function(node, transform) { node.style['-webkit-transform'] = transform; }; -/** - * Re-assign obscured shadow blocks new IDs to prevent collisions - * Scratch specific to help the VM handle deleting obscured shadows. - * @param {Blockly.Block} block the root block to be processed. - */ -Blockly.utils.changeObscuredShadowIds = function(block) { - var blocks = block.getDescendants(); - for (var i = blocks.length - 1; i >= 0; i--) { - var descendant = blocks[i]; - for (var j = 0; j < descendant.inputList.length; j++) { - var connection = descendant.inputList[j].connection; - if (connection) { - var shadowDom = connection.getShadowDom(); - if (shadowDom) { - shadowDom.setAttribute('id', Blockly.utils.genUid()); - connection.setShadowDom(shadowDom); - } - } - } - } -}; - -/** - * Whether a block is both a shadow block and an argument reporter. These - * blocks have special behaviour in scratch-blocks: they're duplicated when - * dragged, and they are rendered slightly differently from normal shadow - * blocks. - * @param {!Blockly.BlockSvg} block The block that should be used to make this - * decision. - * @return {boolean} True if the block should be duplicated on drag. - * @package - */ -Blockly.utils.isShadowArgumentReporter = function(block) { - return (block.isShadow() && (block.type == 'argument_reporter_boolean' || - block.type == 'argument_reporter_string_number')); -}; - /** * Get the position of the current viewport in window coordinates. This takes * scroll into account. diff --git a/core/workspace_svg.js b/core/workspace_svg.js index a251d8271a..83f2b393e1 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -37,6 +37,7 @@ goog.require('Blockly.Events'); goog.require('Blockly.Gesture'); goog.require('Blockly.Grid'); goog.require('Blockly.Options'); +goog.require('Blockly.scratchBlocksUtils'); goog.require('Blockly.ScrollbarPair'); goog.require('Blockly.Touch'); goog.require('Blockly.Trashcan'); @@ -931,7 +932,7 @@ Blockly.WorkspaceSvg.prototype.reportValue = function(id, value) { var contentDiv = Blockly.DropDownDiv.getContentDiv(); var valueReportBox = goog.dom.createElement('div'); valueReportBox.setAttribute('class', 'valueReportBox'); - valueReportBox.innerHTML = Blockly.utils.encodeEntities(value); + valueReportBox.innerHTML = Blockly.scratchBlocksUtils.encodeEntities(value); contentDiv.appendChild(valueReportBox); Blockly.DropDownDiv.setColour( Blockly.Colours.valueReportBackground, @@ -955,7 +956,7 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) { try { var block = Blockly.Xml.domToBlock(xmlBlock, this); // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste - Blockly.utils.changeObscuredShadowIds(block); + Blockly.scratchBlocksUtils.changeObscuredShadowIds(block); // Move the duplicate to original position. var blockX = parseInt(xmlBlock.getAttribute('x'), 10); var blockY = parseInt(xmlBlock.getAttribute('y'), 10); From aa3651c2d9d20f1d4f9db94f83a4feccd05715e7 Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 30 Apr 2018 21:20:51 -0300 Subject: [PATCH 0477/2135] Add 'hide all sprites' and 'user id' blocks --- blocks_vertical/looks.js | 17 +++++++++++++++++ blocks_vertical/sensing.js | 16 ++++++++++++++++ msg/messages.js | 2 ++ 3 files changed, 35 insertions(+) diff --git a/blocks_vertical/looks.js b/blocks_vertical/looks.js index af17c49f9d..2be7e91d83 100644 --- a/blocks_vertical/looks.js +++ b/blocks_vertical/looks.js @@ -146,6 +146,23 @@ Blockly.Blocks['looks_hide'] = { } }; +Blockly.Blocks['looks_hideallsprites'] = { + /** + * Hide-all-sprites block. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit( + { + "message0": Blockly.Msg.LOOKS_HIDEALLSPRITES, + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); + } +}; + Blockly.Blocks['looks_changeeffectby'] = { /** * Block to change graphic effect. diff --git a/blocks_vertical/sensing.js b/blocks_vertical/sensing.js index 834198f9d4..9c50d66995 100644 --- a/blocks_vertical/sensing.js +++ b/blocks_vertical/sensing.js @@ -507,3 +507,19 @@ Blockly.Blocks['sensing_username'] = { }); } }; + +Blockly.Blocks['sensing_userid'] = { + /** + * Block to report user's ID. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.SENSING_USERID, + "category": Blockly.Categories.sensing, + "extensions": ["colours_sensing", "output_number"] + }); + } +}; diff --git a/msg/messages.js b/msg/messages.js index 057f5b8d27..749faf6859 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -90,6 +90,7 @@ Blockly.Msg.LOOKS_THINKFORSECS = "think %1 for %2 seconds"; Blockly.Msg.LOOKS_THINK = "think %1"; Blockly.Msg.LOOKS_SHOW = "show"; Blockly.Msg.LOOKS_HIDE = "hide"; +Blockly.Msg.LOOKS_HIDEALLSPRITES = "hide all sprites"; Blockly.Msg.LOOKS_EFFECT_COLOR = "color"; Blockly.Msg.LOOKS_EFFECT_FISHEYE = "fisheye"; Blockly.Msg.LOOKS_EFFECT_WHIRL = "whirl"; @@ -224,6 +225,7 @@ Blockly.Msg.SENSING_CURRENT_MINUTE = "minute"; Blockly.Msg.SENSING_CURRENT_SECOND = "second"; Blockly.Msg.SENSING_DAYSSINCE2000 = "days since 2000"; Blockly.Msg.SENSING_USERNAME = "username"; +Blockly.Msg.SENSING_USERID = "user id"; // Sound blocks Blockly.Msg.SOUND_PLAY = "start sound %1"; From 2f5362b7a4ce1011b99190a041cf1ffb3fc63385 Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 30 Apr 2018 21:29:21 -0300 Subject: [PATCH 0478/2135] Add set/change stretch blocks --- blocks_vertical/looks.js | 43 ++++++++++++++++++++++++++++++++++++++++ msg/messages.js | 2 ++ 2 files changed, 45 insertions(+) diff --git a/blocks_vertical/looks.js b/blocks_vertical/looks.js index 2be7e91d83..d7b165f193 100644 --- a/blocks_vertical/looks.js +++ b/blocks_vertical/looks.js @@ -298,6 +298,49 @@ Blockly.Blocks['looks_size'] = { } }; +Blockly.Blocks['looks_changestretchby'] = { + /** + * Block to change stretch. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_CHANGESTRETCHBY, + "args0": [ + { + "type": "input_value", + "name": "CHANGE" + } + ], + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); + } +}; + +Blockly.Blocks['looks_setstretchto'] = { + /** + * Block to set stretch. Does not actually do anything. This is an obsolete + * block that is implemented for compatibility with Scratch 2.0 projects. + * @this Blockly.Block + */ + init: function() { + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_SETSTRETCHTO, + "args0": [ + { + "type": "input_value", + "name": "STRETCH" + } + ], + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); + } +}; + Blockly.Blocks['looks_costume'] = { /** * Costumes drop-down menu. diff --git a/msg/messages.js b/msg/messages.js index 749faf6859..026a3fdec3 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -104,6 +104,8 @@ Blockly.Msg.LOOKS_CLEARGRAPHICEFFECTS = "clear graphic effects"; Blockly.Msg.LOOKS_CHANGESIZEBY = "change size by %1"; Blockly.Msg.LOOKS_SETSIZETO = "set size to %1 %"; Blockly.Msg.LOOKS_SIZE = "size"; +Blockly.Msg.LOOKS_CHANGESTRETCHBY = "change stretch by %1"; +Blockly.Msg.LOOKS_SETSTRETCHTO = "set stretch to %1"; Blockly.Msg.LOOKS_SWITCHCOSTUMETO = "switch costume to %1"; Blockly.Msg.LOOKS_NEXTCOSTUME = "next costume"; Blockly.Msg.LOOKS_SWITCHBACKDROPTO = "switch backdrop to %1"; From 1696a4c9a0c16d9ec44c9bed2306614bb70afbe4 Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 30 Apr 2018 21:52:58 -0300 Subject: [PATCH 0479/2135] Make set-stretch label more accurate --- msg/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msg/messages.js b/msg/messages.js index 026a3fdec3..490bcb68b3 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -105,7 +105,7 @@ Blockly.Msg.LOOKS_CHANGESIZEBY = "change size by %1"; Blockly.Msg.LOOKS_SETSIZETO = "set size to %1 %"; Blockly.Msg.LOOKS_SIZE = "size"; Blockly.Msg.LOOKS_CHANGESTRETCHBY = "change stretch by %1"; -Blockly.Msg.LOOKS_SETSTRETCHTO = "set stretch to %1"; +Blockly.Msg.LOOKS_SETSTRETCHTO = "set stretch to %1 %"; Blockly.Msg.LOOKS_SWITCHCOSTUMETO = "switch costume to %1"; Blockly.Msg.LOOKS_NEXTCOSTUME = "next costume"; Blockly.Msg.LOOKS_SWITCHBACKDROPTO = "switch backdrop to %1"; From 99912bd917a4f0a316918b4c47f411d4212f4c00 Mon Sep 17 00:00:00 2001 From: Florrie Date: Tue, 1 May 2018 13:51:34 -0300 Subject: [PATCH 0480/2135] Add history documentation on stretch blocks --- blocks_vertical/looks.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/blocks_vertical/looks.js b/blocks_vertical/looks.js index d7b165f193..f4dc5c2e27 100644 --- a/blocks_vertical/looks.js +++ b/blocks_vertical/looks.js @@ -301,8 +301,15 @@ Blockly.Blocks['looks_size'] = { Blockly.Blocks['looks_changestretchby'] = { /** * Block to change stretch. Does not actually do anything. This is an - * obsolete block that is implemented for compatibility with Scratch 2.0 - * projects. + * obsolete block that is implemented for compatibility with Scratch 1.4 + * projects as well as 2.0 projects that still have the block. + * The "stretch" blocks were introduced in very early versions of Scratch, + * but their functionality was removed shortly later. They still appeared + * correctly up until (and including) Scratch 1.4 - as "change stretch by" + * and "set stretch to" - but were removed altogether in Scratch 2.0, and + * displayed as red "undefined" blocks. Some Scratch projects still contain + * these blocks, however, and they don't open in 3.0 unless the blocks + * actually exist (though they still don't funcitonally do anything). * @this Blockly.Block */ init: function() { @@ -323,7 +330,8 @@ Blockly.Blocks['looks_changestretchby'] = { Blockly.Blocks['looks_setstretchto'] = { /** * Block to set stretch. Does not actually do anything. This is an obsolete - * block that is implemented for compatibility with Scratch 2.0 projects. + * block that is implemented for compatibility with Scratch 1.4 projects + * (see looks_changestretchby). * @this Blockly.Block */ init: function() { From 11a5616804c70a3706ac24be06554a995a9bfe75 Mon Sep 17 00:00:00 2001 From: Florrie Date: Tue, 1 May 2018 14:01:45 -0300 Subject: [PATCH 0481/2135] Remove unnecessary/misleading checkboxInFlyout properties --- blocks_vertical/motion.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/blocks_vertical/motion.js b/blocks_vertical/motion.js index 2a9ca54e3b..6679ebfaa0 100644 --- a/blocks_vertical/motion.js +++ b/blocks_vertical/motion.js @@ -563,7 +563,6 @@ Blockly.Blocks['motion_xscroll'] = { this.jsonInit({ "message0": Blockly.Msg.MOTION_XSCROLL, "category": Blockly.Categories.motion, - "checkboxInFlyout": true, "extensions": ["colours_motion", "output_number"] }); } @@ -580,7 +579,6 @@ Blockly.Blocks['motion_yscroll'] = { this.jsonInit({ "message0": Blockly.Msg.MOTION_YSCROLL, "category": Blockly.Categories.motion, - "checkboxInFlyout": true, "extensions": ["colours_motion", "output_number"] }); } From f13b15880adc67a8f4e87e7d8139c0d317a46633 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 2 May 2018 08:56:34 -0700 Subject: [PATCH 0482/2135] New script to pull from Blockly and do basic cleanup --- cleanup.sh | 3 +- pull_from_blockly.sh | 150 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100755 pull_from_blockly.sh diff --git a/cleanup.sh b/cleanup.sh index d35a82515b..a6ecd9b53e 100755 --- a/cleanup.sh +++ b/cleanup.sh @@ -80,7 +80,8 @@ echo Miscellaneous cleanup... keep_ours=".github/ISSUE_TEMPLATE.md \ .github/PULL_REQUEST_TEMPLATE.md \ .gitignore \ -.travis.yml" +.travis.yml \ +core/block_animations.js" for filename in $keep_ours do diff --git a/pull_from_blockly.sh b/pull_from_blockly.sh new file mode 100755 index 0000000000..c4ae63e3fe --- /dev/null +++ b/pull_from_blockly.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# Pull from Blockly into Scratch Blocks and do basic cleanup. +# Rachel Fenichel (fenichel@google.com) + +BOLD='\e[1m' +NOBOLD='\e[21m' + +# Formatting helper. +empty_lines() { printf '\n\n'; } +bold_echo() { + echo -e "${BOLD}$1${NOBOLD}" +} + +stop_on_fail() { + # Fail if any command fails. + set -e + # Even if you're piping the output. + set -o pipefail +} + +# Undo the effects of start_failing. +continue_on_fail() { + set +e + set +o pipefail +} + +# Prompt for y/n and put the result in $prompt_result +# The first argument specifies the text to use in the prompt. +# The second argument specifies which value to use if we're skipping prompts. +prompt() { + if [ $with_prompts ] + then + if [ $2 = true ] + then + paren_text="(Y/n)" + else + paren_text="(y/N)" + fi + # Prompt the user and retry if they try any funny business. + while true; do + read -p "$1 $paren_text > " yn + case $yn in + [Yy]* ) prompt_result=true; break;; + [Nn]* ) prompt_result=false; break;; + * ) echo "Please answer yes or no.";; + esac + done + else + # Running without prompts. Use the default value. + prompt_result=$2; + fi +} + + +# Ask the user for confirmation, then pull from Blockly's develop branch. +# The default is to do the pull. +pull_from_develop_fn() { + empty_lines + prompt "Do you want to pull from develop?" true + if [ $prompt_result = false ] + then + bold_echo "You don't want to pull from develop. Why are you running this script?" + exit + fi + + bold_echo "Pulling from Blockly's develop branch" + sleep .5 + # This pull will likely fail with merge conflicts, but that's okay. + # However, this means that we won't fail on errors other than merge conflicts. + continue_on_fail + git pull https://github.com/google/blockly.git develop + stop_on_fail +} + +# Ask the user for confirmation, then run cleanup. +# The default is to run cleanup. +run_cleanup_fn() { + empty_lines + prompt "Ready to run cleanup.sh. Continue?" true + if [ $prompt_result = false ] + then + bold_echo "Skipping cleanup.sh" + prompt_for_merge_abort + empty_lines + bold_echo "Done" + exit + fi + + # Cleanup.sh resolves common conflicts. + ./cleanup.sh +} + +# Ask the user for confirmation, then possibly abort the merge. +# The default is to *not* abort the merge. +# Used to clean up the repo instead of leaving it in a bad state. +prompt_for_merge_abort() { + empty_lines + prompt "Do you want to abort this merge?" false + if [ $prompt_result = false ] + then + bold_echo "Skipping merge abort." + else + bold_echo "Running git merge --abort" + git merge --abort + bold_echo "Current status" + sleep .5 + git status + bold_echo "Done" + exit + fi +} + +# Ask the user for confirmation, then show the current repo status. +# The default to to show status. +display_status_fn() { + empty_lines + prompt "Do you want to display the current status?" true + if [ $prompt_result = true ] + then + # Tell the user the current state. + bold_echo "Current status" + sleep .5 + git status + else + bold_echo "Skipping status display." + fi +} + +# Give the user one more chance to abort the merge, then tell them what their +# next steps should be. +finish_fn() { + prompt_for_merge_abort + # Helpful tips about what to do next. + empty_lines + sleep .5 + echo "Fix conflicts and run 'git commit'." + echo "Use 'git add ' to mark resolution." + echo "Use 'git merge --abort' to abort this merge." +} + +# Check whether we're running with prompts. If unset, we'll skip all prompts. +with_prompts=$1 + +# Here we go! +stop_on_fail +pull_from_develop_fn +run_cleanup_fn +display_status_fn +finish_fn From 49eca4eee00bad7d7bb25532c605dcb6f761da80 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 2 May 2018 11:05:43 -0700 Subject: [PATCH 0483/2135] More lint --- core/inject.js | 117 +++++++++++++++++++++++++++++++++--------- core/input.js | 15 +++--- core/toolbox.js | 21 ++++---- core/utils.js | 6 +-- core/variable_map.js | 2 +- core/widgetdiv.js | 4 +- core/workspace_svg.js | 22 ++++---- core/xml.js | 39 +++++++------- 8 files changed, 144 insertions(+), 82 deletions(-) diff --git a/core/inject.js b/core/inject.js index ce8a9f593f..841520f5a5 100644 --- a/core/inject.js +++ b/core/inject.js @@ -131,46 +131,100 @@ Blockly.createDom_ = function(container, options) { // Using a dilate distorts the block shape. // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. var stackGlowFilter = Blockly.utils.createSvgElement('filter', - {'id': 'blocklyStackGlowFilter', - 'height': '160%', 'width': '180%', y: '-30%', x: '-40%'}, defs); + { + 'id': 'blocklyStackGlowFilter', + 'height': '160%', + 'width': '180%', + y: '-30%', + x: '-40%' + }, + defs); options.stackGlowBlur = Blockly.utils.createSvgElement('feGaussianBlur', - {'in': 'SourceGraphic', - 'stdDeviation': Blockly.Colours.stackGlowSize}, stackGlowFilter); + { + 'in': 'SourceGraphic', + 'stdDeviation': Blockly.Colours.stackGlowSize + }, + stackGlowFilter); // Set all gaussian blur pixels to 1 opacity before applying flood var componentTransfer = Blockly.utils.createSvgElement('feComponentTransfer', {'result': 'outBlur'}, stackGlowFilter); Blockly.utils.createSvgElement('feFuncA', - {'type': 'table', 'tableValues': '0' + goog.string.repeat(' 1', 16)}, componentTransfer); + { + 'type': 'table', + 'tableValues': '0' + goog.string.repeat(' 1', 16) + }, + componentTransfer); // Color the highlight Blockly.utils.createSvgElement('feFlood', - {'flood-color': Blockly.Colours.stackGlow, - 'flood-opacity': Blockly.Colours.stackGlowOpacity, 'result': 'outColor'}, stackGlowFilter); + { + 'flood-color': Blockly.Colours.stackGlow, + 'flood-opacity': Blockly.Colours.stackGlowOpacity, + 'result': 'outColor' + }, + stackGlowFilter); Blockly.utils.createSvgElement('feComposite', - {'in': 'outColor', 'in2': 'outBlur', - 'operator': 'in', 'result': 'outGlow'}, stackGlowFilter); + { + 'in': 'outColor', + 'in2': 'outBlur', + 'operator': 'in', + 'result': 'outGlow' + }, + stackGlowFilter); Blockly.utils.createSvgElement('feComposite', - {'in': 'SourceGraphic', 'in2': 'outGlow', 'operator': 'over'}, stackGlowFilter); + { + 'in': 'SourceGraphic', + 'in2': 'outGlow', + 'operator': 'over' + }, + stackGlowFilter); // Filter for replacement marker var replacementGlowFilter = Blockly.utils.createSvgElement('filter', - {'id': 'blocklyReplacementGlowFilter', - 'height': '160%', 'width': '180%', y: '-30%', x: '-40%'}, defs); + { + 'id': 'blocklyReplacementGlowFilter', + 'height': '160%', + 'width': '180%', + y: '-30%', + x: '-40%' + }, + defs); Blockly.utils.createSvgElement('feGaussianBlur', - {'in': 'SourceGraphic', - 'stdDeviation': Blockly.Colours.replacementGlowSize}, replacementGlowFilter); + { + 'in': 'SourceGraphic', + 'stdDeviation': Blockly.Colours.replacementGlowSize + }, + replacementGlowFilter); // Set all gaussian blur pixels to 1 opacity before applying flood var componentTransfer = Blockly.utils.createSvgElement('feComponentTransfer', {'result': 'outBlur'}, replacementGlowFilter); Blockly.utils.createSvgElement('feFuncA', - {'type': 'table', 'tableValues': '0' + goog.string.repeat(' 1', 16)}, componentTransfer); + { + 'type': 'table', + 'tableValues': '0' + goog.string.repeat(' 1', 16) + }, + componentTransfer); // Color the highlight Blockly.utils.createSvgElement('feFlood', - {'flood-color': Blockly.Colours.replacementGlow, - 'flood-opacity': Blockly.Colours.replacementGlowOpacity, 'result': 'outColor'}, replacementGlowFilter); + { + 'flood-color': Blockly.Colours.replacementGlow, + 'flood-opacity': Blockly.Colours.replacementGlowOpacity, + 'result': 'outColor' + }, + replacementGlowFilter); Blockly.utils.createSvgElement('feComposite', - {'in': 'outColor', 'in2': 'outBlur', - 'operator': 'in', 'result': 'outGlow'}, replacementGlowFilter); + { + 'in': 'outColor', + 'in2': 'outBlur', + 'operator': 'in', + 'result': 'outGlow' + }, + replacementGlowFilter); Blockly.utils.createSvgElement('feComposite', - {'in': 'SourceGraphic', 'in2': 'outGlow', 'operator': 'over'}, replacementGlowFilter); + { + 'in': 'SourceGraphic', + 'in2': 'outGlow', + 'operator': 'over' + }, + replacementGlowFilter); /* @@ -179,13 +233,26 @@ Blockly.createDom_ = function(container, options) { */ var disabledPattern = Blockly.utils.createSvgElement('pattern', - {'id': 'blocklyDisabledPattern' + rnd, - 'patternUnits': 'userSpaceOnUse', - 'width': 10, 'height': 10}, defs); + { + 'id': 'blocklyDisabledPattern' + rnd, + 'patternUnits': 'userSpaceOnUse', + 'width': 10, + 'height': 10 + }, + defs); Blockly.utils.createSvgElement('rect', - {'width': 10, 'height': 10, 'fill': '#aaa'}, disabledPattern); + { + 'width': 10, + 'height': 10, + 'fill': '#aaa' + }, + disabledPattern); Blockly.utils.createSvgElement('path', - {'d': 'M 0 0 L 10 10 M 10 0 L 0 10', 'stroke': '#cc0'}, disabledPattern); + { + 'd': 'M 0 0 L 10 10 M 10 0 L 0 10', + 'stroke': '#cc0' + }, + disabledPattern); options.disabledPatternId = disabledPattern.id; options.gridPattern = Blockly.Grid.createDom(rnd, options.gridOptions, defs); diff --git a/core/input.js b/core/input.js index 797467014e..91bb4f12bf 100644 --- a/core/input.js +++ b/core/input.js @@ -274,13 +274,12 @@ Blockly.Input.prototype.initOutlinePath = function(svgRoot) { } if (this.type == Blockly.INPUT_VALUE) { this.outlinePath = Blockly.utils.createSvgElement( - 'path', - { - 'class': 'blocklyPath', - 'style': 'visibility: hidden', // Hide by default - shown when not connected. - 'd': '' // IE doesn't like paths without the data definition, set an empty default - }, - svgRoot - ); + 'path', + { + 'class': 'blocklyPath', + 'style': 'visibility: hidden', // Hide by default - shown when not connected. + 'd': '' // IE doesn't like paths without the data definition, set an empty default + }, + svgRoot); } }; diff --git a/core/toolbox.js b/core/toolbox.js index cb207c3794..0c144b36f7 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -170,8 +170,8 @@ Blockly.Toolbox.prototype.createFlyout_ = function() { } this.flyout_.setParentToolbox(this); - goog.dom.insertSiblingAfter(this.flyout_.createDom('svg'), - this.workspace_.getParentSvg()); + goog.dom.insertSiblingAfter( + this.flyout_.createDom('svg'), this.workspace_.getParentSvg()); this.flyout_.init(workspace); }; @@ -268,8 +268,8 @@ Blockly.Toolbox.prototype.clearSelection = function() { * @package */ Blockly.Toolbox.prototype.addDeleteStyle = function() { - Blockly.utils.addClass(/** @type {!Element} */ (this.HtmlDiv), - 'blocklyToolboxDelete'); + Blockly.utils.addClass( + /** @type {!Element} */ (this.HtmlDiv), 'blocklyToolboxDelete'); }; /** @@ -277,8 +277,8 @@ Blockly.Toolbox.prototype.addDeleteStyle = function() { * @package */ Blockly.Toolbox.prototype.removeDeleteStyle = function() { - Blockly.utils.removeClass(/** @type {!Element} */ (this.HtmlDiv), - 'blocklyToolboxDelete'); + Blockly.utils.removeClass( + /** @type {!Element} */ (this.HtmlDiv), 'blocklyToolboxDelete'); }; /** @@ -592,10 +592,9 @@ Blockly.Toolbox.Category.prototype.dispose = function() { Blockly.Toolbox.Category.prototype.createDom = function() { var toolbox = this.parent_.parent_; this.item_ = goog.dom.createDom('div', - {'class': 'scratchCategoryMenuItem'}); + {'class': 'scratchCategoryMenuItem'}); this.label_ = goog.dom.createDom('div', - {'class': 'scratchCategoryMenuItemLabel'}, - this.name_); + {'class': 'scratchCategoryMenuItemLabel'}, this.name_); if (this.iconURI_) { this.bubble_ = goog.dom.createDom('div', {'class': 'scratchCategoryItemIcon'}); @@ -609,8 +608,8 @@ Blockly.Toolbox.Category.prototype.createDom = function() { this.item_.appendChild(this.bubble_); this.item_.appendChild(this.label_); this.parentHtml_.appendChild(this.item_); - Blockly.bindEvent_(this.item_, 'mouseup', toolbox, - toolbox.setSelectedItemFactory(this)); + Blockly.bindEvent_( + this.item_, 'mouseup', toolbox, toolbox.setSelectedItemFactory(this)); }; /** diff --git a/core/utils.js b/core/utils.js index 65ce562244..15a0125cd4 100644 --- a/core/utils.js +++ b/core/utils.js @@ -288,8 +288,8 @@ Blockly.utils.getRelativeXY.XY_2D_REGEX_ = * @return {!SVGElement} Newly created SVG element. */ Blockly.utils.createSvgElement = function(name, attrs, parent /*, opt_workspace */) { - var e = /** @type {!SVGElement} */ ( - document.createElementNS(Blockly.SVG_NS, name)); + var e = /** @type {!SVGElement} */ + (document.createElementNS(Blockly.SVG_NS, name)); for (var key in attrs) { e.setAttribute(key, attrs[key]); } @@ -567,7 +567,7 @@ Blockly.utils.tokenizeInterpolation_ = function(message, if (goog.isString(rawValue)) { // Attempt to dereference substrings, too, appending to the end. Array.prototype.push.apply(tokens, - Blockly.utils.tokenizeInterpolation(rawValue)); + Blockly.utils.tokenizeInterpolation(rawValue)); } else if (parseInterpolationTokens) { // When parsing interpolation tokens, numbers are special // placeholders (%1, %2, etc). Make sure all other values are diff --git a/core/variable_map.js b/core/variable_map.js index 3451f6460a..dd4a20b5d2 100644 --- a/core/variable_map.js +++ b/core/variable_map.js @@ -18,7 +18,7 @@ * limitations under the License. */ - /** +/** * @fileoverview Object representing a map of variables and their types. * @author marisaleung@google.com (Marisa Leung) */ diff --git a/core/widgetdiv.js b/core/widgetdiv.js index 408f723bda..0917defe71 100644 --- a/core/widgetdiv.js +++ b/core/widgetdiv.js @@ -146,8 +146,8 @@ Blockly.WidgetDiv.hide = function(opt_noAnimate) { // If we want to animate out, set the appropriate timer for final dispose. if (Blockly.WidgetDiv.disposeAnimationFinished_ && !opt_noAnimate) { Blockly.WidgetDiv.disposeAnimationTimer_ = window.setTimeout( - Blockly.WidgetDiv.hide, // Come back to hide and take the first branch. - Blockly.WidgetDiv.disposeAnimationTimerLength_ * 1000 + Blockly.WidgetDiv.hide, // Come back to hide and take the first branch. + Blockly.WidgetDiv.disposeAnimationTimerLength_ * 1000 ); } else { // No timer provided (or no animation desired) - auto-hide the DOM now. diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 83f2b393e1..30d3a6b5da 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -935,8 +935,8 @@ Blockly.WorkspaceSvg.prototype.reportValue = function(id, value) { valueReportBox.innerHTML = Blockly.scratchBlocksUtils.encodeEntities(value); contentDiv.appendChild(valueReportBox); Blockly.DropDownDiv.setColour( - Blockly.Colours.valueReportBackground, - Blockly.Colours.valueReportBorder + Blockly.Colours.valueReportBackground, + Blockly.Colours.valueReportBorder ); Blockly.DropDownDiv.showPositionedByBlock(this, block); }; @@ -1067,8 +1067,8 @@ Blockly.WorkspaceSvg.prototype.deleteVariableById = function(id) { */ Blockly.WorkspaceSvg.prototype.createVariable = function(name, opt_type, opt_id) { var variableInMap = (this.getVariable(name, opt_type) != null); - var newVar = Blockly.WorkspaceSvg.superClass_.createVariable.call(this, name, - opt_type, opt_id); + var newVar = Blockly.WorkspaceSvg.superClass_.createVariable.call( + this, name, opt_type, opt_id); // For performance reasons, only refresh the the toolbox for new variables. // Variables that already exist should already be there. if (!variableInMap && (opt_type != Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE)) { @@ -1380,8 +1380,8 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) { if (deleteList.length < 2 ) { deleteNext(); } else { - Blockly.confirm(Blockly.Msg.DELETE_ALL_BLOCKS. - replace('%1', String(deleteCount)), + Blockly.confirm( + Blockly.Msg.DELETE_ALL_BLOCKS.replace('%1', String(deleteCount)), function(ok) { if (ok) { deleteNext(); @@ -1698,13 +1698,12 @@ Blockly.WorkspaceSvg.prototype.scroll = function(x, y) { metrics.contentWidth); y = Math.max(y, metrics.viewHeight - metrics.contentTop - metrics.contentHeight); - // When the workspace starts scrolling, hide the WidgetDiv without animation. - // This is to prevent a dispoal animation from happening in the wrong location. + // When the workspace starts scrolling, hide the WidgetDiv without animation. + // This is to prevent a dispoal animation from happening in the wrong location. Blockly.WidgetDiv.hide(true); Blockly.DropDownDiv.hideWithoutAnimation(); // Move the scrollbars and the page will scroll automatically. - this.scrollbar.set(-x - metrics.contentLeft, - -y - metrics.contentTop); + this.scrollbar.set(-x - metrics.contentLeft, -y - metrics.contentTop); }; /** @@ -1715,8 +1714,7 @@ Blockly.WorkspaceSvg.prototype.updateStackGlowScale_ = function() { // No such def in the flyout workspace. if (this.options.stackGlowBlur) { this.options.stackGlowBlur.setAttribute('stdDeviation', - Blockly.Colours.stackGlowSize / this.scale - ); + Blockly.Colours.stackGlowSize / this.scale); } }; diff --git a/core/xml.js b/core/xml.js index 546ff074f7..6326f06ac2 100644 --- a/core/xml.js +++ b/core/xml.js @@ -620,21 +620,20 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { var input; // Find any enclosed blocks or shadows in this tag. - var childBlockNode = null; - var childShadowNode = null; - for (var j = 0, grandchildNode; grandchildNode = xmlChild.childNodes[j]; - j++) { - if (grandchildNode.nodeType == 1) { - if (grandchildNode.nodeName.toLowerCase() == 'block') { - childBlockNode = grandchildNode; - } else if (grandchildNode.nodeName.toLowerCase() == 'shadow') { - childShadowNode = grandchildNode; + var childBlockElement = null; + var childShadowElement = null; + for (var j = 0, grandchild; grandchild = xmlChild.childNodes[j]; j++) { + if (grandchild.nodeType == 1) { + if (grandchild.nodeName.toLowerCase() == 'block') { + childBlockElement = /** @type {!Element} */ (grandchild); + } else if (grandchild.nodeName.toLowerCase() == 'shadow') { + childShadowElement = /** @type {!Element} */ (grandchild); } } } // Use the shadow block if there is no child block. - if (!childBlockNode && childShadowNode) { - childBlockNode = childShadowNode; + if (!childBlockElement && childShadowElement) { + childBlockElement = childShadowElement; } var name = xmlChild.getAttribute('name'); @@ -685,11 +684,11 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { prototypeName); break; } - if (childShadowNode) { - input.connection.setShadowDom(childShadowNode); + if (childShadowElement) { + input.connection.setShadowDom(childShadowElement); } - if (childBlockNode) { - blockChild = Blockly.Xml.domToBlockHeadless_(childBlockNode, + if (childBlockElement) { + blockChild = Blockly.Xml.domToBlockHeadless_(childBlockElement, workspace); if (blockChild.outputConnection) { input.connection.connect(blockChild.outputConnection); @@ -702,16 +701,16 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { } break; case 'next': - if (childShadowNode && block.nextConnection) { - block.nextConnection.setShadowDom(childShadowNode); + if (childShadowElement && block.nextConnection) { + block.nextConnection.setShadowDom(childShadowElement); } - if (childBlockNode) { + if (childBlockElement) { goog.asserts.assert(block.nextConnection, 'Next statement does not exist.'); // If there is more than one XML 'next' tag. goog.asserts.assert(!block.nextConnection.isConnected(), 'Next statement is already connected.'); - blockChild = Blockly.Xml.domToBlockHeadless_(childBlockNode, + blockChild = Blockly.Xml.domToBlockHeadless_(childBlockElement, workspace); goog.asserts.assert(blockChild.previousConnection, 'Next block does not have previous statement.'); @@ -787,7 +786,7 @@ Blockly.Xml.domToFieldVariable_ = function(workspace, xml, text, field) { } if (!variable) { variable = Blockly.Variables.getOrCreateVariablePackage(workspace, xml.id, - text, type); + text, type); } // This should never happen :) From fc0da3e070da9b5479d717792fdd94578d40ce91 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 2 May 2018 15:32:28 -0700 Subject: [PATCH 0484/2135] All remaining lint in core --- core/block.js | 6 +-- core/block_drag_surface.js | 17 ++++---- core/block_render_svg_horizontal.js | 18 ++++----- core/block_render_svg_vertical.js | 23 +++++------ core/block_svg.js | 2 +- core/blockly.js | 14 +++---- core/connection.js | 8 ++-- core/data_category.js | 30 +++++++-------- core/dragged_connection_manager.js | 4 +- core/dropdowndiv.js | 10 ++--- core/field.js | 24 ++++++------ core/field_angle.js | 24 ++++++------ core/field_colour_slider.js | 42 +++++++++----------- core/field_dropdown.js | 21 +++++----- core/field_iconmenu.js | 9 +++-- core/field_image.js | 2 +- core/field_label.js | 13 ++++--- core/field_number.js | 2 +- core/field_numberdropdown.js | 4 +- core/field_textdropdown.js | 24 ++++++------ core/field_textinput.js | 18 ++++----- core/field_variable.js | 10 +++-- core/field_vertical_separator.js | 19 ++++----- core/flyout_horizontal.js | 4 +- core/flyout_vertical.js | 60 +++++++++++++++-------------- core/gesture.js | 18 ++++----- core/inject.js | 18 ++++++--- core/scratch_blocks_utils.js | 2 +- core/workspace_svg.js | 2 +- 29 files changed, 225 insertions(+), 223 deletions(-) diff --git a/core/block.js b/core/block.js index 4d69b8263d..4cdcbf4b71 100644 --- a/core/block.js +++ b/core/block.js @@ -790,15 +790,13 @@ Blockly.Block.prototype.setColour = function(colour, colourSecondary, colourTert this.colourSecondary_ = this.makeColour_(colourSecondary); } else { this.colourSecondary_ = goog.color.rgbArrayToHex( - goog.color.darken(goog.color.hexToRgb(this.colour_), - 0.1)); + goog.color.darken(goog.color.hexToRgb(this.colour_), 0.1)); } if (colourTertiary !== undefined) { this.colourTertiary_ = this.makeColour_(colourTertiary); } else { this.colourTertiary_ = goog.color.rgbArrayToHex( - goog.color.darken(goog.color.hexToRgb(this.colour_), - 0.2)); + goog.color.darken(goog.color.hexToRgb(this.colour_), 0.2)); } if (this.rendered) { this.updateColour(); diff --git a/core/block_drag_surface.js b/core/block_drag_surface.js index 0195c19f6a..a3995f879d 100644 --- a/core/block_drag_surface.js +++ b/core/block_drag_surface.js @@ -112,13 +112,14 @@ Blockly.BlockDragSurfaceSvg.prototype.createDom = function() { if (this.SVG_) { return; // Already created. } - this.SVG_ = Blockly.utils.createSvgElement('svg', { - 'xmlns': Blockly.SVG_NS, - 'xmlns:html': Blockly.HTML_NS, - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'class': 'blocklyBlockDragSurface' - }, this.container_); + this.SVG_ = Blockly.utils.createSvgElement('svg', + { + 'xmlns': Blockly.SVG_NS, + 'xmlns:html': Blockly.HTML_NS, + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + 'version': '1.1', + 'class': 'blocklyBlockDragSurface' + }, this.container_); this.dragGroup_ = Blockly.utils.createSvgElement('g', {}, this.SVG_); // Belongs in Scratch Blocks, but not Blockly. var defs = Blockly.utils.createSvgElement('defs', {}, this.SVG_); @@ -152,7 +153,7 @@ Blockly.BlockDragSurfaceSvg.prototype.createDropShadowDom_ = function(defs) { }, dragShadowFilter); var componentTransfer = Blockly.utils.createSvgElement( - 'feComponentTransfer', {'result': 'offsetBlur'}, dragShadowFilter); + 'feComponentTransfer', {'result': 'offsetBlur'}, dragShadowFilter); // Shadow opacity is specified in the adjustable colour library, // since the darkness of the shadow largely depends on the workspace colour. Blockly.utils.createSvgElement('feFuncA', diff --git a/core/block_render_svg_horizontal.js b/core/block_render_svg_horizontal.js index 51962a0311..a74dea0b66 100644 --- a/core/block_render_svg_horizontal.js +++ b/core/block_render_svg_horizontal.js @@ -679,10 +679,10 @@ Blockly.BlockSvg.prototype.renderDrawLeft_ = function(steps, connectionsXY, metr steps.push('m', metrics.fieldRadius + ',0'); // Top-left rounded corner. steps.push( - 'A', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', '0,' + metrics.fieldRadius); + 'A', metrics.fieldRadius + ',' + metrics.fieldRadius, + '0', '0,0', '0,' + metrics.fieldRadius); steps.push( - 'V', metrics.height - metrics.fieldRadius); + 'V', metrics.height - metrics.fieldRadius); } }; @@ -710,8 +710,8 @@ Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps, } else { // Input steps.push( - 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', metrics.fieldRadius + ',' + metrics.fieldRadius); + 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, + '0', '0,0', metrics.fieldRadius + ',' + metrics.fieldRadius); } // Has statement @@ -787,8 +787,8 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, connectionsXY, met } else { // Input steps.push( - 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', metrics.fieldRadius + ',' + -1 * metrics.fieldRadius); + 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, + '0', '0,0', metrics.fieldRadius + ',' + -1 * metrics.fieldRadius); steps.push('v', -1 * (metrics.height - metrics.fieldRadius * 2)); } @@ -833,8 +833,8 @@ Blockly.BlockSvg.prototype.renderDrawTop_ = function(steps, connectionsXY, metri Blockly.BlockSvg.CORNER_RADIUS); } else { steps.push( - 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', '-' + metrics.fieldRadius + ',-' + metrics.fieldRadius); + 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, + '0', '0,0', '-' + metrics.fieldRadius + ',-' + metrics.fieldRadius); } steps.push('z'); }; diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js index 6e20230715..f955567887 100644 --- a/core/block_render_svg_vertical.js +++ b/core/block_render_svg_vertical.js @@ -672,9 +672,8 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) { * @return {number} X-coordinate of the end of the field row (plus a gap). * @private */ -Blockly.BlockSvg.prototype.renderFields_ = - function(fieldList, cursorX, cursorY) { - /* eslint-disable indent */ +Blockly.BlockSvg.prototype.renderFields_ = function(fieldList, cursorX, + cursorY) { if (this.RTL) { cursorX = -cursorX; } @@ -738,8 +737,7 @@ Blockly.BlockSvg.prototype.renderFields_ = translateX += field.renderWidth; } root.setAttribute('transform', - 'translate(' + translateX + ', ' + translateY + ') ' + scale - ); + 'translate(' + translateX + ', ' + translateY + ') ' + scale); // Fields are invisible on insertion marker. if (this.isInsertionMarker()) { @@ -747,7 +745,7 @@ Blockly.BlockSvg.prototype.renderFields_ = } } return this.RTL ? -cursorX : cursorX; -}; /* eslint-enable indent */ +}; /** * Computes the height and widths for each row and field. @@ -1198,7 +1196,6 @@ Blockly.BlockSvg.prototype.renderClassify_ = function() { * @private */ Blockly.BlockSvg.prototype.renderDrawTop_ = function(steps, rightEdge) { - /* eslint-disable indent */ if (this.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { steps.push('m 0, 0'); steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_DEFINE_HAT); @@ -1231,7 +1228,7 @@ Blockly.BlockSvg.prototype.renderDrawTop_ = function(steps, rightEdge) { } } this.width = rightEdge; -}; /* eslint-enable indent */ +}; /** * Render the right edge of the block. @@ -1379,8 +1376,7 @@ Blockly.BlockSvg.prototype.renderInputShape_ = function(input, x, y) { inputShapeY = y - (Blockly.BlockSvg.INPUT_SHAPE_HEIGHT / 2); inputShape.setAttribute('d', inputShapeInfo.path); inputShape.setAttribute('transform', - 'translate(' + inputShapeX + ',' + inputShapeY + ')' - ); + 'translate(' + inputShapeX + ',' + inputShapeY + ')'); inputShape.setAttribute('data-argument-type', inputShapeInfo.argType); inputShape.setAttribute('style', 'visibility: visible'); } @@ -1481,9 +1477,8 @@ Blockly.BlockSvg.prototype.drawEdgeShapeRight_ = function(steps) { * @param {!Blockly.Connection} existingConnection The connection on the * existing block, which newBlock should line up with. */ -Blockly.BlockSvg.prototype.positionNewBlock = - function(newBlock, newConnection, existingConnection) { - /* eslint-disable indent */ +Blockly.BlockSvg.prototype.positionNewBlock = function(newBlock, newConnection, + existingConnection) { // We only need to position the new block if it's before the existing one, // otherwise its position is set by the previous block. if (newConnection.type == Blockly.NEXT_STATEMENT) { @@ -1492,7 +1487,7 @@ Blockly.BlockSvg.prototype.positionNewBlock = newBlock.moveBy(dx, dy); } -}; /* eslint-enable indent */ +}; /** * Draw the outline of a statement input, starting at the top right corner. diff --git a/core/block_svg.js b/core/block_svg.js index 4caf037e25..0b0f3e47cd 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -304,7 +304,7 @@ Blockly.BlockSvg.prototype.setParent = function(newParent) { // If we are a shadow block, inherit tertiary colour. if (this.isShadow()) { this.setColour(this.getColour(), this.getColourSecondary(), - newParent.getColourTertiary()); + newParent.getColourTertiary()); } } }; diff --git a/core/blockly.js b/core/blockly.js index 8c65520a4f..4274f1ce29 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -444,10 +444,9 @@ Blockly.bindEventWithChecks_ = function(node, name, thisObject, func, e.preventDefault(); } }; - for (var i = 0, eventName; - eventName = Blockly.Touch.TOUCH_MAP[name][i]; i++) { - node.addEventListener(eventName, touchWrapFunc, false); - bindData.push([node, eventName, touchWrapFunc]); + for (var i = 0, type; type = Blockly.Touch.TOUCH_MAP[name][i]; i++) { + node.addEventListener(type, touchWrapFunc, false); + bindData.push([node, type, touchWrapFunc]); } } return bindData; @@ -494,10 +493,9 @@ Blockly.bindEvent_ = function(node, name, thisObject, func) { // Stop the browser from scrolling/zooming the page. e.preventDefault(); }; - for (var i = 0, eventName; - eventName = Blockly.Touch.TOUCH_MAP[name][i]; i++) { - node.addEventListener(eventName, touchWrapFunc, false); - bindData.push([node, eventName, touchWrapFunc]); + for (var i = 0, type; type = Blockly.Touch.TOUCH_MAP[name][i]; i++) { + node.addEventListener(type, touchWrapFunc, false); + bindData.push([node, type, touchWrapFunc]); } } return bindData; diff --git a/core/connection.js b/core/connection.js index fad934db58..a34319ffd5 100644 --- a/core/connection.js +++ b/core/connection.js @@ -455,10 +455,10 @@ Blockly.Connection.prototype.isConnectionAllowed = function(candidate) { break; } case Blockly.NEXT_STATEMENT: { - // Scratch-specific behaviour: - // If this is a c-block, we can't connect this block's - // previous connection unless we're connecting to the end of the last - // block on a stack or there's already a block connected inside the c. + // Scratch-specific behaviour: + // If this is a c-block, we can't connect this block's + // previous connection unless we're connecting to the end of the last + // block on a stack or there's already a block connected inside the c. if (firstStatementConnection && this == this.sourceBlock_.previousConnection && candidate.isConnectedToNonInsertionMarker() && diff --git a/core/data_category.js b/core/data_category.js index 5905486150..b7e5443dcb 100644 --- a/core/data_category.js +++ b/core/data_category.js @@ -120,7 +120,7 @@ Blockly.DataCategory.addSetVariableTo = function(xmlList, variable) { // //
Blockly.DataCategory.addBlock(xmlList, variable, 'data_setvariableto', - 'VARIABLE', ['VALUE', 'text', 0]); + 'VARIABLE', ['VALUE', 'text', 0]); }; /** @@ -140,7 +140,7 @@ Blockly.DataCategory.addChangeVariableBy = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_changevariableby', - 'VARIABLE', ['VALUE', 'math_number', 1]); + 'VARIABLE', ['VALUE', 'math_number', 1]); }; /** @@ -155,7 +155,7 @@ Blockly.DataCategory.addShowVariable = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_showvariable', - 'VARIABLE'); + 'VARIABLE'); }; /** @@ -170,7 +170,7 @@ Blockly.DataCategory.addHideVariable = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_hidevariable', - 'VARIABLE'); + 'VARIABLE'); }; /** @@ -202,7 +202,7 @@ Blockly.DataCategory.addAddToList = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_addtolist', 'LIST', - ['ITEM', 'text', 'thing']); + ['ITEM', 'text', 'thing']); }; /** @@ -220,7 +220,7 @@ Blockly.DataCategory.addDeleteOfList = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_deleteoflist', 'LIST', - ['INDEX', 'math_integer', 1]); + ['INDEX', 'math_integer', 1]); }; /** @@ -243,7 +243,7 @@ Blockly.DataCategory.addInsertAtList = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_insertatlist', 'LIST', - ['INDEX', 'math_integer', 1], ['ITEM', 'text', 'thing']); + ['INDEX', 'math_integer', 1], ['ITEM', 'text', 'thing']); }; /** @@ -266,7 +266,7 @@ Blockly.DataCategory.addReplaceItemOfList = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_replaceitemoflist', - 'LIST', ['INDEX', 'math_integer', 1], ['ITEM', 'text', 'thing']); + 'LIST', ['INDEX', 'math_integer', 1], ['ITEM', 'text', 'thing']); }; /** @@ -284,7 +284,7 @@ Blockly.DataCategory.addItemOfList = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemoflist', 'LIST', - ['INDEX', 'math_integer', 1]); + ['INDEX', 'math_integer', 1]); }; /** @@ -314,7 +314,7 @@ Blockly.DataCategory.addListContainsItem = function(xmlList, variable) { // // Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontainsitem', - 'LIST', ['ITEM', 'text', 'thing']); + 'LIST', ['ITEM', 'text', 'thing']); }; /** @@ -361,7 +361,7 @@ Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) { callbackKey = 'CREATE_LIST'; callback = function(button) { Blockly.Variables.createVariable(button.getTargetWorkspace(), null, - Blockly.LIST_VARIABLE_TYPE);}; + Blockly.LIST_VARIABLE_TYPE);}; } button.setAttribute('text', msg); button.setAttribute('callbackKey', callbackKey); @@ -383,18 +383,18 @@ Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) { * @param {?Array.} opt_secondValue Optional array containing the value * name and shadow type of a second pair of value tags. */ -Blockly.DataCategory.addBlock = function(xmlList, - variable, blockType, fieldName, opt_value, opt_secondValue) { +Blockly.DataCategory.addBlock = function(xmlList, variable, blockType, + fieldName, opt_value, opt_secondValue) { if (Blockly.Blocks[blockType]) { var firstValueField; var secondValueField; if (opt_value) { firstValueField = Blockly.DataCategory.createValue(opt_value[0], - opt_value[1], opt_value[2]); + opt_value[1], opt_value[2]); } if (opt_secondValue) { secondValueField = Blockly.DataCategory.createValue(opt_secondValue[0], - opt_secondValue[1], opt_value[2]); + opt_secondValue[1], opt_value[2]); } var gap = 8; diff --git a/core/dragged_connection_manager.js b/core/dragged_connection_manager.js index dd25490ac5..50d0a15902 100644 --- a/core/dragged_connection_manager.js +++ b/core/dragged_connection_manager.js @@ -173,8 +173,8 @@ Blockly.DraggedConnectionManager.prototype.update = function(dxy, deleteArea, is this.closestConnection_ = null; } - // Prefer connecting over dropping into the trash can, but prefer dragging to - // the toolbox over connecting to other blocks. + // Prefer connecting over dropping into the trash can, but prefer dragging to + // the toolbox over connecting to other blocks. var wouldConnect = !!this.closestConnection_ && deleteArea != Blockly.DELETE_AREA_TOOLBOX; var wouldDelete = !!deleteArea && !this.topBlock_.getParent() && diff --git a/core/dropdowndiv.js b/core/dropdowndiv.js index d760549a00..f0847dd963 100644 --- a/core/dropdowndiv.js +++ b/core/dropdowndiv.js @@ -183,7 +183,7 @@ Blockly.DropDownDiv.setCategory = function(category) { * @return {boolean} True if the menu rendered below block; false if above. */ Blockly.DropDownDiv.showPositionedByBlock = function(owner, block, - opt_onHide, opt_secondaryYOffset) { + opt_onHide, opt_secondaryYOffset) { var scale = block.workspace.scale; var bBox = {width: block.width, height: block.height}; bBox.width *= scale; @@ -306,16 +306,16 @@ Blockly.DropDownDiv.getPositionMetrics = function(primaryX, primaryY, secondaryX renderX -= centerX; // Fit horizontally in the bounds. renderX = Math.max( - boundPosition.left, - Math.min(renderX, boundPosition.left + boundSize.width - divSize.width) + boundPosition.left, + Math.min(renderX, boundPosition.left + boundSize.width - divSize.width) ); // After we've finished caclulating renderX, adjust the arrow to be relative to it. arrowX -= renderX; // Pad the arrow by some pixels, primarily so that it doesn't render on top of a rounded border. arrowX = Math.max( - Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING, - Math.min(arrowX, divSize.width - Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING - Blockly.DropDownDiv.ARROW_SIZE) + Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING, + Math.min(arrowX, divSize.width - Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING - Blockly.DropDownDiv.ARROW_SIZE) ); // Calculate arrow Y. If we rendered secondary, add on bottom. diff --git a/core/field.js b/core/field.js index 8a9cf67222..2e08836739 100644 --- a/core/field.js +++ b/core/field.js @@ -48,8 +48,8 @@ goog.require('goog.userAgent'); */ Blockly.Field = function(text, opt_validator) { this.size_ = new goog.math.Size( - Blockly.BlockSvg.FIELD_WIDTH, - Blockly.BlockSvg.FIELD_HEIGHT); + Blockly.BlockSvg.FIELD_WIDTH, + Blockly.BlockSvg.FIELD_HEIGHT); this.setValue(text); this.setValidator(opt_validator); @@ -236,22 +236,22 @@ Blockly.Field.prototype.init = function() { var fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2; /** @type {!Element} */ this.textElement_ = Blockly.utils.createSvgElement('text', - {'class': this.className_, - 'x': fieldX, - 'y': size.height / 2 + Blockly.BlockSvg.FIELD_TOP_PADDING, - 'dominant-baseline': 'middle', - 'dy': goog.userAgent.EDGE_OR_IE ? Blockly.Field.IE_TEXT_OFFSET : '0', - 'text-anchor': 'middle'}, - this.fieldGroup_); + { + 'class': this.className_, + 'x': fieldX, + 'y': size.height / 2 + Blockly.BlockSvg.FIELD_TOP_PADDING, + 'dominant-baseline': 'middle', + 'dy': goog.userAgent.EDGE_OR_IE ? Blockly.Field.IE_TEXT_OFFSET : '0', + 'text-anchor': 'middle' + }, this.fieldGroup_); this.updateEditable(); this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); // Force a render. this.render_(); this.size_.width = 0; - this.mouseDownWrapper_ = - Blockly.bindEventWithChecks_(this.getClickTarget_(), 'mousedown', this, - this.onMouseDown_); + this.mouseDownWrapper_ = Blockly.bindEventWithChecks_( + this.getClickTarget_(), 'mousedown', this, this.onMouseDown_); }; /** diff --git a/core/field_angle.js b/core/field_angle.js index e16c4d33f0..e19cfd3c77 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -224,20 +224,18 @@ Blockly.FieldAngle.prototype.showEditor_ = function() { 'r': Blockly.FieldAngle.HANDLE_RADIUS, 'class': 'blocklyAngleDragHandle' }, this.handle_); - this.arrowSvg_ = Blockly.utils.createSvgElement( - 'image', - { - 'width': Blockly.FieldAngle.ARROW_WIDTH, - 'height': Blockly.FieldAngle.ARROW_WIDTH, - 'x': -Blockly.FieldAngle.ARROW_WIDTH / 2, - 'y': -Blockly.FieldAngle.ARROW_WIDTH / 2 - }, - this.handle_ - ); + this.arrowSvg_ = Blockly.utils.createSvgElement('image', + { + 'width': Blockly.FieldAngle.ARROW_WIDTH, + 'height': Blockly.FieldAngle.ARROW_WIDTH, + 'x': -Blockly.FieldAngle.ARROW_WIDTH / 2, + 'y': -Blockly.FieldAngle.ARROW_WIDTH / 2 + }, + this.handle_); this.arrowSvg_.setAttributeNS( - 'http://www.w3.org/1999/xlink', - 'xlink:href', - Blockly.mainWorkspace.options.pathToMedia + Blockly.FieldAngle.ARROW_SVG_PATH + 'http://www.w3.org/1999/xlink', + 'xlink:href', + Blockly.mainWorkspace.options.pathToMedia + Blockly.FieldAngle.ARROW_SVG_PATH ); Blockly.DropDownDiv.setColour(this.sourceBlock_.parentBlock_.getColour(), diff --git a/core/field_colour_slider.js b/core/field_colour_slider.js index 7123e4b0fe..0bc4d2de2d 100644 --- a/core/field_colour_slider.js +++ b/core/field_colour_slider.js @@ -152,15 +152,15 @@ Blockly.FieldColourSlider.prototype.createColourStops_ = function(channel) { Blockly.FieldColourSlider.prototype.setGradient_ = function(node, channel) { var gradient = this.createColourStops_(channel).join(','); goog.style.setStyle(node, 'background', - '-moz-linear-gradient(left, ' + gradient + ')'); + '-moz-linear-gradient(left, ' + gradient + ')'); goog.style.setStyle(node, 'background', - '-webkit-linear-gradient(left, ' + gradient + ')'); + '-webkit-linear-gradient(left, ' + gradient + ')'); goog.style.setStyle(node, 'background', - '-o-linear-gradient(left, ' + gradient + ')'); + '-o-linear-gradient(left, ' + gradient + ')'); goog.style.setStyle(node, 'background', - '-ms-linear-gradient(left, ' + gradient + ')'); + '-ms-linear-gradient(left, ' + gradient + ')'); goog.style.setStyle(node, 'background', - 'linear-gradient(left, ' + gradient + ')'); + 'linear-gradient(left, ' + gradient + ')'); }; /** @@ -250,7 +250,7 @@ Blockly.FieldColourSlider.prototype.sliderCallbackFactory_ = function(channel) { } var colour = goog.color.hsvToHex(hsv[0], hsv[1], hsv[2]); if (thisField.sourceBlock_) { - // Call any validation function, and allow it to override. + // Call any validation function, and allow it to override. colour = thisField.callValidator(colour); } if (colour !== null) { @@ -301,9 +301,8 @@ Blockly.FieldColourSlider.prototype.showEditor_ = function() { this.hueSlider_.setMoveToPointEnabled(true); this.hueSlider_.render(div); - var saturationElements = this.createLabelDom_( - Blockly.Msg.COLOUR_SATURATION_LABEL - ); + var saturationElements = + this.createLabelDom_(Blockly.Msg.COLOUR_SATURATION_LABEL); div.appendChild(saturationElements[0]); this.saturationReadout_ = saturationElements[1]; this.saturationSlider_ = new goog.ui.Slider(); @@ -314,9 +313,8 @@ Blockly.FieldColourSlider.prototype.showEditor_ = function() { this.saturationSlider_.setMaximum(1.0); this.saturationSlider_.render(div); - var brightnessElements = this.createLabelDom_( - Blockly.Msg.COLOUR_BRIGHTNESS_LABEL - ); + var brightnessElements = + this.createLabelDom_(Blockly.Msg.COLOUR_BRIGHTNESS_LABEL); div.appendChild(brightnessElements[0]); this.brightnessReadout_ = brightnessElements[1]; this.brightnessSlider_ = new goog.ui.Slider(); @@ -327,14 +325,14 @@ Blockly.FieldColourSlider.prototype.showEditor_ = function() { this.brightnessSlider_.render(div); Blockly.FieldColourSlider.hueChangeEventKey_ = goog.events.listen(this.hueSlider_, - goog.ui.Component.EventType.CHANGE, - this.sliderCallbackFactory_('hue')); + goog.ui.Component.EventType.CHANGE, + this.sliderCallbackFactory_('hue')); Blockly.FieldColourSlider.saturationChangeEventKey_ = goog.events.listen(this.saturationSlider_, - goog.ui.Component.EventType.CHANGE, - this.sliderCallbackFactory_('saturation')); + goog.ui.Component.EventType.CHANGE, + this.sliderCallbackFactory_('saturation')); Blockly.FieldColourSlider.brightnessChangeEventKey_ = goog.events.listen(this.brightnessSlider_, - goog.ui.Component.EventType.CHANGE, - this.sliderCallbackFactory_('brightness')); + goog.ui.Component.EventType.CHANGE, + this.sliderCallbackFactory_('brightness')); if (Blockly.FieldColourSlider.activateEyedropper_) { var button = document.createElement('button'); @@ -343,11 +341,9 @@ Blockly.FieldColourSlider.prototype.showEditor_ = function() { image.src = Blockly.mainWorkspace.options.pathToMedia + Blockly.FieldColourSlider.EYEDROPPER_PATH; button.appendChild(image); div.appendChild(button); - Blockly.FieldColourSlider.eyedropperEventData_ = Blockly.bindEventWithChecks_(button, - 'mousedown', - this, - this.activateEyedropperInternal_ - ); + Blockly.FieldColourSlider.eyedropperEventData_ = + Blockly.bindEventWithChecks_(button, 'mousedown', this, + this.activateEyedropperInternal_); } Blockly.DropDownDiv.setColour('#ffffff', '#dddddd'); diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 6d12654518..c5394fe1c1 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -221,10 +221,10 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() { // Activate the menu item. control.performActionInternal(e); } - menu.getHandler().listen(menu.getElement(), goog.events.EventType.TOUCHSTART, - callbackTouchStart); - menu.getHandler().listen(menu.getElement(), goog.events.EventType.TOUCHEND, - callbackTouchEnd); + menu.getHandler().listen( + menu.getElement(), goog.events.EventType.TOUCHSTART, callbackTouchStart); + menu.getHandler().listen( + menu.getElement(), goog.events.EventType.TOUCHEND, callbackTouchEnd); // Record windowSize and scrollOffset before adding menu. menu.render(contentDiv); @@ -256,8 +256,8 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() { var secondaryY = position.top; // Set bounds to workspace; show the drop-down. Blockly.DropDownDiv.setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode); - Blockly.DropDownDiv.show(this, primaryX, primaryY, secondaryX, secondaryY, - this.onHide.bind(this)); + Blockly.DropDownDiv.show( + this, primaryX, primaryY, secondaryX, secondaryY, this.onHide.bind(this)); menu.setAllowAutoFocus(true); menuDom.focus(); @@ -267,7 +267,8 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() { if (this.sourceBlock_.isShadow()) { this.savedPrimary_ = this.sourceBlock_.getColour(); this.sourceBlock_.setColour(this.sourceBlock_.getColourTertiary(), - this.sourceBlock_.getColourSecondary(), this.sourceBlock_.getColourTertiary()); + this.sourceBlock_.getColourSecondary(), + this.sourceBlock_.getColourTertiary()); } else if (this.box_) { this.box_.setAttribute('fill', this.sourceBlock_.getColourTertiary()); } @@ -283,7 +284,8 @@ Blockly.FieldDropdown.prototype.onHide = function() { if (!this.disableColourChange_ && this.sourceBlock_) { if (this.sourceBlock_.isShadow()) { this.sourceBlock_.setColour(this.savedPrimary_, - this.sourceBlock_.getColourSecondary(), this.sourceBlock_.getColourTertiary()); + this.sourceBlock_.getColourSecondary(), + this.sourceBlock_.getColourTertiary()); } else if (this.box_) { this.box_.setAttribute('fill', this.sourceBlock_.getColour()); } @@ -480,8 +482,7 @@ Blockly.FieldDropdown.prototype.positionArrow = function(x) { this.arrowX_ += Blockly.BlockSvg.BOX_FIELD_PADDING; } this.arrow_.setAttribute('transform', - 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')' - ); + 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')'); return addedWidth; }; diff --git a/core/field_iconmenu.js b/core/field_iconmenu.js index b258b356e1..351f8891b9 100644 --- a/core/field_iconmenu.js +++ b/core/field_iconmenu.js @@ -253,7 +253,8 @@ Blockly.FieldIconMenu.prototype.showEditor_ = function() { // Update source block colour to look selected this.savedPrimary_ = this.sourceBlock_.getColour(); this.sourceBlock_.setColour(this.sourceBlock_.getColourSecondary(), - this.sourceBlock_.getColourSecondary(), this.sourceBlock_.getColourTertiary()); + this.sourceBlock_.getColourSecondary(), + this.sourceBlock_.getColourTertiary()); var scale = this.sourceBlock_.workspace.scale; // Offset for icon-type horizontal blocks. @@ -268,8 +269,7 @@ Blockly.FieldIconMenu.prototype.showEditor_ = function() { var arrowY = this.arrowY_ + Blockly.DropDownDiv.ARROW_SIZE / 1.5; // Flip the arrow on the button this.arrowIcon_.setAttribute('transform', - 'translate(' + arrowX + ',' + arrowY + ') rotate(180)'); - } + 'translate(' + arrowX + ',' + arrowY + ') rotate(180)');} }; /** @@ -294,7 +294,8 @@ Blockly.FieldIconMenu.prototype.onHide_ = function() { // when a block is dragged from the flyout. if (this.sourceBlock_) { this.sourceBlock_.setColour(this.savedPrimary_, - this.sourceBlock_.getColourSecondary(), this.sourceBlock_.getColourTertiary()); + this.sourceBlock_.getColourSecondary(), + this.sourceBlock_.getColourTertiary()); } Blockly.DropDownDiv.content_.removeAttribute('role'); Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup'); diff --git a/core/field_image.js b/core/field_image.js index 0b9c4a8789..57fcf30eff 100644 --- a/core/field_image.js +++ b/core/field_image.js @@ -187,7 +187,7 @@ Blockly.FieldImage.prototype.render_ = function() { * @private */ Blockly.FieldImage.prototype.updateWidth = function() { - // NOP + // NOP }; Blockly.Field.register('field_image', Blockly.FieldImage); diff --git a/core/field_label.js b/core/field_label.js index 95e5697963..1565b66368 100644 --- a/core/field_label.js +++ b/core/field_label.js @@ -85,12 +85,13 @@ Blockly.FieldLabel.prototype.init = function() { } // Build the DOM. this.textElement_ = Blockly.utils.createSvgElement('text', - {'class': 'blocklyText', - 'y': Blockly.BlockSvg.FIELD_TOP_PADDING, - 'text-anchor': 'middle', - 'dominant-baseline': 'middle', - 'dy': goog.userAgent.EDGE_OR_IE ? Blockly.Field.IE_TEXT_OFFSET : '0' - }, null); + { + 'class': 'blocklyText', + 'y': Blockly.BlockSvg.FIELD_TOP_PADDING, + 'text-anchor': 'middle', + 'dominant-baseline': 'middle', + 'dy': goog.userAgent.EDGE_OR_IE ? Blockly.Field.IE_TEXT_OFFSET : '0' + }, null); if (this.class_) { Blockly.utils.addClass(this.textElement_, this.class_); } diff --git a/core/field_number.js b/core/field_number.js index 14ec0ace33..646877504d 100644 --- a/core/field_number.js +++ b/core/field_number.js @@ -95,7 +95,7 @@ Blockly.FieldNumber.DROPDOWN_Y_PADDING = 8; * @type {Array.} * @const */ - // Calculator order +// Calculator order Blockly.FieldNumber.NUMPAD_BUTTONS = ['7', '8', '9', '4', '5', '6', '1', '2', '3', '.', '0', '-', ' ']; diff --git a/core/field_numberdropdown.js b/core/field_numberdropdown.js index dd9136389c..27f1c91c04 100644 --- a/core/field_numberdropdown.js +++ b/core/field_numberdropdown.js @@ -50,10 +50,10 @@ Blockly.FieldNumberDropdown = function(value, menuGenerator, opt_min, opt_max, this.setConstraints_ = Blockly.FieldNumber.prototype.setConstraints_; var numRestrictor = Blockly.FieldNumber.prototype.getNumRestrictor.call( - this, opt_min, opt_max, opt_precision + this, opt_min, opt_max, opt_precision ); Blockly.FieldNumberDropdown.superClass_.constructor.call( - this, value, menuGenerator, opt_validator, numRestrictor + this, value, menuGenerator, opt_validator, numRestrictor ); this.addArgType('numberdropdown'); }; diff --git a/core/field_textdropdown.js b/core/field_textdropdown.js index 84c79ff44f..4e192a33f6 100644 --- a/core/field_textdropdown.js +++ b/core/field_textdropdown.js @@ -90,17 +90,17 @@ Blockly.FieldTextDropdown.prototype.init = function() { this.arrowX_ = 0; /** @type {Number} */ this.arrowY_ = 11; - this.arrow_ = Blockly.utils.createSvgElement('image', { - 'height': this.arrowSize_ + 'px', - 'width': this.arrowSize_ + 'px' - }); + this.arrow_ = Blockly.utils.createSvgElement('image', + { + 'height': this.arrowSize_ + 'px', + 'width': this.arrowSize_ + 'px' + }); this.arrow_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow-dark.svg'); this.arrow_.style.cursor = 'pointer'; this.fieldGroup_.appendChild(this.arrow_); this.mouseUpWrapper_ = - Blockly.bindEvent_(this.arrow_, 'mouseup', this, - this.showDropdown_); + Blockly.bindEvent_(this.arrow_, 'mouseup', this, this.showDropdown_); } // Prevent the drop-down handler from changing the field colour on open. this.disableColourChange_ = true; @@ -124,12 +124,12 @@ Blockly.FieldTextDropdown.prototype.dispose = function() { Blockly.FieldTextDropdown.prototype.showEditor_ = function() { if (!this.dropDownOpen_) { Blockly.FieldTextDropdown.superClass_.showEditor_.call(this, null, null, - true, function() { - // When the drop-down arrow is clicked, hide text editor and show drop-down. - Blockly.WidgetDiv.hide(); - this.showDropdown_(); - Blockly.Touch.clearTouchIdentifier(); - }); + true, function() { + // When the drop-down arrow is clicked, hide text editor and show drop-down. + Blockly.WidgetDiv.hide(); + this.showDropdown_(); + Blockly.Touch.clearTouchIdentifier(); + }); } }; diff --git a/core/field_textinput.js b/core/field_textinput.js index 26119845b1..07d5b46e1a 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -248,7 +248,7 @@ Blockly.FieldTextInput.prototype.showEditor_ = function( var dropDownArrow = goog.dom.createDom(goog.dom.TagName.IMG, 'blocklyTextDropDownArrow'); dropDownArrow.setAttribute('src', - Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow-dark.svg'); + Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow-dark.svg'); dropDownArrow.style.width = this.arrowSize_ + 'px'; dropDownArrow.style.height = this.arrowSize_ + 'px'; dropDownArrow.style.top = this.arrowY_ + 'px'; @@ -262,7 +262,7 @@ Blockly.FieldTextInput.prototype.showEditor_ = function( } if (opt_arrowCallback) { htmlInput.dropDownArrowMouseWrapper_ = Blockly.bindEvent_(dropDownArrow, - 'mousedown', this, opt_arrowCallback); + 'mousedown', this, opt_arrowCallback); } div.appendChild(dropDownArrow); } @@ -305,15 +305,15 @@ Blockly.FieldTextInput.prototype.bindEvents_ = function(htmlInput) { // Bind to keydown -- trap Enter without IME and Esc to hide. htmlInput.onKeyDownWrapper_ = Blockly.bindEventWithChecks_(htmlInput, 'keydown', this, - this.onHtmlInputKeyDown_); + this.onHtmlInputKeyDown_); // Bind to keyup -- trap Enter; resize after every keystroke. htmlInput.onKeyUpWrapper_ = Blockly.bindEventWithChecks_(htmlInput, 'keyup', this, - this.onHtmlInputChange_); + this.onHtmlInputChange_); // Bind to keyPress -- repeatedly resize when holding down a key. htmlInput.onKeyPressWrapper_ = Blockly.bindEventWithChecks_(htmlInput, 'keypress', this, - this.onHtmlInputChange_); + this.onHtmlInputChange_); // For modern browsers (IE 9+, Chrome, Firefox, etc.) that support the // DOM input event, also trigger onHtmlInputChange_ then. The input event // is triggered on keypress but after the value of the text input @@ -460,10 +460,10 @@ Blockly.FieldTextInput.prototype.resizeEditor_ = function() { if (Blockly.BlockSvg.FIELD_TEXTINPUT_EXPAND_PAST_TRUNCATION) { // Resize the box based on the measured width of the text, pre-truncation var textWidth = Blockly.scratchBlocksUtils.measureText( - Blockly.FieldTextInput.htmlInput_.style.fontSize, - Blockly.FieldTextInput.htmlInput_.style.fontFamily, - Blockly.FieldTextInput.htmlInput_.style.fontWeight, - Blockly.FieldTextInput.htmlInput_.value + Blockly.FieldTextInput.htmlInput_.style.fontSize, + Blockly.FieldTextInput.htmlInput_.style.fontFamily, + Blockly.FieldTextInput.htmlInput_.style.fontWeight, + Blockly.FieldTextInput.htmlInput_.value ); // Size drawn in the canvas needs padding and scaling textWidth += Blockly.FieldTextInput.TEXT_MEASURE_PADDING_MAGIC; diff --git a/core/field_variable.js b/core/field_variable.js index c5ce7b949a..be772c1d28 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -309,12 +309,16 @@ Blockly.FieldVariable.dropdownCreate = function() { options[i] = [variableModelList[i].name, variableModelList[i].getId()]; } if (this.defaultType_ == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) { - options.push([Blockly.Msg.NEW_BROADCAST_MESSAGE, Blockly.NEW_BROADCAST_MESSAGE_ID]); + options.push( + [Blockly.Msg.NEW_BROADCAST_MESSAGE, Blockly.NEW_BROADCAST_MESSAGE_ID]); } else { options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]); if (Blockly.Msg.DELETE_VARIABLE) { - options.push([Blockly.Msg.DELETE_VARIABLE.replace('%1', name), - Blockly.DELETE_VARIABLE_ID]); + options.push( + [ + Blockly.Msg.DELETE_VARIABLE.replace('%1', name), + Blockly.DELETE_VARIABLE_ID + ]); } } diff --git a/core/field_vertical_separator.js b/core/field_vertical_separator.js index c25203e6a0..cde897d61f 100644 --- a/core/field_vertical_separator.js +++ b/core/field_vertical_separator.js @@ -77,14 +77,15 @@ Blockly.FieldVerticalSeparator.prototype.init = function() { this.fieldGroup_.style.display = 'none'; } /** @type {SVGElement} */ - this.lineElement_ = Blockly.utils.createSvgElement('line', { - 'stroke': this.sourceBlock_.getColourSecondary(), - 'stroke-linecap': 'round', - 'x1': 0, - 'y1': 0, - 'x2': 0, - 'y2': this.height_ - }, this.fieldGroup_); + this.lineElement_ = Blockly.utils.createSvgElement('line', + { + 'stroke': this.sourceBlock_.getColourSecondary(), + 'stroke-linecap': 'round', + 'x1': 0, + 'y1': 0, + 'x2': 0, + 'y2': this.height_ + }, this.fieldGroup_); this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); }; @@ -153,7 +154,7 @@ Blockly.FieldVerticalSeparator.prototype.render_ = function() { * @private */ Blockly.FieldVerticalSeparator.prototype.updateWidth = function() { - // NOP + // NOP }; Blockly.Field.register( diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js index 1316a13abd..fa3d7cd173 100644 --- a/core/flyout_horizontal.js +++ b/core/flyout_horizontal.js @@ -230,7 +230,7 @@ Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(width, height) path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, this.CORNER_RADIUS, -this.CORNER_RADIUS); path.push('h', width); - // Right. + // Right. path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, this.CORNER_RADIUS, this.CORNER_RADIUS); path.push('v', height); @@ -365,7 +365,7 @@ Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) { // Clicking on a flyout button or label is a lot like clicking on the // flyout background. this.listeners_.push(Blockly.bindEventWithChecks_(buttonSvg, 'mousedown', - this, this.onMouseDown_)); + this, this.onMouseDown_)); this.buttons_.push(button); diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index 489e6c6059..13b9dc1474 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -2,7 +2,7 @@ * @license * Visual Blocks Editor * - * Copyright 2011 Google Inc. + * Copyright 2017 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ goog.require('goog.events'); goog.require('goog.math.Rect'); goog.require('goog.userAgent'); + /** * Class for a flyout. * @param {!Object} workspaceOptions Dictionary of options for the workspace. @@ -51,7 +52,7 @@ Blockly.VerticalFlyout = function(workspaceOptions) { Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions); /** - * Flyout should be laid out horizontally vs vertically. + * Flyout should be laid out vertically. * @type {boolean} * @private */ @@ -154,15 +155,16 @@ Blockly.VerticalFlyout.prototype.createDom = function(tagName) { var clipPath = Blockly.utils.createSvgElement('clipPath', {'id':'blocklyBlockMenuClipPath'}, this.defs_); this.clipRect_ = Blockly.utils.createSvgElement('rect', - {'id': 'blocklyBlockMenuClipRect', + { + 'id': 'blocklyBlockMenuClipRect', 'height': '0', 'width': '0', 'y': '0', 'x': '0' }, clipPath); - this.workspace_.svgGroup_.setAttribute('clip-path', - 'url(#blocklyBlockMenuClipPath)'); + this.workspace_.svgGroup_.setAttribute( + 'clip-path', 'url(#blocklyBlockMenuClipPath)'); return this.svgGroup_; }; @@ -477,8 +479,8 @@ Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) { button.show(); // Clicking on a flyout button or label is a lot like clicking on the // flyout background. - this.listeners_.push(Blockly.bindEventWithChecks_(buttonSvg, 'mousedown', - this, this.onMouseDown_)); + this.listeners_.push(Blockly.bindEventWithChecks_( + buttonSvg, 'mousedown', this, this.onMouseDown_)); this.buttons_.push(button); cursorY += button.height + gaps[i]; @@ -504,13 +506,13 @@ Blockly.VerticalFlyout.prototype.createRect_ = function(block, x, y, // Create an invisible rectangle under the block to act as a button. Just // using the block as a button is poor, since blocks have holes in them. var rect = Blockly.utils.createSvgElement('rect', - { - 'fill-opacity': 0, - 'x': x, - 'y': y, - 'height': blockHW.height, - 'width': blockHW.width - }, null); + { + 'fill-opacity': 0, + 'x': x, + 'y': y, + 'height': blockHW.height, + 'width': blockHW.width + }, null); rect.tooltip = block; Blockly.Tooltip.bindMouseEvents(rect); // Add the rectangles under the blocks, so that the blocks' tooltips work. @@ -531,29 +533,29 @@ Blockly.VerticalFlyout.prototype.createRect_ = function(block, x, y, * @private */ Blockly.VerticalFlyout.prototype.createCheckbox_ = function(block, cursorX, - cursorY, blockHW) { + cursorY, blockHW) { var checkboxState = Blockly.VerticalFlyout.getCheckboxState(block.id); var svgRoot = block.getSvgRoot(); var extraSpace = this.CHECKBOX_SIZE + this.CHECKBOX_MARGIN; var width = this.RTL ? this.getWidth() / this.workspace_.scale - extraSpace : cursorX; var height = cursorY + blockHW.height / 2 - this.CHECKBOX_SIZE / 2; var checkboxGroup = Blockly.utils.createSvgElement('g', - { - 'class': 'blocklyFlyoutCheckbox', - 'transform': 'translate(' + width + ', ' + height + ')' - }, null); + { + 'class': 'blocklyFlyoutCheckbox', + 'transform': 'translate(' + width + ', ' + height + ')' + }, null); Blockly.utils.createSvgElement('rect', - { - 'height': this.CHECKBOX_SIZE, - 'width': this.CHECKBOX_SIZE, - 'rx': this.CHECKBOX_CORNER_RADIUS, - 'ry': this.CHECKBOX_CORNER_RADIUS - }, checkboxGroup); + { + 'height': this.CHECKBOX_SIZE, + 'width': this.CHECKBOX_SIZE, + 'rx': this.CHECKBOX_CORNER_RADIUS, + 'ry': this.CHECKBOX_CORNER_RADIUS + }, checkboxGroup); Blockly.utils.createSvgElement('path', - { - 'class': 'blocklyFlyoutCheckboxPath', - 'd': this.CHECKMARK_PATH - }, checkboxGroup); + { + 'class': 'blocklyFlyoutCheckboxPath', + 'd': this.CHECKMARK_PATH + }, checkboxGroup); var checkboxObj = {svgRoot: checkboxGroup, clicked: checkboxState, block: block}; if (checkboxState) { diff --git a/core/gesture.js b/core/gesture.js index 46dd66123e..2a82ce9f57 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -570,8 +570,8 @@ Blockly.Gesture.prototype.handleRightClick = function(e) { */ Blockly.Gesture.prototype.handleWsStart = function(e, ws) { goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.handleWsStart, but the gesture had already been ' + - 'started.'); + 'Tried to call gesture.handleWsStart, but the gesture had already been ' + + 'started.'); this.setStartWorkspace_(ws); this.mostRecentEvent_ = e; this.doStart(e); @@ -585,8 +585,8 @@ Blockly.Gesture.prototype.handleWsStart = function(e, ws) { */ Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) { goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.handleFlyoutStart, but the gesture had already been ' + - 'started.'); + 'Tried to call gesture.handleFlyoutStart, but the gesture had already ' + + 'been started.'); this.setStartFlyout_(flyout); this.handleWsStart(e, flyout.getWorkspace()); }; @@ -599,8 +599,8 @@ Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) { */ Blockly.Gesture.prototype.handleBlockStart = function(e, block) { goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.handleBlockStart, but the gesture had already been ' + - 'started.'); + 'Tried to call gesture.handleBlockStart, but the gesture had already ' + + 'been started.'); this.setStartBlock(block); this.mostRecentEvent_ = e; }; @@ -643,7 +643,7 @@ Blockly.Gesture.prototype.doBlockClick_ = function() { // This is used to toggle the stack when any block in the stack is clicked. var rootBlock = this.startBlock_.getRootBlock(); Blockly.Events.fire( - new Blockly.Events.Ui(rootBlock, 'stackclick', undefined, undefined)); + new Blockly.Events.Ui(rootBlock, 'stackclick', undefined, undefined)); } } this.bringBlockToFront_(); @@ -684,8 +684,8 @@ Blockly.Gesture.prototype.bringBlockToFront_ = function() { */ Blockly.Gesture.prototype.setStartField = function(field) { goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.setStartField, but the gesture had already been ' + - 'started.'); + 'Tried to call gesture.setStartField, but the gesture had already been ' + + 'started.'); if (!this.startField_) { this.startField_ = field; } diff --git a/core/inject.js b/core/inject.js index 841520f5a5..bcc08c4ee5 100644 --- a/core/inject.js +++ b/core/inject.js @@ -443,13 +443,19 @@ Blockly.inject.bindDocumentEvents_ = function() { Blockly.inject.loadSounds_ = function(pathToMedia, workspace) { var audioMgr = workspace.getAudioManager(); audioMgr.load( - [pathToMedia + 'click.mp3', - pathToMedia + 'click.wav', - pathToMedia + 'click.ogg'], 'click'); + [ + pathToMedia + 'click.mp3', + pathToMedia + 'click.wav', + pathToMedia + 'click.ogg' + ], + 'click'); audioMgr.load( - [pathToMedia + 'delete.mp3', - pathToMedia + 'delete.ogg', - pathToMedia + 'delete.wav'], 'delete'); + [ + pathToMedia + 'delete.mp3', + pathToMedia + 'delete.ogg', + pathToMedia + 'delete.wav' + ], + 'delete'); // Bind temporary hooks that preload the sounds. var soundBinds = []; diff --git a/core/scratch_blocks_utils.js b/core/scratch_blocks_utils.js index 739c30d215..60a1bda333 100644 --- a/core/scratch_blocks_utils.js +++ b/core/scratch_blocks_utils.js @@ -59,7 +59,7 @@ Blockly.scratchBlocksUtils.measureText = function(fontSize, fontFamily, */ Blockly.scratchBlocksUtils.encodeEntities = function(rawStr) { // CC-BY-SA https://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript - return rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { + return rawStr.replace(/[\u00A0-\u9999<>&]/gim, function(i) { return '&#' + i.charCodeAt(0) + ';'; }); }; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 30d3a6b5da..3ce00b9bec 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -425,7 +425,7 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) { bottom = this.addTrashcan_(bottom); } if (this.options.zoomOptions && this.options.zoomOptions.controls) { - bottom = this.addZoomControls_(bottom); + this.addZoomControls_(bottom); } if (!this.isFlyout) { From ab5d4e7b9d6ef7b1311f46546fe7714d6562a79b Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 2 May 2018 15:58:36 -0700 Subject: [PATCH 0485/2135] Lint in block definitions --- blocks_horizontal/event.js | 66 +++--- blocks_horizontal/wedo.js | 97 ++++---- blocks_vertical/control.js | 31 ++- blocks_vertical/data.js | 4 +- blocks_vertical/event.js | 35 ++- blocks_vertical/extensions.js | 33 ++- blocks_vertical/looks.js | 123 +++++----- blocks_vertical/motion.js | 68 +++--- blocks_vertical/operators.js | 303 ++++++++++++------------- blocks_vertical/procedures.js | 2 +- blocks_vertical/sensing.js | 85 ++++--- blocks_vertical/sound.js | 51 ++--- blocks_vertical/vertical_extensions.js | 6 +- 13 files changed, 442 insertions(+), 462 deletions(-) diff --git a/blocks_horizontal/event.js b/blocks_horizontal/event.js index 07af163fa5..19a6ebab03 100644 --- a/blocks_horizontal/event.js +++ b/blocks_horizontal/event.js @@ -66,24 +66,25 @@ Blockly.Blocks['dropdown_whenbroadcast'] = { */ init: function() { this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu([ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_blue.svg', - value: 'blue', width: 48, height: 48, alt: 'Blue'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_green.svg', - value: 'green', width: 48, height: 48, alt: 'Green'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_coral.svg', - value: 'coral', width: 48, height: 48, alt: 'Coral'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_magenta.svg', - value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_orange.svg', - value: 'orange', width: 48, height: 48, alt: 'Orange'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_purple.svg', - value: 'purple', width: 48, height: 48, alt: 'Purple'} - ]), 'CHOICE'); + .appendField(new Blockly.FieldIconMenu( + [ + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_blue.svg', + value: 'blue', width: 48, height: 48, alt: 'Blue'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_green.svg', + value: 'green', width: 48, height: 48, alt: 'Green'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_coral.svg', + value: 'coral', width: 48, height: 48, alt: 'Coral'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_magenta.svg', + value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_orange.svg', + value: 'orange', width: 48, height: 48, alt: 'Orange'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_purple.svg', + value: 'purple', width: 48, height: 48, alt: 'Purple'} + ]), 'CHOICE'); this.setOutput(true); this.setColour(Blockly.Colours.event.primary, - Blockly.Colours.event.secondary, - Blockly.Colours.event.tertiary + Blockly.Colours.event.secondary, + Blockly.Colours.event.tertiary ); } }; @@ -127,24 +128,25 @@ Blockly.Blocks['dropdown_broadcast'] = { */ init: function() { this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu([ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_blue.svg', - value: 'blue', width: 48, height: 48, alt: 'Blue'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_green.svg', - value: 'green', width: 48, height: 48, alt: 'Green'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_coral.svg', - value: 'coral', width: 48, height: 48, alt: 'Coral'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_magenta.svg', - value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_orange.svg', - value: 'orange', width: 48, height: 48, alt: 'Orange'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_purple.svg', - value: 'purple', width: 48, height: 48, alt: 'Purple'} - ]), 'CHOICE'); + .appendField(new Blockly.FieldIconMenu( + [ + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_blue.svg', + value: 'blue', width: 48, height: 48, alt: 'Blue'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_green.svg', + value: 'green', width: 48, height: 48, alt: 'Green'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_coral.svg', + value: 'coral', width: 48, height: 48, alt: 'Coral'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_magenta.svg', + value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_orange.svg', + value: 'orange', width: 48, height: 48, alt: 'Orange'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_purple.svg', + value: 'purple', width: 48, height: 48, alt: 'Purple'} + ]), 'CHOICE'); this.setOutput(true); this.setColour(Blockly.Colours.event.primary, - Blockly.Colours.event.secondary, - Blockly.Colours.event.tertiary + Blockly.Colours.event.secondary, + Blockly.Colours.event.tertiary ); } }; diff --git a/blocks_horizontal/wedo.js b/blocks_horizontal/wedo.js index 31727adc29..f2052aa296 100644 --- a/blocks_horizontal/wedo.js +++ b/blocks_horizontal/wedo.js @@ -37,30 +37,31 @@ Blockly.Blocks['dropdown_wedo_setcolor'] = { */ init: function() { this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu([ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_mystery.svg', - value: 'mystery', width: 48, height: 48, alt: 'Mystery'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_yellow.svg', - value: 'yellow', width: 48, height: 48, alt: 'Yellow'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_orange.svg', - value: 'orange', width: 48, height: 48, alt: 'Orange'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_coral.svg', - value: 'coral', width: 48, height: 48, alt: 'Coral'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_magenta.svg', - value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_purple.svg', - value: 'purple', width: 48, height: 48, alt: 'Purple'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_blue.svg', - value: 'blue', width: 48, height: 48, alt: 'Blue'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_green.svg', - value: 'green', width: 48, height: 48, alt: 'Green'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_white.svg', - value: 'white', width: 48, height: 48, alt: 'White'} - ]), 'CHOICE'); + .appendField(new Blockly.FieldIconMenu( + [ + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_mystery.svg', + value: 'mystery', width: 48, height: 48, alt: 'Mystery'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_yellow.svg', + value: 'yellow', width: 48, height: 48, alt: 'Yellow'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_orange.svg', + value: 'orange', width: 48, height: 48, alt: 'Orange'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_coral.svg', + value: 'coral', width: 48, height: 48, alt: 'Coral'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_magenta.svg', + value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_purple.svg', + value: 'purple', width: 48, height: 48, alt: 'Purple'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_blue.svg', + value: 'blue', width: 48, height: 48, alt: 'Blue'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_green.svg', + value: 'green', width: 48, height: 48, alt: 'Green'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_white.svg', + value: 'white', width: 48, height: 48, alt: 'White'} + ]), 'CHOICE'); this.setOutput(true); this.setColour(Blockly.Colours.looks.primary, - Blockly.Colours.looks.secondary, - Blockly.Colours.looks.tertiary + Blockly.Colours.looks.secondary, + Blockly.Colours.looks.tertiary ); } }; @@ -173,18 +174,19 @@ Blockly.Blocks['dropdown_wedo_motorspeed'] = { */ init: function() { this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu([ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_slow.svg', - value: 'slow', width: 48, height: 48, alt: 'Slow'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_med.svg', - value: 'medium', width: 48, height: 48, alt: 'Medium'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_fast.svg', - value: 'fast', width: 48, height: 48, alt: 'Fast'} - ]), 'CHOICE'); + .appendField(new Blockly.FieldIconMenu( + [ + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_slow.svg', + value: 'slow', width: 48, height: 48, alt: 'Slow'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_med.svg', + value: 'medium', width: 48, height: 48, alt: 'Medium'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_fast.svg', + value: 'fast', width: 48, height: 48, alt: 'Fast'} + ]), 'CHOICE'); this.setOutput(true); this.setColour(Blockly.Colours.motion.primary, - Blockly.Colours.motion.secondary, - Blockly.Colours.motion.tertiary + Blockly.Colours.motion.secondary, + Blockly.Colours.motion.tertiary ); } }; @@ -229,25 +231,26 @@ Blockly.Blocks['dropdown_wedo_whentilt'] = { */ init: function() { this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu([ - {type: 'placeholder', width: 48, height: 48}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-forward.svg', - value: 'forward', width: 48, height: 48, alt: 'Tilt forward'}, - {type: 'placeholder', width: 48, height: 48}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-left.svg', - value: 'left', width: 48, height: 48, alt: 'Tilt left'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt.svg', - value: 'any', width: 48, height: 48, alt: 'Tilt any'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-right.svg', + .appendField(new Blockly.FieldIconMenu( + [ + {type: 'placeholder', width: 48, height: 48}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-forward.svg', + value: 'forward', width: 48, height: 48, alt: 'Tilt forward'}, + {type: 'placeholder', width: 48, height: 48}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-left.svg', + value: 'left', width: 48, height: 48, alt: 'Tilt left'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt.svg', + value: 'any', width: 48, height: 48, alt: 'Tilt any'}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-right.svg', value: 'right', width: 48, height: 48, alt: 'Tilt right'}, - {type: 'placeholder', width: 48, height: 48}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-backward.svg', + {type: 'placeholder', width: 48, height: 48}, + {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-backward.svg', value: 'backward', width: 48, height: 48, alt: 'Tilt backward'} - ]), 'CHOICE'); + ]), 'CHOICE'); this.setOutput(true); this.setColour(Blockly.Colours.event.primary, - Blockly.Colours.event.secondary, - Blockly.Colours.event.tertiary + Blockly.Colours.event.secondary, + Blockly.Colours.event.tertiary ); } }; diff --git a/blocks_vertical/control.js b/blocks_vertical/control.js index 8ff7282886..734672c2fa 100644 --- a/blocks_vertical/control.js +++ b/blocks_vertical/control.js @@ -208,8 +208,8 @@ Blockly.Blocks['control_stop'] = { .appendField(stopDropdown, 'STOP_OPTION'); this.setCategory(Blockly.Categories.control); this.setColour(Blockly.Colours.control.primary, - Blockly.Colours.control.secondary, - Blockly.Colours.control.tertiary + Blockly.Colours.control.secondary, + Blockly.Colours.control.tertiary ); this.setPreviousStatement(true); }, @@ -402,20 +402,19 @@ Blockly.Blocks['control_create_clone_of_menu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "CLONE_OPTION", - "options": [ - ['myself', '_myself_'] - ] - } - ], - "extensions": ["colours_control", "output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "CLONE_OPTION", + "options": [ + ['myself', '_myself_'] + ] + } + ], + "extensions": ["colours_control", "output_string"] + }); } }; diff --git a/blocks_vertical/data.js b/blocks_vertical/data.js index 2a7894a39b..6be812c09d 100644 --- a/blocks_vertical/data.js +++ b/blocks_vertical/data.js @@ -478,7 +478,7 @@ Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN = { option.callback = Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY(this, - option.text); + option.text); options.push(option); } } else { @@ -500,7 +500,7 @@ Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN = { }; Blockly.Extensions.registerMixin('contextMenu_getVariableBlock', - Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN); + Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN); /** * Callback factory for dropdown menu options associated with a variable getter diff --git a/blocks_vertical/event.js b/blocks_vertical/event.js index f1eb5be7c6..65b90ae78c 100644 --- a/blocks_vertical/event.js +++ b/blocks_vertical/event.js @@ -117,7 +117,7 @@ Blockly.Blocks['event_whenbackdropswitchesto'] = { "type": "field_dropdown", "name": "BACKDROP", "options": [ - ['backdrop1', 'BACKDROP1'] + ['backdrop1', 'BACKDROP1'] ] } ], @@ -140,7 +140,7 @@ Blockly.Blocks['event_whengreaterthan'] = { "type": "field_dropdown", "name": "WHENGREATERTHANMENU", "options": [ - ['timer', 'TIMER'] + ['timer', 'TIMER'] ] }, { @@ -160,22 +160,21 @@ Blockly.Blocks['event_broadcast_menu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_variable", - "name": "BROADCAST_OPTION", - "variableTypes":[Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], - "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME - } - ], - "colour": Blockly.Colours.event.secondary, - "colourSecondary": Blockly.Colours.event.secondary, - "colourTertiary": Blockly.Colours.event.tertiary, - "extensions": ["output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_variable", + "name": "BROADCAST_OPTION", + "variableTypes":[Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], + "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME + } + ], + "colour": Blockly.Colours.event.secondary, + "colourSecondary": Blockly.Colours.event.secondary, + "colourTertiary": Blockly.Colours.event.tertiary, + "extensions": ["output_string"] + }); } }; diff --git a/blocks_vertical/extensions.js b/blocks_vertical/extensions.js index 59efd5f2cd..409f19e4a9 100644 --- a/blocks_vertical/extensions.js +++ b/blocks_vertical/extensions.js @@ -190,23 +190,22 @@ Blockly.Blocks['extension_wedo_tilt_menu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TILT", - "options": [ - ['Any', 'Any'], - ['Whirl', 'Whirl'], - ['South', 'South'], - ['Back in time', 'Back in time'] - ] - } - ], - "extensions": ["colours_more", "output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "TILT", + "options": [ + ['Any', 'Any'], + ['Whirl', 'Whirl'], + ['South', 'South'], + ['Back in time', 'Back in time'] + ] + } + ], + "extensions": ["colours_more", "output_string"] + }); } }; diff --git a/blocks_vertical/looks.js b/blocks_vertical/looks.js index f4dc5c2e27..96a3820ead 100644 --- a/blocks_vertical/looks.js +++ b/blocks_vertical/looks.js @@ -122,12 +122,11 @@ Blockly.Blocks['looks_show'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.LOOKS_SHOW, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_SHOW, + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); } }; @@ -137,12 +136,11 @@ Blockly.Blocks['looks_hide'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.LOOKS_HIDE, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_HIDE, + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); } }; @@ -154,12 +152,11 @@ Blockly.Blocks['looks_hideallsprites'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.LOOKS_HIDEALLSPRITES, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_HIDEALLSPRITES, + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); } }; @@ -355,24 +352,23 @@ Blockly.Blocks['looks_costume'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "COSTUME", - "options": [ - ['costume1', 'COSTUME1'], - ['costume2', 'COSTUME2'] - ] - } - ], - "colour": Blockly.Colours.looks.secondary, - "colourSecondary": Blockly.Colours.looks.secondary, - "colourTertiary": Blockly.Colours.looks.tertiary, - "extensions": ["output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "COSTUME", + "options": [ + ['costume1', 'COSTUME1'], + ['costume2', 'COSTUME2'] + ] + } + ], + "colour": Blockly.Colours.looks.secondary, + "colourSecondary": Blockly.Colours.looks.secondary, + "colourTertiary": Blockly.Colours.looks.tertiary, + "extensions": ["output_string"] + }); } }; @@ -402,12 +398,11 @@ Blockly.Blocks['looks_nextcostume'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.LOOKS_NEXTCOSTUME, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_NEXTCOSTUME, + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); } }; @@ -445,7 +440,7 @@ Blockly.Blocks['looks_backdrops'] = { "type": "field_dropdown", "name": "BACKDROP", "options": [ - ['backdrop1', 'BACKDROP1'] + ['backdrop1', 'BACKDROP1'] ] } ], @@ -463,22 +458,21 @@ Blockly.Blocks['looks_gotofrontback'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.LOOKS_GOTOFRONTBACK, - "args0": [ - { - "type": "field_dropdown", - "name": "FRONT_BACK", - "options": [ - [Blockly.Msg.LOOKS_GOTOFRONTBACK_FRONT, 'front'], - [Blockly.Msg.LOOKS_GOTOFRONTBACK_BACK, 'back'] - ] - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_GOTOFRONTBACK, + "args0": [ + { + "type": "field_dropdown", + "name": "FRONT_BACK", + "options": [ + [Blockly.Msg.LOOKS_GOTOFRONTBACK_FRONT, 'front'], + [Blockly.Msg.LOOKS_GOTOFRONTBACK_BACK, 'back'] + ] + } + ], + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); } }; @@ -586,11 +580,10 @@ Blockly.Blocks['looks_nextbackdrop'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.LOOKS_NEXTBACKDROP, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); + this.jsonInit({ + "message0": Blockly.Msg.LOOKS_NEXTBACKDROP, + "category": Blockly.Categories.looks, + "extensions": ["colours_looks", "shape_statement"] + }); } }; diff --git a/blocks_vertical/motion.js b/blocks_vertical/motion.js index 6679ebfaa0..8f53ec8eb3 100644 --- a/blocks_vertical/motion.js +++ b/blocks_vertical/motion.js @@ -126,23 +126,22 @@ Blockly.Blocks['motion_pointtowards_menu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TOWARDS", - "options": [ - [Blockly.Msg.MOTION_POINTTOWARDS_POINTER, '_mouse_'] - ] - } - ], - "colour": Blockly.Colours.motion.secondary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "extensions": ["output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "TOWARDS", + "options": [ + [Blockly.Msg.MOTION_POINTTOWARDS_POINTER, '_mouse_'] + ] + } + ], + "colour": Blockly.Colours.motion.secondary, + "colourSecondary": Blockly.Colours.motion.secondary, + "colourTertiary": Blockly.Colours.motion.tertiary, + "extensions": ["output_string"] + }); } }; @@ -172,24 +171,23 @@ Blockly.Blocks['motion_goto_menu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TO", - "options": [ - [Blockly.Msg.MOTION_GOTO_POINTER, '_mouse_'], - [Blockly.Msg.MOTION_GOTO_RANDOM, '_random_'] - ] - } - ], - "colour": Blockly.Colours.motion.secondary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "extensions": ["output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "TO", + "options": [ + [Blockly.Msg.MOTION_GOTO_POINTER, '_mouse_'], + [Blockly.Msg.MOTION_GOTO_RANDOM, '_random_'] + ] + } + ], + "colour": Blockly.Colours.motion.secondary, + "colourSecondary": Blockly.Colours.motion.secondary, + "colourTertiary": Blockly.Colours.motion.tertiary, + "extensions": ["output_string"] + }); } }; diff --git a/blocks_vertical/operators.js b/blocks_vertical/operators.js index 6bd62ba026..1caa04481c 100644 --- a/blocks_vertical/operators.js +++ b/blocks_vertical/operators.js @@ -34,22 +34,21 @@ Blockly.Blocks['operator_add'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_ADD, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_ADD, + "args0": [ + { + "type": "input_value", + "name": "NUM1" + }, + { + "type": "input_value", + "name": "NUM2" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; @@ -59,22 +58,21 @@ Blockly.Blocks['operator_subtract'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_SUBTRACT, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_SUBTRACT, + "args0": [ + { + "type": "input_value", + "name": "NUM1" + }, + { + "type": "input_value", + "name": "NUM2" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; @@ -84,22 +82,21 @@ Blockly.Blocks['operator_multiply'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_MULTIPLY, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_MULTIPLY, + "args0": [ + { + "type": "input_value", + "name": "NUM1" + }, + { + "type": "input_value", + "name": "NUM2" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; @@ -109,22 +106,21 @@ Blockly.Blocks['operator_divide'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_DIVIDE, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_DIVIDE, + "args0": [ + { + "type": "input_value", + "name": "NUM1" + }, + { + "type": "input_value", + "name": "NUM2" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; @@ -134,22 +130,21 @@ Blockly.Blocks['operator_random'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_RANDOM, - "args0": [ - { - "type": "input_value", - "name": "FROM" - }, - { - "type": "input_value", - "name": "TO" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_RANDOM, + "args0": [ + { + "type": "input_value", + "name": "FROM" + }, + { + "type": "input_value", + "name": "TO" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; @@ -372,22 +367,21 @@ Blockly.Blocks['operator_contains'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_CONTAINS, - "args0": [ - { - "type": "input_value", - "name": "STRING1" - }, - { - "type": "input_value", - "name": "STRING2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_CONTAINS, + "args0": [ + { + "type": "input_value", + "name": "STRING1" + }, + { + "type": "input_value", + "name": "STRING2" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_boolean"] + }); } }; @@ -397,22 +391,21 @@ Blockly.Blocks['operator_mod'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_MOD, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_MOD, + "args0": [ + { + "type": "input_value", + "name": "NUM1" + }, + { + "type": "input_value", + "name": "NUM2" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; @@ -422,18 +415,17 @@ Blockly.Blocks['operator_round'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_ROUND, - "args0": [ - { - "type": "input_value", - "name": "NUM" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_ROUND, + "args0": [ + { + "type": "input_value", + "name": "NUM" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; @@ -443,37 +435,36 @@ Blockly.Blocks['operator_mathop'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": Blockly.Msg.OPERATORS_MATHOP, - "args0": [ - { - "type": "field_dropdown", - "name": "OPERATOR", - "options": [ - [Blockly.Msg.OPERATORS_MATHOP_ABS, 'abs'], - [Blockly.Msg.OPERATORS_MATHOP_FLOOR, 'floor'], - [Blockly.Msg.OPERATORS_MATHOP_CEILING, 'ceiling'], - [Blockly.Msg.OPERATORS_MATHOP_SQRT, 'sqrt'], - [Blockly.Msg.OPERATORS_MATHOP_SIN, 'sin'], - [Blockly.Msg.OPERATORS_MATHOP_COS, 'cos'], - [Blockly.Msg.OPERATORS_MATHOP_TAN, 'tan'], - [Blockly.Msg.OPERATORS_MATHOP_ASIN, 'asin'], - [Blockly.Msg.OPERATORS_MATHOP_ACOS, 'acos'], - [Blockly.Msg.OPERATORS_MATHOP_ATAN, 'atan'], - [Blockly.Msg.OPERATORS_MATHOP_LN, 'ln'], - [Blockly.Msg.OPERATORS_MATHOP_LOG, 'log'], - [Blockly.Msg.OPERATORS_MATHOP_EEXP, 'e ^'], - [Blockly.Msg.OPERATORS_MATHOP_10EXP, '10 ^'] - ] - }, - { - "type": "input_value", - "name": "NUM" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); + this.jsonInit({ + "message0": Blockly.Msg.OPERATORS_MATHOP, + "args0": [ + { + "type": "field_dropdown", + "name": "OPERATOR", + "options": [ + [Blockly.Msg.OPERATORS_MATHOP_ABS, 'abs'], + [Blockly.Msg.OPERATORS_MATHOP_FLOOR, 'floor'], + [Blockly.Msg.OPERATORS_MATHOP_CEILING, 'ceiling'], + [Blockly.Msg.OPERATORS_MATHOP_SQRT, 'sqrt'], + [Blockly.Msg.OPERATORS_MATHOP_SIN, 'sin'], + [Blockly.Msg.OPERATORS_MATHOP_COS, 'cos'], + [Blockly.Msg.OPERATORS_MATHOP_TAN, 'tan'], + [Blockly.Msg.OPERATORS_MATHOP_ASIN, 'asin'], + [Blockly.Msg.OPERATORS_MATHOP_ACOS, 'acos'], + [Blockly.Msg.OPERATORS_MATHOP_ATAN, 'atan'], + [Blockly.Msg.OPERATORS_MATHOP_LN, 'ln'], + [Blockly.Msg.OPERATORS_MATHOP_LOG, 'log'], + [Blockly.Msg.OPERATORS_MATHOP_EEXP, 'e ^'], + [Blockly.Msg.OPERATORS_MATHOP_10EXP, '10 ^'] + ] + }, + { + "type": "input_value", + "name": "NUM" + } + ], + "category": Blockly.Categories.operators, + "extensions": ["colours_operators", "output_number"] + }); } }; diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 0272e0504f..d25e61034a 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -200,7 +200,7 @@ Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function() { */ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) { // Split the proc into components, by %n, %b, and %s (ignoring escaped). - var procComponents = this.procCode_.split(/(?=[^\\]\%[nbs])/); + var procComponents = this.procCode_.split(/(?=[^\\]%[nbs])/); procComponents = procComponents.map(function(c) { return c.trim(); // Strip whitespace. }); diff --git a/blocks_vertical/sensing.js b/blocks_vertical/sensing.js index 39b0e16a53..d6b3ad0afb 100644 --- a/blocks_vertical/sensing.js +++ b/blocks_vertical/sensing.js @@ -54,21 +54,20 @@ Blockly.Blocks['sensing_touchingobjectmenu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TOUCHINGOBJECTMENU", - "options": [ - [Blockly.Msg.SENSING_TOUCHINGOBJECT_POINTER, '_mouse_'], - [Blockly.Msg.SENSING_TOUCHINGOBJECT_EDGE, '_edge_'] - ] - } - ], - "extensions": ["colours_sensing", "output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "TOUCHINGOBJECTMENU", + "options": [ + [Blockly.Msg.SENSING_TOUCHINGOBJECT_POINTER, '_mouse_'], + [Blockly.Msg.SENSING_TOUCHINGOBJECT_EDGE, '_edge_'] + ] + } + ], + "extensions": ["colours_sensing", "output_string"] + }); } }; @@ -142,20 +141,19 @@ Blockly.Blocks['sensing_distancetomenu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "DISTANCETOMENU", - "options": [ - [Blockly.Msg.SENSING_DISTANCETO_POINTER, '_mouse_'] - ] - } - ], - "extensions": ["colours_sensing", "output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "DISTANCETOMENU", + "options": [ + [Blockly.Msg.SENSING_DISTANCETO_POINTER, '_mouse_'] + ] + } + ], + "extensions": ["colours_sensing", "output_string"] + }); } }; @@ -409,21 +407,20 @@ Blockly.Blocks['sensing_of_object_menu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "OBJECT", - "options": [ - ['Sprite1', 'Sprite1'], - ['Stage', '_stage_'] - ] - } - ], - "extensions": ["colours_sensing", "output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "OBJECT", + "options": [ + ['Sprite1', 'Sprite1'], + ['Stage', '_stage_'] + ] + } + ], + "extensions": ["colours_sensing", "output_string"] + }); } }; diff --git a/blocks_vertical/sound.js b/blocks_vertical/sound.js index 0c530ad50b..e9e9c530a0 100644 --- a/blocks_vertical/sound.js +++ b/blocks_vertical/sound.js @@ -33,32 +33,31 @@ Blockly.Blocks['sound_sounds_menu'] = { * @this Blockly.Block */ init: function() { - this.jsonInit( - { - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "SOUND_MENU", - "options": [ - ['1', '0'], - ['2', '1'], - ['3', '2'], - ['4', '3'], - ['5', '4'], - ['6', '5'], - ['7', '6'], - ['8', '7'], - ['9', '8'], - ['10', '9'] - ] - } - ], - "colour": Blockly.Colours.sounds.secondary, - "colourSecondary": Blockly.Colours.sounds.secondary, - "colourTertiary": Blockly.Colours.sounds.tertiary, - "extensions": ["output_string"] - }); + this.jsonInit({ + "message0": "%1", + "args0": [ + { + "type": "field_dropdown", + "name": "SOUND_MENU", + "options": [ + ['1', '0'], + ['2', '1'], + ['3', '2'], + ['4', '3'], + ['5', '4'], + ['6', '5'], + ['7', '6'], + ['8', '7'], + ['9', '8'], + ['10', '9'] + ] + } + ], + "colour": Blockly.Colours.sounds.secondary, + "colourSecondary": Blockly.Colours.sounds.secondary, + "colourTertiary": Blockly.Colours.sounds.tertiary, + "extensions": ["output_string"] + }); } }; diff --git a/blocks_vertical/vertical_extensions.js b/blocks_vertical/vertical_extensions.js index 740a01090e..c0fffa7acb 100644 --- a/blocks_vertical/vertical_extensions.js +++ b/blocks_vertical/vertical_extensions.js @@ -223,10 +223,10 @@ Blockly.ScratchBlocks.VerticalExtensions.SCRATCH_EXTENSION = function() { Blockly.ScratchBlocks.VerticalExtensions.registerAll = function() { var categoryNames = ['control', 'data', 'data_lists', 'sounds', 'motion', 'looks', 'event', - 'sensing', 'pen', 'operators', 'more']; + 'sensing', 'pen', 'operators', 'more']; // Register functions for all category colours. for (var i = 0; i < categoryNames.length; i++) { - name = categoryNames[i]; + var name = categoryNames[i]; Blockly.Extensions.register('colours_' + name, Blockly.ScratchBlocks.VerticalExtensions.colourHelper(name)); } @@ -259,7 +259,7 @@ Blockly.ScratchBlocks.VerticalExtensions.registerAll = function() { // Extension blocks have slightly different block rendering. Blockly.Extensions.register('scratch_extension', - Blockly.ScratchBlocks.VerticalExtensions.SCRATCH_EXTENSION); + Blockly.ScratchBlocks.VerticalExtensions.SCRATCH_EXTENSION); }; Blockly.ScratchBlocks.VerticalExtensions.registerAll(); From 9a1dfe4acbbeb954d0752b5463a948f750534222 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 May 2018 10:21:07 -0700 Subject: [PATCH 0486/2135] Cleanup --- pull_from_blockly.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pull_from_blockly.sh b/pull_from_blockly.sh index c4ae63e3fe..fe3dcb8674 100755 --- a/pull_from_blockly.sh +++ b/pull_from_blockly.sh @@ -87,6 +87,8 @@ run_cleanup_fn() { exit fi + bold_echo "Running cleanup.sh" + sleep .5 # Cleanup.sh resolves common conflicts. ./cleanup.sh } @@ -99,13 +101,11 @@ prompt_for_merge_abort() { prompt "Do you want to abort this merge?" false if [ $prompt_result = false ] then - bold_echo "Skipping merge abort." + bold_echo "Continuing with merge..." else bold_echo "Running git merge --abort" git merge --abort - bold_echo "Current status" - sleep .5 - git status + display_status_fn bold_echo "Done" exit fi @@ -131,6 +131,7 @@ display_status_fn() { # next steps should be. finish_fn() { prompt_for_merge_abort + bold_echo "Done. You may need to manually resolve conflicts." # Helpful tips about what to do next. empty_lines sleep .5 From 52d48080fccb018a79194bf07c4c72c0cd41fd1b Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 May 2018 11:26:31 -0700 Subject: [PATCH 0487/2135] Add new indent rule to .eslintrc and increase the minimum version --- .eslintrc | 23 ++++++++++++++++++++++- package.json | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 4efde62aad..2d78203ac1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,7 +2,28 @@ "rules": { "curly": ["error", "multi-line"], "eol-last": ["error"], - "indent": ["warn", 2, {"SwitchCase": 1}], # Blockly/Google use 2-space indents + "indent": [ + "error", 2, # Blockly/Google use 2-space indents + # Blockly/Google uses +4 space indents for line continuations. + { + "SwitchCase": 1, + "MemberExpression": 2, + "ObjectExpression": 1, + "FunctionDeclaration": { + "body": 1, + "parameters": 2 + }, + "FunctionExpression": { + "body": 1, + "parameters": 2 + }, + "CallExpression": { + "arguments": 2 + }, + # Ignore default rules for ternary expressions. + "ignoredNodes": ["ConditionalExpression"] + } + ], "linebreak-style": ["error", "unix"], "max-len": ["error", 120, 4], "no-trailing-spaces": ["error", { "skipBlankLines": true }], diff --git a/package.json b/package.json index e56b96d4cd..10b2c077e1 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "devDependencies": { "async": "2.6.0", "copy-webpack-plugin": "4.0.1", - "eslint": "2.9.0", + "eslint": "^4.16", "event-stream": "3.3.4", "exports-loader": "0.6.3", "gh-pages": "0.12.0", From c3ca2258322a9657dd74a191236c58885c53e061 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 May 2018 14:03:33 -0700 Subject: [PATCH 0488/2135] Faster event filtering --- core/events.js | 69 ++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/core/events.js b/core/events.js index 20bcc1f1a9..e5cddd73c6 100644 --- a/core/events.js +++ b/core/events.js @@ -186,45 +186,42 @@ Blockly.Events.filter = function(queueIn, forward) { // Undo is merged in reverse order. queue.reverse(); } - // Merge duplicates. O(n^2), but n should be very small. - for (var i = 0, event1; event1 = queue[i]; i++) { - for (var j = i + 1, event2; event2 = queue[j]; j++) { - if (event1.type == event2.type && - event1.blockId == event2.blockId && - event1.workspaceId == event2.workspaceId) { - if (event1.type == Blockly.Events.MOVE) { - // Merge move events. - event1.newParentId = event2.newParentId; - event1.newInputName = event2.newInputName; - event1.newCoordinate = event2.newCoordinate; - queue.splice(j, 1); - j--; - } else if (event1.type == Blockly.Events.CHANGE && - event1.element == event2.element && - event1.name == event2.name) { - // Merge change events. - event1.newValue = event2.newValue; - queue.splice(j, 1); - j--; - } else if (event1.type == Blockly.Events.UI && - event2.element == 'click' && - (event1.element == 'commentOpen' || - event1.element == 'mutatorOpen' || - event1.element == 'warningOpen')) { - // Merge change events. - event1.newValue = event2.newValue; - queue.splice(j, 1); - j--; - } + var mergedQueue = []; + var hash = Object.create(null); + // Merge duplicates. + for (var i = 0, event; event = queue[i]; i++) { + if (!event.isNull()) { + var key = [event.type, event.blockId, event.workspaceId].join(' '); + var lastEvent = hash[key]; + if (!lastEvent) { + hash[key] = event; + mergedQueue.push(event); + } else if (event.type == Blockly.Events.MOVE) { + // Merge move events. + lastEvent.newParentId = event.newParentId; + lastEvent.newInputName = event.newInputName; + lastEvent.newCoordinate = event.newCoordinate; + } else if (event.type == Blockly.Events.CHANGE && + event.element == lastEvent.element && + event.name == lastEvent.name) { + // Merge change events. + lastEvent.newValue = event.newValue; + } else if (event.type == Blockly.Events.UI && + event.element == 'click' && + (lastEvent.element == 'commentOpen' || + lastEvent.element == 'mutatorOpen' || + lastEvent.element == 'warningOpen')) { + // Merge click events. + lastEvent.newValue = event.newValue; + } else { + // Collision: newer events should merge into this event to maintain order + hash[key] = event; + mergedQueue.push(event); } } } - // Remove null events. - for (var i = queue.length - 1; i >= 0; i--) { - if (queue[i].isNull()) { - queue.splice(i, 1); - } - } + // Filter out any events that have become null due to merging. + queue = mergedQueue.filter(function(e) { return !e.isNull(); }); if (!forward) { // Restore undo order. queue.reverse(); From c632570d8f71b46d4b3c84602e52cb5381948829 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 May 2018 14:17:42 -0700 Subject: [PATCH 0489/2135] Update field_test.js --- tests/jsunit/field_test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/jsunit/field_test.js b/tests/jsunit/field_test.js index 1a07de6408..2da04834dc 100644 --- a/tests/jsunit/field_test.js +++ b/tests/jsunit/field_test.js @@ -123,3 +123,4 @@ function test_field_register_with_custom_field() { field = Blockly.Field.fromJson(json); assertNotNull(field); assertEquals(field.getValue(), 'ok'); +} From ac119452bf320c541194310ba54a65f4d84039b3 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 3 May 2018 18:34:49 -0400 Subject: [PATCH 0490/2135] Lint --- core/data_category.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/data_category.js b/core/data_category.js index 9614873016..7a109058bd 100644 --- a/core/data_category.js +++ b/core/data_category.js @@ -302,7 +302,7 @@ Blockly.DataCategory.addItemNumberOfList = function(xmlList, variable) { // variablename // Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemnumoflist', - 'LIST', ['ITEM', 'text', 'thing']); + 'LIST', ['ITEM', 'text', 'thing']); }; /** From ddd356a48a873156c644e2365225056c4703225f Mon Sep 17 00:00:00 2001 From: Mx Corey Frang Date: Fri, 4 May 2018 13:23:41 -0400 Subject: [PATCH 0491/2135] Mark CTM dirty instead of forcing a reflow/recalc (#1499) * Mark CTM dirty instead of forcing a reflow/recalc * use false instead of DIRTY * So "dirty" * Blockly.DIRTY --- core/constants.js | 6 ++++++ core/workspace_svg.js | 23 ++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/core/constants.js b/core/constants.js index f98b1e62b0..d0a4e5570a 100644 --- a/core/constants.js +++ b/core/constants.js @@ -366,3 +366,9 @@ Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE = 'procedures_prototype'; * @const {string} */ Blockly.PROCEDURES_CALL_BLOCK_TYPE = 'procedures_call'; + +/** + * A constant value that we can use to mark calculated properties as dirty. + * @const {string} + */ +Blockly.DIRTY = 'dirty'; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 3ce00b9bec..33d9fe08a0 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -54,7 +54,6 @@ goog.require('goog.math.Coordinate'); goog.require('goog.userAgent'); goog.require('goog.math.Rect'); - /** * Class for a workspace. This is an onscreen area with optional trashcan, * scrollbars, bubbles, and dragging. @@ -286,24 +285,34 @@ Blockly.WorkspaceSvg.prototype.toolboxCategoryCallbacks_ = {}; * @type {SVGMatrix} * @private */ -Blockly.WorkspaceSvg.prototype.inverseScreenCTM_ = null; +Blockly.WorkspaceSvg.prototype.inverseScreenCTM_ = Blockly.DIRTY; /** * Getter for the inverted screen CTM. * @return {SVGMatrix} The matrix to use in mouseToSvg */ Blockly.WorkspaceSvg.prototype.getInverseScreenCTM = function() { + + // Defer getting the screen CTM until we actually need it, this should + // avoid forced reflows from any calls to updateInverseScreenCTM. + if (this.inverseScreenCTM_ == Blockly.DIRTY) { + var ctm = this.getParentSvg().getScreenCTM(); + if (ctm) { + this.inverseScreenCTM_ = ctm.inverse(); + } else { + // When dirty, and we can't get a CTM, set it to null. + this.inverseScreenCTM_ = null; + } + } + return this.inverseScreenCTM_; }; /** - * Update the inverted screen CTM. + * Mark the inverse screen CTM as dirty. */ Blockly.WorkspaceSvg.prototype.updateInverseScreenCTM = function() { - var ctm = this.getParentSvg().getScreenCTM(); - if (ctm) { - this.inverseScreenCTM_ = ctm.inverse(); - } + this.inverseScreenCTM_ = Blockly.DIRTY; }; /** From 51c3e8e96cc61ee58325010a7773d791babf6af3 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Fri, 13 Apr 2018 11:12:27 -0400 Subject: [PATCH 0492/2135] Optimize block.setParent during clearing workspace --- core/block_svg.js | 36 ++++++++++++++++++++++-------------- core/workspace.js | 9 +++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 0b0f3e47cd..9384c9fe7d 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -276,27 +276,24 @@ Blockly.BlockSvg.prototype.getIcons = function() { * @param {Blockly.BlockSvg} newParent New parent block. */ Blockly.BlockSvg.prototype.setParent = function(newParent) { - if (newParent == this.parentBlock_) { + var oldParent = this.parentBlock_; + if (newParent == oldParent) { return; } - var svgRoot = this.getSvgRoot(); - if (this.parentBlock_ && svgRoot) { - // Move this block up the DOM. Keep track of x/y translations. - var xy = this.getRelativeToSurfaceXY(); - // Avoid moving a block up the DOM if it's currently selected/dragging, - // so as to avoid taking things off the drag surface. - if (Blockly.selected != this) { - this.workspace.getCanvas().appendChild(svgRoot); - this.translate(xy.x, xy.y); - } - } - Blockly.Field.startCache(); Blockly.BlockSvg.superClass_.setParent.call(this, newParent); Blockly.Field.stopCache(); + var svgRoot = this.getSvgRoot(); + + // Bail early if workspace is clearing, or we aren't rendered. + // We won't need to reattach ourselves anywhere. + if (this.workspace.isClearing || !svgRoot) { + return; + } + + var oldXY = this.getRelativeToSurfaceXY(); if (newParent) { - var oldXY = this.getRelativeToSurfaceXY(); newParent.getSvgRoot().appendChild(svgRoot); var newXY = this.getRelativeToSurfaceXY(); // Move the connections to match the child's new position. @@ -307,6 +304,17 @@ Blockly.BlockSvg.prototype.setParent = function(newParent) { newParent.getColourTertiary()); } } + // If we are losing a parent, we want to move our DOM element to the + // root of the workspace. + else if (oldParent) { + // Avoid moving a block up the DOM if it's currently selected/dragging, + // so as to avoid taking things off the drag surface. + if (Blockly.selected != this) { + this.workspace.getCanvas().appendChild(svgRoot); + this.translate(oldXY.x, oldXY.y); + } + } + }; /** diff --git a/core/workspace.js b/core/workspace.js index f2450bafb5..3deca08228 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -110,6 +110,13 @@ Blockly.Workspace = function(opt_options) { */ Blockly.Workspace.prototype.rendered = false; +/** + * Returns `true` if the workspace is currently in the process of a bulk clear. + * @type {boolean} + * @package + */ +Blockly.Workspace.prototype.isClearing = false; + /** * Maximum number of undo events in stack. `0` turns off undo, `Infinity` sets it to unlimited. * @type {number} @@ -202,6 +209,7 @@ Blockly.Workspace.prototype.getAllBlocks = function() { * Dispose of all blocks in workspace. */ Blockly.Workspace.prototype.clear = function() { + this.isClearing = true; var existingGroup = Blockly.Events.getGroup(); if (!existingGroup) { Blockly.Events.setGroup(true); @@ -223,6 +231,7 @@ Blockly.Workspace.prototype.clear = function() { if (this.potentialVariableMap_) { this.potentialVariableMap_.clear(); } + this.isClearing = false; }; /* Begin functions that are just pass-throughs to the variable map. */ From 099e15ab4c03af9fa36ad906956ddddf74430cf0 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Fri, 13 Apr 2018 11:22:44 -0400 Subject: [PATCH 0493/2135] Recycle flyout blocks --- core/block_svg.js | 39 ++++++++++++++++++++++++ core/flyout_base.js | 72 +++++++++++++++++++++++++++++++++++++++++++-- core/procedures.js | 1 + 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 9384c9fe7d..908f24e0dc 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1393,3 +1393,42 @@ Blockly.BlockSvg.prototype.scheduleSnapAndBump = function() { Blockly.Events.setGroup(false); }, Blockly.BUMP_DELAY); }; + +/** + * Determine if this block can be recycled, basic non-dynamic shadows and no variables + * @return {boolean} true if the block can be recycled + */ +Blockly.BlockSvg.prototype.isRecyclable = function() { + + // The procedures_call type is always dynamic as it can be mutated at runtime. + if (this.type === 'procedures_call') { + return false; + } + + for (var i = 0; i < this.inputList.length; i++) { + var input = this.inputList[i]; + for (var j = 0; j < input.fieldRow.length; j++) { + var field = input.fieldRow[j]; + // No variables. + if (field instanceof Blockly.FieldVariable || + field instanceof Blockly.FieldVariableGetter) { + return false; + } + if (field instanceof Blockly.FieldDropdown || + field instanceof Blockly.FieldNumberDropdown || + field instanceof Blockly.FieldTextDropdown) { + if (field.isOptionListDynamic()) { + return false; + } + } + } + // Check children. + if (input.connection) { + var child = input.connection.targetBlock(); + if (child && !child.isRecyclable()) { + return false; + } + } + } + return true; +}; diff --git a/core/flyout_base.js b/core/flyout_base.js index 15b06baf2d..c297620259 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -55,6 +55,14 @@ Blockly.Flyout = function(workspaceOptions) { this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions); this.workspace_.isFlyout = true; + // When we create blocks for this workspace, instead of using the "optional" id + // make the default `id` the same as the `type` for easier re-use. + var newBlock = this.workspace_.newBlock; + this.workspace_.newBlock = function(type, id) { + // Use `type` if `id` isn't passed. `this` will be workspace. + return newBlock.call(this, type, id || type); + }; + /** * Is RTL vs LTR. * @type {boolean} @@ -126,6 +134,14 @@ Blockly.Flyout = function(workspaceOptions) { * @package */ this.scrollTarget = null; + + /** + * A recycle bin for blocks. + * @type {!Array.} + * @private + */ + this.recycleBlocks_ = []; + }; /** @@ -467,7 +483,27 @@ Blockly.Flyout.prototype.show = function(xmlList) { var tagName = xml.tagName.toUpperCase(); var default_gap = this.horizontalLayout_ ? this.GAP_X : this.GAP_Y; if (tagName == 'BLOCK') { - var curBlock = Blockly.Xml.domToBlock(xml, this.workspace_); + + // We assume that in a flyout, the same block id (or type if missing id) means + // the same output BlockSVG. + + // Look for a block that matches the id or type, our createBlock will assign + // id = type if none existed. + var id = xml.getAttribute('id') || xml.getAttribute('type'); + var recycled = this.recycleBlocks_.findIndex(function(block) { + return block.id === id; + }); + + + // If we found a recycled item, reuse the BlockSVG from last time. + // Otherwise, convert the XML block to a BlockSVG. + var curBlock; + if (recycled > -1) { + curBlock = this.recycleBlocks_.splice(recycled, 1)[0]; + } else { + curBlock = Blockly.Xml.domToBlock(xml, this.workspace_); + } + if (curBlock.disabled) { // Record blocks that were initially disabled. // Do not enable these blocks as a result of capacity filtering. @@ -501,6 +537,8 @@ Blockly.Flyout.prototype.show = function(xmlList) { } } + this.emptyRecycleBlocks_(); + this.layout_(contents, gaps); // IE 11 is an incompetent browser that fails to fire mouseout events. @@ -527,6 +565,19 @@ Blockly.Flyout.prototype.show = function(xmlList) { this.recordCategoryScrollPositions_(); }; +/** + * Empty out the recycled blocks, properly destroying everything. + * @private + */ +Blockly.Flyout.prototype.emptyRecycleBlocks_ = function() { + // Clean out the old recycle bin. + var oldBlocks = this.recycleBlocks_; + this.recycleBlocks_ = []; + for (var i = 0; i < oldBlocks.length; i++) { + oldBlocks[i].dispose(false, false); + } +}; + /** * Store an array of category names, scrollbar positions, and category lengths. * This is used when scrolling the flyout to cause a category to be selected. @@ -626,7 +677,11 @@ Blockly.Flyout.prototype.clearOldBlocks_ = function() { var oldBlocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; block = oldBlocks[i]; i++) { if (block.workspace == this.workspace_) { - block.dispose(false, false); + if (block.isRecyclable()) { + this.recycleBlock_(block); + } else { + block.dispose(false, false); + } } } // Delete any background buttons from a previous showing. @@ -816,3 +871,16 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) { block.moveBy(finalOffsetMainWs.x, finalOffsetMainWs.y); return block; }; + +/** + * Put a previously created block into the recycle bin, used during large + * workspace swaps to limit the number of new dom elements we need to create + * + * @param {!Blockly.BlockSvg} block The block to recycle. + * @private + */ +Blockly.Flyout.prototype.recycleBlock_ = function(block) { + var xy = block.getRelativeToSurfaceXY(); + block.moveBy(-xy.x, -xy.y); + this.recycleBlocks_.push(block); +}; diff --git a/core/procedures.js b/core/procedures.js index 8d219639ff..47e9616bd0 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -237,6 +237,7 @@ Blockly.Procedures.flyoutCategory = function(workspace) { // // var block = goog.dom.createDom('block'); + block.setAttribute('id', 'proccode:' + mutation.proccode ); block.setAttribute('type', 'procedures_call'); block.setAttribute('gap', 16); block.appendChild(mutation); From d0b33636c884660bc06bda5b8dd30e11645a8b84 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Fri, 4 May 2018 15:01:41 -0400 Subject: [PATCH 0494/2135] More general approach to procedures_call reasoning for being dynamic --- core/block_svg.js | 4 ++-- core/procedures.js | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index 908f24e0dc..b6fe0812ef 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1400,8 +1400,8 @@ Blockly.BlockSvg.prototype.scheduleSnapAndBump = function() { */ Blockly.BlockSvg.prototype.isRecyclable = function() { - // The procedures_call type is always dynamic as it can be mutated at runtime. - if (this.type === 'procedures_call') { + // If the block needs to parse mutations, it's probably safest to never recycle. + if (this.mutationToDom && this.domToMutation) { return false; } diff --git a/core/procedures.js b/core/procedures.js index 47e9616bd0..8d219639ff 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -237,7 +237,6 @@ Blockly.Procedures.flyoutCategory = function(workspace) { // // var block = goog.dom.createDom('block'); - block.setAttribute('id', 'proccode:' + mutation.proccode ); block.setAttribute('type', 'procedures_call'); block.setAttribute('gap', 16); block.appendChild(mutation); From 8c65088fa98cc0d1d1d97ac8cb30b670273e0128 Mon Sep 17 00:00:00 2001 From: Bill Shoener Date: Mon, 7 May 2018 09:21:00 -0500 Subject: [PATCH 0495/2135] Fixed lack of category identifiers When I was changing CSS via a userscript, I noticed that a few blocks were not being affected. Looking at the code I noticed that those specific blocks did not have the `"category": Blockly.Categories.sound,` property. This simple change should fix the problem. (There is one more block with this issue, and that is the "When backdrop switches to" block. I will also fix that in a hotsecond) --- blocks_vertical/sound.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blocks_vertical/sound.js b/blocks_vertical/sound.js index e9e9c530a0..a9d9f1b3df 100644 --- a/blocks_vertical/sound.js +++ b/blocks_vertical/sound.js @@ -95,6 +95,7 @@ Blockly.Blocks['sound_playuntildone'] = { "name": "SOUND_MENU" } ], + "category": Blockly.Categories.sound, "extensions": ["colours_sounds", "shape_statement"] }); } @@ -136,6 +137,7 @@ Blockly.Blocks['sound_seteffectto'] = { "name": "VALUE" } ], + "category": Blockly.Categories.sound, "extensions": ["colours_sounds", "shape_statement"] }); } @@ -164,6 +166,7 @@ Blockly.Blocks['sound_changeeffectby'] = { "name": "VALUE" } ], + "category": Blockly.Categories.sound, "extensions": ["colours_sounds", "shape_statement"] }); } @@ -177,6 +180,7 @@ Blockly.Blocks['sound_cleareffects'] = { init: function() { this.jsonInit({ "message0": Blockly.Msg.SOUND_CLEAREFFECTS, + "category": Blockly.Categories.sound, "extensions": ["colours_sounds", "shape_statement"] }); } From de3b84b0c7f4527e83d23d160a12ee6433cf9b5a Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 30 Apr 2018 17:33:54 -0400 Subject: [PATCH 0496/2135] chore(webpack): update to webpack 4 --- package.json | 6 ++-- ...compressed_horizontal-blocks_compressed.js | 2 +- shim/blockly_compressed_horizontal.js | 2 +- ...y_compressed_vertical-blocks_compressed.js | 2 +- shim/blockly_compressed_vertical.js | 2 +- shim/blocks_compressed_horizontal.js | 2 +- shim/blocks_compressed_vertical.js | 2 +- shim/gh-pages.js | 1 + shim/horizontal.js | 2 +- shim/vertical.js | 2 +- webpack.config.js | 29 ++++++++++++++++++- 11 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 shim/gh-pages.js diff --git a/package.json b/package.json index 10b2c077e1..163a6b854e 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "async": "2.6.0", - "copy-webpack-plugin": "4.0.1", + "copy-webpack-plugin": "^4.5.1", "eslint": "^4.16", "event-stream": "3.3.4", "exports-loader": "0.6.3", @@ -35,7 +35,9 @@ "json": "9.0.4", "rimraf": "2.6.2", "travis-after-all": "1.4.4", + "uglifyjs-webpack-plugin": "^1.2.5", "webdriverio": "4.8.0", - "webpack": "1.13.2" + "webpack": "^4.8.0", + "webpack-cli": "^2.0.15" } } diff --git a/shim/blockly_compressed_horizontal-blocks_compressed.js b/shim/blockly_compressed_horizontal-blocks_compressed.js index 71a555c618..32675cc413 100644 --- a/shim/blockly_compressed_horizontal-blocks_compressed.js +++ b/shim/blockly_compressed_horizontal-blocks_compressed.js @@ -1 +1 @@ -module.exports = require('imports?Blockly=./shim/blockly_compressed_horizontal.Blockly!exports?Blockly!../blocks_compressed'); +module.exports = require('imports-loader?Blockly=./shim/blockly_compressed_horizontal.Blockly!exports-loader?Blockly!../blocks_compressed'); diff --git a/shim/blockly_compressed_horizontal.js b/shim/blockly_compressed_horizontal.js index 94ea0f5204..ed83a67472 100644 --- a/shim/blockly_compressed_horizontal.js +++ b/shim/blockly_compressed_horizontal.js @@ -1 +1 @@ -module.exports = require('imports?this=>window!exports?Blockly&goog!../blockly_compressed_horizontal'); +module.exports = require('imports-loader?this=>window!exports-loader?Blockly&goog!../blockly_compressed_horizontal'); diff --git a/shim/blockly_compressed_vertical-blocks_compressed.js b/shim/blockly_compressed_vertical-blocks_compressed.js index 1aec6dee13..8eec1f68a2 100644 --- a/shim/blockly_compressed_vertical-blocks_compressed.js +++ b/shim/blockly_compressed_vertical-blocks_compressed.js @@ -1 +1 @@ -module.exports = require('imports?Blockly=./shim/blockly_compressed_vertical.Blockly!exports?Blockly!../blocks_compressed'); +module.exports = require('imports-loader?Blockly=./shim/blockly_compressed_vertical.Blockly!exports-loader?Blockly!../blocks_compressed'); diff --git a/shim/blockly_compressed_vertical.js b/shim/blockly_compressed_vertical.js index 22a28cfbe6..dce46a9de5 100644 --- a/shim/blockly_compressed_vertical.js +++ b/shim/blockly_compressed_vertical.js @@ -1 +1 @@ -module.exports = require('imports?this=>window!exports?Blockly&goog!../blockly_compressed_vertical'); +module.exports = require('imports-loader?this=>window!exports-loader?Blockly&goog!../blockly_compressed_vertical'); diff --git a/shim/blocks_compressed_horizontal.js b/shim/blocks_compressed_horizontal.js index 2d68d0478b..0c4272ae08 100644 --- a/shim/blocks_compressed_horizontal.js +++ b/shim/blocks_compressed_horizontal.js @@ -1 +1 @@ -module.exports = require('imports?Blockly=./shim/blockly_compressed_horizontal-blocks_compressed!exports?Blockly!../blocks_compressed_horizontal'); +module.exports = require('imports-loader?Blockly=./shim/blockly_compressed_horizontal-blocks_compressed!exports-loader?Blockly!../blocks_compressed_horizontal'); diff --git a/shim/blocks_compressed_vertical.js b/shim/blocks_compressed_vertical.js index 5938d38ec0..3368c09d03 100644 --- a/shim/blocks_compressed_vertical.js +++ b/shim/blocks_compressed_vertical.js @@ -1 +1 @@ -module.exports = require('imports?goog=./shim/blockly_compressed_vertical.goog,Blockly=./shim/blockly_compressed_vertical-blocks_compressed!exports?Blockly!../blocks_compressed_vertical'); +module.exports = require('imports-loader?goog=./shim/blockly_compressed_vertical.goog,Blockly=./shim/blockly_compressed_vertical-blocks_compressed!exports-loader?Blockly!../blocks_compressed_vertical'); diff --git a/shim/gh-pages.js b/shim/gh-pages.js new file mode 100644 index 0000000000..36db4933d9 --- /dev/null +++ b/shim/gh-pages.js @@ -0,0 +1 @@ +// intentionally left empty diff --git a/shim/horizontal.js b/shim/horizontal.js index 73970e9211..db666d0671 100644 --- a/shim/horizontal.js +++ b/shim/horizontal.js @@ -1 +1 @@ -module.exports = require('imports?Blockly=../shim/blocks_compressed_horizontal,goog=../shim/blockly_compressed_horizontal.goog!exports?Blockly!../msg/messages'); \ No newline at end of file +module.exports = require('imports-loader?Blockly=../shim/blocks_compressed_horizontal,goog=../shim/blockly_compressed_horizontal.goog!exports-loader?Blockly!../msg/messages'); \ No newline at end of file diff --git a/shim/vertical.js b/shim/vertical.js index dcba342da1..998b465f6a 100644 --- a/shim/vertical.js +++ b/shim/vertical.js @@ -1 +1 @@ -module.exports = require('imports?Blockly=../shim/blocks_compressed_vertical,goog=../shim/blockly_compressed_vertical.goog!exports?Blockly!../msg/messages'); \ No newline at end of file +module.exports = require('imports-loader?Blockly=../shim/blocks_compressed_vertical,goog=../shim/blockly_compressed_vertical.goog!exports-loader?Blockly!../msg/messages'); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index c74854d4da..6d3be1d18c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,8 +5,10 @@ gracefulFs.gracefulify(realFs); var CopyWebpackPlugin = require('copy-webpack-plugin'); var path = require('path'); +var UglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = [{ + mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', entry: { horizontal: './shim/horizontal.js', vertical: './shim/vertical.js' @@ -16,8 +18,15 @@ module.exports = [{ libraryTarget: 'commonjs2', path: path.resolve(__dirname, 'dist'), filename: '[name].js' + }, + optimization: { + minimize: false + }, + performance: { + hints: false } }, { + mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', entry: { horizontal: './shim/horizontal.js', vertical: './shim/vertical.js' @@ -27,13 +36,31 @@ module.exports = [{ libraryTarget: 'umd', path: path.resolve(__dirname, 'dist', 'web'), filename: '[name].js' - } + }, + optimization: { + minimizer: [ + new UglifyJsPlugin({ + uglifyOptions: { + mangle: false + } + }) + ] + }, + plugins: [] }, { + mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', + entry: './shim/gh-pages.js', output: { filename: '[name].js', path: path.resolve(__dirname, 'gh-pages') }, + optimization: { + minimize: false + }, + performance: { + hints: false + }, plugins: [ new CopyWebpackPlugin([{ from: 'node_modules/google-closure-library', From 625778739737f686c1a5243b761d16123cb61772 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Mon, 7 May 2018 15:37:13 -0400 Subject: [PATCH 0497/2135] update ctm logic to fit with upstream change --- core/constants.js | 6 ------ core/workspace_svg.js | 17 +++++++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/constants.js b/core/constants.js index d0a4e5570a..f98b1e62b0 100644 --- a/core/constants.js +++ b/core/constants.js @@ -366,9 +366,3 @@ Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE = 'procedures_prototype'; * @const {string} */ Blockly.PROCEDURES_CALL_BLOCK_TYPE = 'procedures_call'; - -/** - * A constant value that we can use to mark calculated properties as dirty. - * @const {string} - */ -Blockly.DIRTY = 'dirty'; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 33d9fe08a0..084105d4d3 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -285,7 +285,14 @@ Blockly.WorkspaceSvg.prototype.toolboxCategoryCallbacks_ = {}; * @type {SVGMatrix} * @private */ -Blockly.WorkspaceSvg.prototype.inverseScreenCTM_ = Blockly.DIRTY; +Blockly.WorkspaceSvg.prototype.inverseScreenCTM_ = null; + +/** + * Inverted screen CTM is dirty. + * @type {Boolean} + * @private + */ +Blockly.WorkspaceSvg.prototype.inverseScreenCTMDirty_ = true; /** * Getter for the inverted screen CTM. @@ -295,13 +302,11 @@ Blockly.WorkspaceSvg.prototype.getInverseScreenCTM = function() { // Defer getting the screen CTM until we actually need it, this should // avoid forced reflows from any calls to updateInverseScreenCTM. - if (this.inverseScreenCTM_ == Blockly.DIRTY) { + if (this.inverseScreenCTMDirty_) { var ctm = this.getParentSvg().getScreenCTM(); if (ctm) { this.inverseScreenCTM_ = ctm.inverse(); - } else { - // When dirty, and we can't get a CTM, set it to null. - this.inverseScreenCTM_ = null; + this.inverseScreenCTMDirty_ = false; } } @@ -312,7 +317,7 @@ Blockly.WorkspaceSvg.prototype.getInverseScreenCTM = function() { * Mark the inverse screen CTM as dirty. */ Blockly.WorkspaceSvg.prototype.updateInverseScreenCTM = function() { - this.inverseScreenCTM_ = Blockly.DIRTY; + this.inverseScreenCTMDirty_ = true; }; /** From 68bec36c2e6cbb3efea441457dd6c5fbdcb4cdcf Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 8 May 2018 12:53:25 -0400 Subject: [PATCH 0498/2135] Bump Travis Node version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7902d87d3a..3b60d72cce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: -- "4" +- "8" - "stable" sudo: required addons: From 4700e61ca893c1717235745c9ccf3af22bb4ed2c Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Tue, 8 May 2018 13:01:16 -0400 Subject: [PATCH 0499/2135] Pin copy-webpack-plugin --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 163a6b854e..8578c1d7e4 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "async": "2.6.0", - "copy-webpack-plugin": "^4.5.1", + "copy-webpack-plugin": "4.5.1", "eslint": "^4.16", "event-stream": "3.3.4", "exports-loader": "0.6.3", From 4f9c266563497501e84a17529f5575202218843b Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Mon, 30 Apr 2018 17:38:48 -0400 Subject: [PATCH 0500/2135] chore(package): expose src/index as browser entry point Downstream webpack will need any dependencies src/ depends on so it can successfully build. Also if multiple packages being built into a larger script share a common dependency version range, they can share the dependency instead of duplicating it. This will save built file size, execution time, and memory. --- package.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8578c1d7e4..c049f3d39e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "url": "https://github.com/LLK/scratch-blocks.git" }, "main": "./dist/vertical.js", + "browser": "./shim/vertical.js", "scripts": { "deploy": "rimraf gh-pages/closure-library/scripts/ci/CloseAdobeDialog.exe && gh-pages -t -d gh-pages -m \"Build for $(git log --pretty=format:%H -n1)\"", "prepublish": "python build.py && webpack", @@ -20,18 +21,20 @@ "version": "json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"", "translate": "node i18n/js_to_json.js && node i18n/json_to_js.js" }, + "dependencies": { + "exports-loader": "0.6.3", + "imports-loader": "0.6.5" + }, "devDependencies": { "async": "2.6.0", "copy-webpack-plugin": "4.5.1", "eslint": "^4.16", "event-stream": "3.3.4", - "exports-loader": "0.6.3", "gh-pages": "0.12.0", "glob": "7.1.2", "google-closure-compiler": "20180402.0.0", "google-closure-library": "20180204.0.0", "graceful-fs": "4.1.11", - "imports-loader": "0.6.5", "json": "9.0.4", "rimraf": "2.6.2", "travis-after-all": "1.4.4", From 904231694ba6f1485a7d98b7655c99fa5af19214 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 9 May 2018 14:37:02 -0400 Subject: [PATCH 0501/2135] Show the show/hide blocks in the data category now that they are done. --- core/data_category.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/data_category.js b/core/data_category.js index 7a109058bd..4be0eb02bf 100644 --- a/core/data_category.js +++ b/core/data_category.js @@ -57,9 +57,8 @@ Blockly.DataCategory = function(workspace) { Blockly.DataCategory.addSetVariableTo(xmlList, firstVariable); Blockly.DataCategory.addChangeVariableBy(xmlList, firstVariable); - // TODO (#1276): uncomment these when their implementations are finished. - // Blockly.DataCategory.addShowVariable(xmlList, firstVariable); - // Blockly.DataCategory.addHideVariable(xmlList, firstVariable); + Blockly.DataCategory.addShowVariable(xmlList, firstVariable); + Blockly.DataCategory.addHideVariable(xmlList, firstVariable); } // Now add list variables to the flyout @@ -82,9 +81,8 @@ Blockly.DataCategory = function(workspace) { Blockly.DataCategory.addItemNumberOfList(xmlList, firstVariable); Blockly.DataCategory.addLengthOfList(xmlList, firstVariable); Blockly.DataCategory.addListContainsItem(xmlList, firstVariable); - // TODO (#1276): uncomment these when their implementations are finished. - // Blockly.DataCategory.addShowList(xmlList, firstVariable); - // Blockly.DataCategory.addHideList(xmlList, firstVariable); + Blockly.DataCategory.addShowList(xmlList, firstVariable); + Blockly.DataCategory.addHideList(xmlList, firstVariable); } return xmlList; From 4836e555368393b8d8102b15f1e07cdb2d67f556 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 9 May 2018 13:34:21 -0700 Subject: [PATCH 0502/2135] Break events.js into multiple files --- core/block.js | 4 + core/block_dragger.js | 1 + core/block_events.js | 526 ++++++++++++++++++++++ core/block_svg.js | 2 + core/comment.js | 2 + core/connection.js | 2 + core/contextmenu.js | 2 + core/events.js | 747 ------------------------------- core/events_abstract.js | 111 +++++ core/field.js | 1 + core/flyout_base.js | 2 + core/gesture.js | 2 +- core/insertion_marker_manager.js | 2 +- core/mutator.js | 2 + core/procedures.js | 1 + core/toolbox.js | 1 + core/ui_events.js | 91 ++++ core/variable_events.js | 247 ++++++++++ core/variable_map.js | 3 +- core/variable_model.js | 2 + core/warning.js | 1 + core/workspace_svg.js | 2 +- core/xml.js | 3 + 23 files changed, 1006 insertions(+), 751 deletions(-) create mode 100644 core/block_events.js create mode 100644 core/events_abstract.js create mode 100644 core/ui_events.js create mode 100644 core/variable_events.js diff --git a/core/block.js b/core/block.js index 4cdcbf4b71..f50f7b41be 100644 --- a/core/block.js +++ b/core/block.js @@ -30,6 +30,10 @@ goog.require('Blockly.Blocks'); goog.require('Blockly.Colours'); goog.require('Blockly.Comment'); goog.require('Blockly.Connection'); +goog.require('Blockly.Events.BlockChange'); +goog.require('Blockly.Events.BlockCreate'); +goog.require('Blockly.Events.BlockDelete'); +goog.require('Blockly.Events.BlockMove'); goog.require('Blockly.Extensions'); goog.require('Blockly.FieldLabelSerializable'); goog.require('Blockly.FieldVariableGetter'); diff --git a/core/block_dragger.js b/core/block_dragger.js index 75a2e5ab9e..5e50f40dae 100644 --- a/core/block_dragger.js +++ b/core/block_dragger.js @@ -27,6 +27,7 @@ goog.provide('Blockly.BlockDragger'); goog.require('Blockly.BlockAnimations'); +goog.require('Blockly.Events.BlockMove'); goog.require('Blockly.InsertionMarkerManager'); goog.require('goog.math.Coordinate'); diff --git a/core/block_events.js b/core/block_events.js new file mode 100644 index 0000000000..131fb5d0d0 --- /dev/null +++ b/core/block_events.js @@ -0,0 +1,526 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Classes for all types of block events. + * @author fenichel@google.com (Rachel Fenichel) + */ +'use strict'; + +goog.provide('Blockly.Events.BlockBase'); +goog.provide('Blockly.Events.BlockChange'); +goog.provide('Blockly.Events.BlockCreate'); +goog.provide('Blockly.Events.BlockDelete'); +goog.provide('Blockly.Events.BlockMove'); +goog.provide('Blockly.Events.Change'); // Deprecated. +goog.provide('Blockly.Events.Create'); // Deprecated. +goog.provide('Blockly.Events.Delete'); // Deprecated. +goog.provide('Blockly.Events.Move'); // Deprecated. + +goog.require('Blockly.Events'); +goog.require('Blockly.Events.Abstract'); + +goog.require('goog.array'); +goog.require('goog.math.Coordinate'); + + +/** + * Abstract class for a block event. + * @param {Blockly.Block} block The block this event corresponds to. + * @extends {Blockly.Events.Abstract} + * @constructor + */ +Blockly.Events.BlockBase = function(block) { + Blockly.Events.BlockBase.superClass_.constructor.call(this); + + /** + * The block id for the block this event pertains to + * @type {string} + */ + this.blockId = block.id; + this.workspaceId = block.workspace.id; +}; +goog.inherits(Blockly.Events.BlockBase, Blockly.Events.Abstract); + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.BlockBase.prototype.toJson = function() { + var json = Blockly.Events.BlockBase.superClass_.toJson.call(this); + json['blockId'] = this.blockId; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.BlockBase.prototype.fromJson = function(json) { + Blockly.Events.BlockBase.superClass_.toJson.call(this); + this.blockId = json['blockId']; +}; + +/** + * Class for a block change event. + * @param {Blockly.Block} block The changed block. Null for a blank event. + * @param {string} element One of 'field', 'comment', 'disabled', etc. + * @param {?string} name Name of input or field affected, or null. + * @param {*} oldValue Previous value of element. + * @param {*} newValue New value of element. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.Change = function(block, element, name, oldValue, newValue) { + if (!block) { + return; // Blank event to be populated by fromJson. + } + Blockly.Events.Change.superClass_.constructor.call(this, block); + this.element = element; + this.name = name; + this.oldValue = oldValue; + this.newValue = newValue; +}; +goog.inherits(Blockly.Events.Change, Blockly.Events.BlockBase); + +/** + * Class for a block change event. + * @param {Blockly.Block} block The changed block. Null for a blank event. + * @param {string} element One of 'field', 'comment', 'disabled', etc. + * @param {?string} name Name of input or field affected, or null. + * @param {*} oldValue Previous value of element. + * @param {*} newValue New value of element. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.BlockChange = Blockly.Events.Change; + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Change.prototype.type = Blockly.Events.CHANGE; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.Change.prototype.toJson = function() { + var json = Blockly.Events.Change.superClass_.toJson.call(this); + json['element'] = this.element; + if (this.name) { + json['name'] = this.name; + } + json['newValue'] = this.newValue; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.Change.prototype.fromJson = function(json) { + Blockly.Events.Change.superClass_.fromJson.call(this, json); + this.element = json['element']; + this.name = json['name']; + this.newValue = json['newValue']; +}; + +/** + * Does this event record any change of state? + * @return {boolean} True if something changed. + */ +Blockly.Events.Change.prototype.isNull = function() { + return this.oldValue == this.newValue; +}; + +/** + * Run a change event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ +Blockly.Events.Change.prototype.run = function(forward) { + var workspace = this.getEventWorkspace_(); + var block = workspace.getBlockById(this.blockId); + if (!block) { + console.warn("Can't change non-existent block: " + this.blockId); + return; + } + if (block.mutator) { + // Close the mutator (if open) since we don't want to update it. + block.mutator.setVisible(false); + } + var value = forward ? this.newValue : this.oldValue; + switch (this.element) { + case 'field': + var field = block.getField(this.name); + if (field) { + // Run the validator for any side-effects it may have. + // The validator's opinion on validity is ignored. + field.callValidator(value); + field.setValue(value); + } else { + console.warn("Can't set non-existent field: " + this.name); + } + break; + case 'comment': + block.setCommentText(value || null); + break; + case 'collapsed': + block.setCollapsed(value); + break; + case 'disabled': + block.setDisabled(value); + break; + case 'inline': + block.setInputsInline(value); + break; + case 'mutation': + var oldMutation = ''; + if (block.mutationToDom) { + var oldMutationDom = block.mutationToDom(); + oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); + } + if (block.domToMutation) { + value = value || ''; + var dom = Blockly.Xml.textToDom('' + value + ''); + block.domToMutation(dom.firstChild); + } + Blockly.Events.fire(new Blockly.Events.Change( + block, 'mutation', null, oldMutation, value)); + break; + default: + console.warn('Unknown change type: ' + this.element); + } +}; + +/** + * Class for a block creation event. + * @param {Blockly.Block} block The created block. Null for a blank event. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.Create = function(block) { + if (!block) { + return; // Blank event to be populated by fromJson. + } + Blockly.Events.Create.superClass_.constructor.call(this, block); + + if (block.workspace.rendered) { + this.xml = Blockly.Xml.blockToDomWithXY(block); + } else { + this.xml = Blockly.Xml.blockToDom(block); + } + this.ids = Blockly.Events.getDescendantIds_(block); +}; +goog.inherits(Blockly.Events.Create, Blockly.Events.BlockBase); + +/** + * Class for a block creation event. + * @param {Blockly.Block} block The created block. Null for a blank event. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.BlockCreate = Blockly.Events.Create; + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Create.prototype.type = Blockly.Events.CREATE; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.Create.prototype.toJson = function() { + var json = Blockly.Events.Create.superClass_.toJson.call(this); + json['xml'] = Blockly.Xml.domToText(this.xml); + json['ids'] = this.ids; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.Create.prototype.fromJson = function(json) { + Blockly.Events.Create.superClass_.fromJson.call(this, json); + this.xml = Blockly.Xml.textToDom('' + json['xml'] + '').firstChild; + this.ids = json['ids']; +}; + +/** + * Run a creation event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ +Blockly.Events.Create.prototype.run = function(forward) { + var workspace = this.getEventWorkspace_(); + if (forward) { + var xml = goog.dom.createDom('xml'); + xml.appendChild(this.xml); + Blockly.Xml.domToWorkspace(xml, workspace); + } else { + for (var i = 0, id; id = this.ids[i]; i++) { + var block = workspace.getBlockById(id); + if (block) { + block.dispose(false, false); + } else if (id == this.blockId) { + // Only complain about root-level block. + console.warn("Can't uncreate non-existent block: " + id); + } + } + } +}; + +/** + * Class for a block deletion event. + * @param {Blockly.Block} block The deleted block. Null for a blank event. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.Delete = function(block) { + if (!block) { + return; // Blank event to be populated by fromJson. + } + if (block.getParent()) { + throw 'Connected blocks cannot be deleted.'; + } + Blockly.Events.Delete.superClass_.constructor.call(this, block); + + if (block.workspace.rendered) { + this.oldXml = Blockly.Xml.blockToDomWithXY(block); + } else { + this.oldXml = Blockly.Xml.blockToDom(block); + } + this.ids = Blockly.Events.getDescendantIds_(block); +}; +goog.inherits(Blockly.Events.Delete, Blockly.Events.BlockBase); + +/** + * Class for a block deletion event. + * @param {Blockly.Block} block The deleted block. Null for a blank event. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.BlockDelete = Blockly.Events.Delete; + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Delete.prototype.type = Blockly.Events.DELETE; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.Delete.prototype.toJson = function() { + var json = Blockly.Events.Delete.superClass_.toJson.call(this); + json['ids'] = this.ids; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.Delete.prototype.fromJson = function(json) { + Blockly.Events.Delete.superClass_.fromJson.call(this, json); + this.ids = json['ids']; +}; + +/** + * Run a deletion event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ +Blockly.Events.Delete.prototype.run = function(forward) { + var workspace = this.getEventWorkspace_(); + if (forward) { + for (var i = 0, id; id = this.ids[i]; i++) { + var block = workspace.getBlockById(id); + if (block) { + block.dispose(false, false); + } else if (id == this.blockId) { + // Only complain about root-level block. + console.warn("Can't delete non-existent block: " + id); + } + } + } else { + var xml = goog.dom.createDom('xml'); + xml.appendChild(this.oldXml); + Blockly.Xml.domToWorkspace(xml, workspace); + } +}; + +/** + * Class for a block move event. Created before the move. + * @param {Blockly.Block} block The moved block. Null for a blank event. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.Move = function(block) { + if (!block) { + return; // Blank event to be populated by fromJson. + } + Blockly.Events.Move.superClass_.constructor.call(this, block); + var location = this.currentLocation_(); + this.oldParentId = location.parentId; + this.oldInputName = location.inputName; + this.oldCoordinate = location.coordinate; +}; +goog.inherits(Blockly.Events.Move, Blockly.Events.BlockBase); + +/** + * Class for a block move event. Created before the move. + * @param {Blockly.Block} block The moved block. Null for a blank event. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.BlockMove = Blockly.Events.Move; + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Move.prototype.type = Blockly.Events.MOVE; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.Move.prototype.toJson = function() { + var json = Blockly.Events.Move.superClass_.toJson.call(this); + if (this.newParentId) { + json['newParentId'] = this.newParentId; + } + if (this.newInputName) { + json['newInputName'] = this.newInputName; + } + if (this.newCoordinate) { + json['newCoordinate'] = Math.round(this.newCoordinate.x) + ',' + + Math.round(this.newCoordinate.y); + } + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.Move.prototype.fromJson = function(json) { + Blockly.Events.Move.superClass_.fromJson.call(this, json); + this.newParentId = json['newParentId']; + this.newInputName = json['newInputName']; + if (json['newCoordinate']) { + var xy = json['newCoordinate'].split(','); + this.newCoordinate = + new goog.math.Coordinate(parseFloat(xy[0]), parseFloat(xy[1])); + } +}; + +/** + * Record the block's new location. Called after the move. + */ +Blockly.Events.Move.prototype.recordNew = function() { + var location = this.currentLocation_(); + this.newParentId = location.parentId; + this.newInputName = location.inputName; + this.newCoordinate = location.coordinate; +}; + +/** + * Returns the parentId and input if the block is connected, + * or the XY location if disconnected. + * @return {!Object} Collection of location info. + * @private + */ +Blockly.Events.Move.prototype.currentLocation_ = function() { + var workspace = Blockly.Workspace.getById(this.workspaceId); + var block = workspace.getBlockById(this.blockId); + var location = {}; + var parent = block.getParent(); + if (parent) { + location.parentId = parent.id; + var input = parent.getInputWithBlock(block); + if (input) { + location.inputName = input.name; + } + } else { + location.coordinate = block.getRelativeToSurfaceXY(); + } + return location; +}; + +/** + * Does this event record any change of state? + * @return {boolean} True if something changed. + */ +Blockly.Events.Move.prototype.isNull = function() { + return this.oldParentId == this.newParentId && + this.oldInputName == this.newInputName && + goog.math.Coordinate.equals(this.oldCoordinate, this.newCoordinate); +}; + +/** + * Run a move event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ +Blockly.Events.Move.prototype.run = function(forward) { + var workspace = this.getEventWorkspace_(); + var block = workspace.getBlockById(this.blockId); + if (!block) { + console.warn("Can't move non-existent block: " + this.blockId); + return; + } + var parentId = forward ? this.newParentId : this.oldParentId; + var inputName = forward ? this.newInputName : this.oldInputName; + var coordinate = forward ? this.newCoordinate : this.oldCoordinate; + var parentBlock = null; + if (parentId) { + parentBlock = workspace.getBlockById(parentId); + if (!parentBlock) { + console.warn("Can't connect to non-existent block: " + parentId); + return; + } + } + if (block.getParent()) { + block.unplug(); + } + if (coordinate) { + var xy = block.getRelativeToSurfaceXY(); + block.moveBy(coordinate.x - xy.x, coordinate.y - xy.y); + } else { + var blockConnection = block.outputConnection || block.previousConnection; + var parentConnection; + if (inputName) { + var input = parentBlock.getInput(inputName); + if (input) { + parentConnection = input.connection; + } + } else if (blockConnection.type == Blockly.PREVIOUS_STATEMENT) { + parentConnection = parentBlock.nextConnection; + } + if (parentConnection) { + blockConnection.connect(parentConnection); + } else { + console.warn("Can't connect to non-existent input: " + inputName); + } + } +}; diff --git a/core/block_svg.js b/core/block_svg.js index b6fe0812ef..9a49e69583 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -29,6 +29,8 @@ goog.provide('Blockly.BlockSvg'); goog.require('Blockly.Block'); goog.require('Blockly.BlockAnimations'); goog.require('Blockly.ContextMenu'); +goog.require('Blockly.Events.Ui'); +goog.require('Blockly.Events.BlockMove'); goog.require('Blockly.Grid'); goog.require('Blockly.RenderedConnection'); goog.require('Blockly.scratchBlocksUtils'); diff --git a/core/comment.js b/core/comment.js index eab5408c8b..fc631d47f9 100644 --- a/core/comment.js +++ b/core/comment.js @@ -27,6 +27,8 @@ goog.provide('Blockly.Comment'); goog.require('Blockly.Bubble'); +goog.require('Blockly.Events.BlockChange'); +goog.require('Blockly.Events.Ui'); goog.require('Blockly.Icon'); goog.require('goog.userAgent'); diff --git a/core/connection.js b/core/connection.js index a34319ffd5..0962693e0e 100644 --- a/core/connection.js +++ b/core/connection.js @@ -26,6 +26,8 @@ goog.provide('Blockly.Connection'); +goog.require('Blockly.Events.BlockMove'); + goog.require('goog.asserts'); goog.require('goog.dom'); diff --git a/core/contextmenu.js b/core/contextmenu.js index 28f303185a..7984ad0e22 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -30,6 +30,7 @@ */ goog.provide('Blockly.ContextMenu'); +goog.require('Blockly.Events.BlockCreate'); goog.require('Blockly.utils'); goog.require('Blockly.utils.uiMenu'); @@ -38,6 +39,7 @@ goog.require('goog.events'); goog.require('goog.style'); goog.require('goog.ui.Menu'); goog.require('goog.ui.MenuItem'); +goog.require('goog.userAgent'); /** diff --git a/core/events.js b/core/events.js index e5cddd73c6..0eb678ab8b 100644 --- a/core/events.js +++ b/core/events.js @@ -347,534 +347,6 @@ Blockly.Events.fromJson = function(json, workspace) { return event; }; -/** - * Abstract class for an event. - * @param {Blockly.Block|Blockly.VariableModel} elem The block or variable. - * @constructor - */ -Blockly.Events.Abstract = function(elem) { - if (elem instanceof Blockly.Block) { - this.blockId = elem.id; - this.workspaceId = elem.workspace.id; - } - else if (elem instanceof Blockly.VariableModel){ - this.workspaceId = elem.workspace.id; - this.varId = elem.getId(); - } - this.group = Blockly.Events.group_; - this.recordUndo = Blockly.Events.recordUndo; -}; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Abstract.prototype.toJson = function() { - var json = { - 'type': this.type - }; - if (this.blockId) { - json['blockId'] = this.blockId; - } - if (this.varId) { - json['varId'] = this.varId; - } - if (this.group) { - json['group'] = this.group; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Abstract.prototype.fromJson = function(json) { - this.blockId = json['blockId']; - this.varId = json['varId']; - this.group = json['group']; -}; - -/** - * Does this event record any change of state? - * @return {boolean} True if null, false if something changed. - */ -Blockly.Events.Abstract.prototype.isNull = function() { - return false; -}; - -/** - * Run an event. - * @param {boolean} forward True if run forward, false if run backward (undo). - * @abstract - */ -Blockly.Events.Abstract.prototype.run = function(/*forward*/) { - // Defined by subclasses. -}; - -/** - * Get workspace the event belongs to. - * @return {Blockly.Workspace} The workspace the event belongs to. - * @throws {Error} if workspace is null. - * @private - */ -Blockly.Events.Abstract.prototype.getEventWorkspace_ = function() { - var workspace = Blockly.Workspace.getById(this.workspaceId); - if (!workspace) { - throw Error('Workspace is null. Event must have been generated from real' + - ' Blockly events.'); - } - return workspace; -}; - -/** - * Class for a block creation event. - * @param {Blockly.Block} block The created block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.Create = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.Create.superClass_.constructor.call(this, block); - - if (block.workspace.rendered) { - this.xml = Blockly.Xml.blockToDomWithXY(block); - } else { - this.xml = Blockly.Xml.blockToDom(block); - } - this.ids = Blockly.Events.getDescendantIds_(block); -}; -goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract); - -/** - * Class for a block creation event. - * @param {Blockly.Block} block The created block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.BlockCreate = Blockly.Events.Create; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Create.prototype.type = Blockly.Events.CREATE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Create.prototype.toJson = function() { - var json = Blockly.Events.Create.superClass_.toJson.call(this); - json['xml'] = Blockly.Xml.domToText(this.xml); - json['ids'] = this.ids; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Create.prototype.fromJson = function(json) { - Blockly.Events.Create.superClass_.fromJson.call(this, json); - this.xml = Blockly.Xml.textToDom('' + json['xml'] + '').firstChild; - this.ids = json['ids']; -}; - -/** - * Run a creation event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Create.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - var xml = goog.dom.createDom('xml'); - xml.appendChild(this.xml); - Blockly.Xml.domToWorkspace(xml, workspace); - } else { - for (var i = 0, id; id = this.ids[i]; i++) { - var block = workspace.getBlockById(id); - if (block) { - block.dispose(false, false); - } else if (id == this.blockId) { - // Only complain about root-level block. - console.warn("Can't uncreate non-existant block: " + id); - } - } - } -}; - -/** - * Class for a block deletion event. - * @param {Blockly.Block} block The deleted block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.Delete = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - if (block.getParent()) { - throw 'Connected blocks cannot be deleted.'; - } - Blockly.Events.Delete.superClass_.constructor.call(this, block); - - if (block.workspace.rendered) { - this.oldXml = Blockly.Xml.blockToDomWithXY(block); - } else { - this.oldXml = Blockly.Xml.blockToDom(block); - } - this.ids = Blockly.Events.getDescendantIds_(block); -}; -goog.inherits(Blockly.Events.Delete, Blockly.Events.Abstract); - -/** - * Class for a block deletion event. - * @param {Blockly.Block} block The deleted block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.BlockDelete = Blockly.Events.Delete; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Delete.prototype.type = Blockly.Events.DELETE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Delete.prototype.toJson = function() { - var json = Blockly.Events.Delete.superClass_.toJson.call(this); - json['ids'] = this.ids; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Delete.prototype.fromJson = function(json) { - Blockly.Events.Delete.superClass_.fromJson.call(this, json); - this.ids = json['ids']; -}; - -/** - * Run a deletion event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Delete.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - for (var i = 0, id; id = this.ids[i]; i++) { - var block = workspace.getBlockById(id); - if (block) { - block.dispose(false, false); - } else if (id == this.blockId) { - // Only complain about root-level block. - console.warn("Can't delete non-existant block: " + id); - } - } - } else { - var xml = goog.dom.createDom('xml'); - xml.appendChild(this.oldXml); - Blockly.Xml.domToWorkspace(xml, workspace); - } -}; - -/** - * Class for a block change event. - * @param {Blockly.Block} block The changed block. Null for a blank event. - * @param {string} element One of 'field', 'comment', 'disabled', etc. - * @param {?string} name Name of input or field affected, or null. - * @param {string} oldValue Previous value of element. - * @param {string} newValue New value of element. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.Change = function(block, element, name, oldValue, newValue) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.Change.superClass_.constructor.call(this, block); - this.element = element; - this.name = name; - this.oldValue = oldValue; - this.newValue = newValue; -}; -goog.inherits(Blockly.Events.Change, Blockly.Events.Abstract); - -/** - * Class for a block change event. - * @param {Blockly.Block} block The changed block. Null for a blank event. - * @param {string} element One of 'field', 'comment', 'disabled', etc. - * @param {?string} name Name of input or field affected, or null. - * @param {string} oldValue Previous value of element. - * @param {string} newValue New value of element. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.BlockChange = Blockly.Events.Change; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Change.prototype.type = Blockly.Events.CHANGE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Change.prototype.toJson = function() { - var json = Blockly.Events.Change.superClass_.toJson.call(this); - json['element'] = this.element; - if (this.name) { - json['name'] = this.name; - } - json['newValue'] = this.newValue; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Change.prototype.fromJson = function(json) { - Blockly.Events.Change.superClass_.fromJson.call(this, json); - this.element = json['element']; - this.name = json['name']; - this.newValue = json['newValue']; -}; - -/** - * Does this event record any change of state? - * @return {boolean} True if something changed. - */ -Blockly.Events.Change.prototype.isNull = function() { - return this.oldValue == this.newValue; -}; - -/** - * Run a change event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Change.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - var block = workspace.getBlockById(this.blockId); - if (!block) { - console.warn("Can't change non-existant block: " + this.blockId); - return; - } - if (block.mutator) { - // Close the mutator (if open) since we don't want to update it. - block.mutator.setVisible(false); - } - var value = forward ? this.newValue : this.oldValue; - switch (this.element) { - case 'field': - var field = block.getField(this.name); - if (field) { - // Run the validator for any side-effects it may have. - // The validator's opinion on validity is ignored. - field.callValidator(value); - field.setValue(value); - } else { - console.warn("Can't set non-existant field: " + this.name); - } - break; - case 'comment': - block.setCommentText(value || null); - break; - case 'collapsed': - block.setCollapsed(value); - break; - case 'disabled': - block.setDisabled(value); - break; - case 'inline': - block.setInputsInline(value); - break; - case 'mutation': - var oldMutation = ''; - if (block.mutationToDom) { - var oldMutationDom = block.mutationToDom(); - oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); - } - if (block.domToMutation) { - value = value || ''; - var dom = Blockly.Xml.textToDom('' + value + ''); - block.domToMutation(dom.firstChild); - } - Blockly.Events.fire(new Blockly.Events.Change( - block, 'mutation', null, oldMutation, value)); - break; - default: - console.warn('Unknown change type: ' + this.element); - } -}; - -/** - * Class for a block move event. Created before the move. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.Move = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.Move.superClass_.constructor.call(this, block); - var location = this.currentLocation_(); - this.oldParentId = location.parentId; - this.oldInputName = location.inputName; - this.oldCoordinate = location.coordinate; -}; -goog.inherits(Blockly.Events.Move, Blockly.Events.Abstract); - - -/** - * Class for a block move event. Created before the move. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.BlockMove = Blockly.Events.Move; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Move.prototype.type = Blockly.Events.MOVE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Move.prototype.toJson = function() { - var json = Blockly.Events.Move.superClass_.toJson.call(this); - if (this.newParentId) { - json['newParentId'] = this.newParentId; - } - if (this.newInputName) { - json['newInputName'] = this.newInputName; - } - if (this.newCoordinate) { - json['newCoordinate'] = Math.round(this.newCoordinate.x) + ',' + - Math.round(this.newCoordinate.y); - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Move.prototype.fromJson = function(json) { - Blockly.Events.Move.superClass_.fromJson.call(this, json); - this.newParentId = json['newParentId']; - this.newInputName = json['newInputName']; - if (json['newCoordinate']) { - var xy = json['newCoordinate'].split(','); - this.newCoordinate = - new goog.math.Coordinate(parseFloat(xy[0]), parseFloat(xy[1])); - } -}; - -/** - * Record the block's new location. Called after the move. - */ -Blockly.Events.Move.prototype.recordNew = function() { - var location = this.currentLocation_(); - this.newParentId = location.parentId; - this.newInputName = location.inputName; - this.newCoordinate = location.coordinate; -}; - -/** - * Returns the parentId and input if the block is connected, - * or the XY location if disconnected. - * @return {!Object} Collection of location info. - * @private - */ -Blockly.Events.Move.prototype.currentLocation_ = function() { - var workspace = Blockly.Workspace.getById(this.workspaceId); - var block = workspace.getBlockById(this.blockId); - var location = {}; - var parent = block.getParent(); - if (parent) { - location.parentId = parent.id; - var input = parent.getInputWithBlock(block); - if (input) { - location.inputName = input.name; - } - } else { - location.coordinate = block.getRelativeToSurfaceXY(); - } - return location; -}; - -/** - * Does this event record any change of state? - * @return {boolean} True if something changed. - */ -Blockly.Events.Move.prototype.isNull = function() { - return this.oldParentId == this.newParentId && - this.oldInputName == this.newInputName && - goog.math.Coordinate.equals(this.oldCoordinate, this.newCoordinate); -}; - -/** - * Run a move event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Move.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - var block = workspace.getBlockById(this.blockId); - if (!block) { - console.warn("Can't move non-existant block: " + this.blockId); - return; - } - var parentId = forward ? this.newParentId : this.oldParentId; - var inputName = forward ? this.newInputName : this.oldInputName; - var coordinate = forward ? this.newCoordinate : this.oldCoordinate; - var parentBlock = null; - if (parentId) { - parentBlock = workspace.getBlockById(parentId); - if (!parentBlock) { - console.warn("Can't connect to non-existant block: " + parentId); - return; - } - } - if (block.getParent()) { - block.unplug(); - } - if (coordinate) { - var xy = block.getRelativeToSurfaceXY(); - block.moveBy(coordinate.x - xy.x, coordinate.y - xy.y); - } else { - var blockConnection = block.outputConnection || block.previousConnection; - var parentConnection; - if (inputName) { - var input = parentBlock.getInput(inputName); - if (input) { - parentConnection = input.connection; - } - } else if (blockConnection.type == Blockly.PREVIOUS_STATEMENT) { - parentConnection = parentBlock.nextConnection; - } - if (parentConnection) { - blockConnection.connect(parentConnection); - } else { - console.warn("Can't connect to non-existant input: " + inputName); - } - } -}; - /** * Class for a block drag event. Fired when block dragged into or out of * the blocks UI. @@ -1001,225 +473,6 @@ Blockly.Events.EndDrag.prototype.isNull = function() { return false; }; -/** - * Class for a UI event. - * @param {Blockly.Block} block The affected block. - * @param {string} element One of 'selected', 'comment', 'mutator', etc. - * @param {string} oldValue Previous value of element. - * @param {string} newValue New value of element. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.Ui = function(block, element, oldValue, newValue) { - Blockly.Events.Ui.superClass_.constructor.call(this, block); - this.element = element; - this.oldValue = oldValue; - this.newValue = newValue; - this.recordUndo = false; -}; -goog.inherits(Blockly.Events.Ui, Blockly.Events.Abstract); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Ui.prototype.type = Blockly.Events.UI; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Ui.prototype.toJson = function() { - var json = Blockly.Events.Ui.superClass_.toJson.call(this); - json['element'] = this.element; - if (this.newValue !== undefined) { - json['newValue'] = this.newValue; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Ui.prototype.fromJson = function(json) { - Blockly.Events.Ui.superClass_.fromJson.call(this, json); - this.element = json['element']; - this.newValue = json['newValue']; -}; - -/** - * Class for a variable creation event. - * @param {Blockly.VariableModel} variable The created variable. - * Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.VarCreate = function(variable) { - if (!variable) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.VarCreate.superClass_.constructor.call(this, variable); - this.varType = variable.type; - this.varName = variable.name; -}; -goog.inherits(Blockly.Events.VarCreate, Blockly.Events.Abstract); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.VarCreate.prototype.type = Blockly.Events.VAR_CREATE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.VarCreate.prototype.toJson = function() { - var json = Blockly.Events.VarCreate.superClass_.toJson.call(this); - json['varType'] = this.varType; - json['varName'] = this.varName; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.VarCreate.prototype.fromJson = function(json) { - Blockly.Events.VarCreate.superClass_.fromJson.call(this, json); - this.varType = json['varType']; - this.varName = json['varName']; -}; - -/** - * Run a variable creation event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.VarCreate.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - workspace.createVariable(this.varName, this.varType, this.varId); - } else { - workspace.deleteVariableById(this.varId); - } -}; - -/** - * Class for a variable deletion event. - * @param {Blockly.VariableModel} variable The deleted variable. - * Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.VarDelete = function(variable) { - if (!variable) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.VarDelete.superClass_.constructor.call(this, variable); - this.varType = variable.type; - this.varName = variable.name; -}; -goog.inherits(Blockly.Events.VarDelete, Blockly.Events.Abstract); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.VarDelete.prototype.type = Blockly.Events.VAR_DELETE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.VarDelete.prototype.toJson = function() { - var json = Blockly.Events.VarDelete.superClass_.toJson.call(this); - json['varType'] = this.varType; - json['varName'] = this.varName; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.VarDelete.prototype.fromJson = function(json) { - Blockly.Events.VarDelete.superClass_.fromJson.call(this, json); - this.varType = json['varType']; - this.varName = json['varName']; -}; - -/** - * Run a variable deletion event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.VarDelete.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - workspace.deleteVariableById(this.varId); - } else { - workspace.createVariable(this.varName, this.varType, this.varId); - } -}; - -/** - * Class for a variable rename event. - * @param {Blockly.VariableModel} variable The renamed variable. - * Null for a blank event. - * @param {string} newName The new name the variable will be changed to. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.VarRename = function(variable, newName) { - if (!variable) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.VarRename.superClass_.constructor.call(this, variable); - this.oldName = variable.name; - this.newName = newName; -}; -goog.inherits(Blockly.Events.VarRename, Blockly.Events.Abstract); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.VarRename.prototype.type = Blockly.Events.VAR_RENAME; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.VarRename.prototype.toJson = function() { - var json = Blockly.Events.VarRename.superClass_.toJson.call(this); - json['oldName'] = this.oldName; - json['newName'] = this.newName; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.VarRename.prototype.fromJson = function(json) { - Blockly.Events.VarRename.superClass_.fromJson.call(this, json); - this.oldName = json['oldName']; - this.newName = json['newName']; -}; - -/** - * Run a variable rename event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.VarRename.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - workspace.renameVariableById(this.varId, this.newName); - } else { - workspace.renameVariableById(this.varId, this.oldName); - } -}; - /** * Enable/disable a block depending on whether it is properly connected. * Use this on applications where all blocks should be connected to a top block. diff --git a/core/events_abstract.js b/core/events_abstract.js new file mode 100644 index 0000000000..17eea76d1b --- /dev/null +++ b/core/events_abstract.js @@ -0,0 +1,111 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Abstract class for events fired as a result of actions in + * Blockly's editor. + * @author fraser@google.com (Neil Fraser) + */ +'use strict'; + +goog.provide('Blockly.Events.Abstract'); + +goog.require('Blockly.Events'); +goog.require('goog.array'); +goog.require('goog.math.Coordinate'); + +/** + * Abstract class for an event. + * @constructor + */ +Blockly.Events.Abstract = function() { + /** + * The workspace identifier for this event. + * @type {string|undefined} + */ + this.workspaceId = undefined; + + /** + * The event group id for the group this event belongs to. Groups define + * events that should be treated as an single action from the user's + * perspective, and should be undone together. + * @type {string} + */ + this.group = Blockly.Events.group_; + + /** + * Sets whether the event should be added to the undo stack. + * @type {boolean} + */ + this.recordUndo = Blockly.Events.recordUndo; +}; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.Abstract.prototype.toJson = function() { + var json = { + 'type': this.type + }; + if (this.group) { + json['group'] = this.group; + } + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.Abstract.prototype.fromJson = function(json) { + this.group = json['group']; +}; + +/** + * Does this event record any change of state? + * @return {boolean} True if null, false if something changed. + */ +Blockly.Events.Abstract.prototype.isNull = function() { + return false; +}; + +/** + * Run an event. + * @param {boolean} _forward True if run forward, false if run backward (undo). + */ +Blockly.Events.Abstract.prototype.run = function(_forward) { + // Defined by subclasses. +}; + +/** + * Get workspace the event belongs to. + * @return {Blockly.Workspace} The workspace the event belongs to. + * @throws {Error} if workspace is null. + * @protected + */ +Blockly.Events.Abstract.prototype.getEventWorkspace_ = function() { + var workspace = Blockly.Workspace.getById(this.workspaceId); + if (!workspace) { + throw Error('Workspace is null. Event must have been generated from real' + + ' Blockly events.'); + } + return workspace; +}; diff --git a/core/field.js b/core/field.js index 2e08836739..510fa2bad2 100644 --- a/core/field.js +++ b/core/field.js @@ -28,6 +28,7 @@ goog.provide('Blockly.Field'); +goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.Gesture'); goog.require('goog.asserts'); diff --git a/core/flyout_base.js b/core/flyout_base.js index c297620259..022e7f0628 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -29,6 +29,8 @@ goog.provide('Blockly.Flyout'); goog.require('Blockly.Block'); goog.require('Blockly.Comment'); goog.require('Blockly.Events'); +goog.require('Blockly.Events.BlockCreate'); +goog.require('Blockly.Events.VarCreate'); goog.require('Blockly.FlyoutButton'); goog.require('Blockly.Gesture'); goog.require('Blockly.Touch'); diff --git a/core/gesture.js b/core/gesture.js index 2a82ce9f57..3d2370cd82 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -30,7 +30,7 @@ goog.provide('Blockly.Gesture'); goog.require('Blockly.BlockAnimations'); goog.require('Blockly.BlockDragger'); goog.require('Blockly.constants'); -goog.require('Blockly.Events'); +goog.require('Blockly.Events.Ui'); goog.require('Blockly.FlyoutDragger'); goog.require('Blockly.scratchBlocksUtils'); goog.require('Blockly.Tooltip'); diff --git a/core/insertion_marker_manager.js b/core/insertion_marker_manager.js index ba147c1668..ade8c3d432 100644 --- a/core/insertion_marker_manager.js +++ b/core/insertion_marker_manager.js @@ -27,7 +27,7 @@ goog.provide('Blockly.InsertionMarkerManager'); goog.require('Blockly.BlockAnimations'); -goog.require('Blockly.Events'); +goog.require('Blockly.Events.BlockMove'); goog.require('Blockly.RenderedConnection'); goog.require('goog.math.Coordinate'); diff --git a/core/mutator.js b/core/mutator.js index 95cfa6a708..e51018af4d 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -28,6 +28,8 @@ goog.provide('Blockly.Mutator'); goog.require('Blockly.Bubble'); +goog.require('Blockly.Events.BlockChange'); +goog.require('Blockly.Events.Ui'); goog.require('Blockly.Icon'); goog.require('Blockly.WorkspaceSvg'); goog.require('goog.dom'); diff --git a/core/procedures.js b/core/procedures.js index 8d219639ff..757d189e46 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -32,6 +32,7 @@ goog.provide('Blockly.Procedures'); goog.require('Blockly.Blocks'); goog.require('Blockly.constants'); +goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.Field'); goog.require('Blockly.Names'); goog.require('Blockly.Workspace'); diff --git a/core/toolbox.js b/core/toolbox.js index 0c144b36f7..3c1c7407eb 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -26,6 +26,7 @@ goog.provide('Blockly.Toolbox'); +goog.require('Blockly.Events.Ui'); goog.require('Blockly.HorizontalFlyout'); goog.require('Blockly.Touch'); goog.require('Blockly.VerticalFlyout'); diff --git a/core/ui_events.js b/core/ui_events.js new file mode 100644 index 0000000000..bd09f7575d --- /dev/null +++ b/core/ui_events.js @@ -0,0 +1,91 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Events fired as a result of UI actions in Blockly's editor. + * @author fraser@google.com (Neil Fraser) + */ +'use strict'; + +goog.provide('Blockly.Events.Ui'); + +goog.require('Blockly.Events'); +goog.require('Blockly.Events.Abstract'); + +goog.require('goog.array'); +goog.require('goog.math.Coordinate'); + +/** + * Class for a UI event. + * UI events are events that don't need to be sent over the wire for multi-user + * editing to work (e.g. scrolling the workspace, zooming, opening toolbox + * categories). + * UI events do not undo or redo. + * @param {Blockly.Block} block The affected block. + * @param {string} element One of 'selected', 'comment', 'mutator', etc. + * @param {*} oldValue Previous value of element. + * @param {*} newValue New value of element. + * @extends {Blockly.Events.Abstract} + * @constructor + */ +Blockly.Events.Ui = function(block, element, oldValue, newValue) { + Blockly.Events.Ui.superClass_.constructor.call(this); + this.blockId = block ? block.id : null; + this.workspaceId = block? block.workspace.id : null; + this.element = element; + this.oldValue = oldValue; + this.newValue = newValue; + // UI events do not undo or redo. + this.recordUndo = false; +}; +goog.inherits(Blockly.Events.Ui, Blockly.Events.Abstract); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Ui.prototype.type = Blockly.Events.UI; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.Ui.prototype.toJson = function() { + var json = Blockly.Events.Ui.superClass_.toJson.call(this); + json['element'] = this.element; + if (this.newValue !== undefined) { + json['newValue'] = this.newValue; + } + if (this.blockId) { + json['blockId'] = this.blockId; + } + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.Ui.prototype.fromJson = function(json) { + Blockly.Events.Ui.superClass_.fromJson.call(this, json); + this.element = json['element']; + this.newValue = json['newValue']; + this.blockId = json['blockId']; +}; diff --git a/core/variable_events.js b/core/variable_events.js new file mode 100644 index 0000000000..8691dbb0f0 --- /dev/null +++ b/core/variable_events.js @@ -0,0 +1,247 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Classes for all types of variable events. + * @author fenichel@google.com (Rachel Fenichel) + */ +'use strict'; + +goog.provide('Blockly.Events.VarBase'); +goog.provide('Blockly.Events.VarCreate'); +goog.provide('Blockly.Events.VarDelete'); +goog.provide('Blockly.Events.VarRename'); + +goog.require('Blockly.Events'); +goog.require('Blockly.Events.Abstract'); + +goog.require('goog.array'); +goog.require('goog.math.Coordinate'); + + +/** + * Abstract class for a variable event. + * @param {Blockly.VariableModel} variable The variable this event corresponds + * to. + * @extends {Blockly.Events.Abstract} + * @constructor + */ +Blockly.Events.VarBase = function(variable) { + Blockly.Events.VarBase.superClass_.constructor.call(this); + + /** + * The variable id for the variable this event pertains to. + * @type {string} + */ + this.varId = variable.getId(); + this.workspaceId = variable.workspace.id; +}; +goog.inherits(Blockly.Events.VarBase, Blockly.Events.Abstract); + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.VarBase.prototype.toJson = function() { + var json = Blockly.Events.VarBase.superClass_.toJson.call(this); + json['varId'] = this.varId; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.VarBase.prototype.fromJson = function(json) { + Blockly.Events.VarBase.superClass_.toJson.call(this); + this.varId = json['varId']; +}; + +/** + * Class for a variable creation event. + * @param {Blockly.VariableModel} variable The created variable. + * Null for a blank event. + * @extends {Blockly.Events.VarBase} + * @constructor + */ +Blockly.Events.VarCreate = function(variable) { + if (!variable) { + return; // Blank event to be populated by fromJson. + } + Blockly.Events.VarCreate.superClass_.constructor.call(this, variable); + this.varType = variable.type; + this.varName = variable.name; +}; +goog.inherits(Blockly.Events.VarCreate, Blockly.Events.VarBase); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.VarCreate.prototype.type = Blockly.Events.VAR_CREATE; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.VarCreate.prototype.toJson = function() { + var json = Blockly.Events.VarCreate.superClass_.toJson.call(this); + json['varType'] = this.varType; + json['varName'] = this.varName; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.VarCreate.prototype.fromJson = function(json) { + Blockly.Events.VarCreate.superClass_.fromJson.call(this, json); + this.varType = json['varType']; + this.varName = json['varName']; +}; + +/** + * Run a variable creation event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ +Blockly.Events.VarCreate.prototype.run = function(forward) { + var workspace = this.getEventWorkspace_(); + if (forward) { + workspace.createVariable(this.varName, this.varType, this.varId); + } else { + workspace.deleteVariableById(this.varId); + } +}; + +/** + * Class for a variable deletion event. + * @param {Blockly.VariableModel} variable The deleted variable. + * Null for a blank event. + * @extends {Blockly.Events.VarBase} + * @constructor + */ +Blockly.Events.VarDelete = function(variable) { + if (!variable) { + return; // Blank event to be populated by fromJson. + } + Blockly.Events.VarDelete.superClass_.constructor.call(this, variable); + this.varType = variable.type; + this.varName = variable.name; +}; +goog.inherits(Blockly.Events.VarDelete, Blockly.Events.VarBase); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.VarDelete.prototype.type = Blockly.Events.VAR_DELETE; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.VarDelete.prototype.toJson = function() { + var json = Blockly.Events.VarDelete.superClass_.toJson.call(this); + json['varType'] = this.varType; + json['varName'] = this.varName; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.VarDelete.prototype.fromJson = function(json) { + Blockly.Events.VarDelete.superClass_.fromJson.call(this, json); + this.varType = json['varType']; + this.varName = json['varName']; +}; + +/** + * Run a variable deletion event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ +Blockly.Events.VarDelete.prototype.run = function(forward) { + var workspace = this.getEventWorkspace_(); + if (forward) { + workspace.deleteVariableById(this.varId); + } else { + workspace.createVariable(this.varName, this.varType, this.varId); + } +}; + +/** + * Class for a variable rename event. + * @param {Blockly.VariableModel} variable The renamed variable. + * Null for a blank event. + * @param {string} newName The new name the variable will be changed to. + * @extends {Blockly.Events.VarBase} + * @constructor + */ +Blockly.Events.VarRename = function(variable, newName) { + if (!variable) { + return; // Blank event to be populated by fromJson. + } + Blockly.Events.VarRename.superClass_.constructor.call(this, variable); + this.oldName = variable.name; + this.newName = newName; +}; +goog.inherits(Blockly.Events.VarRename, Blockly.Events.VarBase); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.VarRename.prototype.type = Blockly.Events.VAR_RENAME; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.VarRename.prototype.toJson = function() { + var json = Blockly.Events.VarRename.superClass_.toJson.call(this); + json['oldName'] = this.oldName; + json['newName'] = this.newName; + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.VarRename.prototype.fromJson = function(json) { + Blockly.Events.VarRename.superClass_.fromJson.call(this, json); + this.oldName = json['oldName']; + this.newName = json['newName']; +}; + +/** + * Run a variable rename event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ +Blockly.Events.VarRename.prototype.run = function(forward) { + var workspace = this.getEventWorkspace_(); + if (forward) { + workspace.renameVariableById(this.varId, this.newName); + } else { + workspace.renameVariableById(this.varId, this.oldName); + } +}; diff --git a/core/variable_map.js b/core/variable_map.js index dd4a20b5d2..26ab4c41c9 100644 --- a/core/variable_map.js +++ b/core/variable_map.js @@ -26,7 +26,8 @@ goog.provide('Blockly.VariableMap'); -goog.require('Blockly.VariableModel'); +goog.require('Blockly.Events.VarDelete'); +goog.require('Blockly.Events.VarRename'); /** * Class for a variable map. This contains a dictionary data structure with diff --git a/core/variable_model.js b/core/variable_model.js index 453dfa6874..39eb0c73f2 100644 --- a/core/variable_model.js +++ b/core/variable_model.js @@ -26,6 +26,8 @@ goog.provide('Blockly.VariableModel'); +goog.require('Blockly.Events.VarCreate'); + goog.require('goog.string'); diff --git a/core/warning.js b/core/warning.js index b3b13e15ab..8ea805bd31 100644 --- a/core/warning.js +++ b/core/warning.js @@ -27,6 +27,7 @@ goog.provide('Blockly.Warning'); goog.require('Blockly.Bubble'); +goog.require('Blockly.Events.Ui'); goog.require('Blockly.Icon'); diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 33d9fe08a0..64f4062603 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -33,7 +33,7 @@ goog.require('Blockly.ConnectionDB'); goog.require('Blockly.constants'); goog.require('Blockly.DataCategory'); goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.Events'); +goog.require('Blockly.Events.BlockCreate'); goog.require('Blockly.Gesture'); goog.require('Blockly.Grid'); goog.require('Blockly.Options'); diff --git a/core/xml.js b/core/xml.js index 6326f06ac2..96004265eb 100644 --- a/core/xml.js +++ b/core/xml.js @@ -30,6 +30,9 @@ **/ goog.provide('Blockly.Xml'); +goog.require('Blockly.Events.BlockCreate'); +goog.require('Blockly.Events.VarCreate'); + goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.userAgent'); From 2c234b36c5182d20cadc070c4405244b9eb2b87f Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Wed, 9 May 2018 16:55:14 -0400 Subject: [PATCH 0503/2135] Fix the check to compile local or remote in do_compile --- build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.py b/build.py index 28cd47c097..271f9b7759 100755 --- a/build.py +++ b/build.py @@ -322,7 +322,7 @@ def gen_generator(self, language): self.do_compile(params, target_filename, filenames, remove) def do_compile(self, params, target_filename, filenames, remove): - do_compile = self.do_compile_remote if self.closure_env == REMOTE_COMPILER else self.do_compile_local + do_compile = self.do_compile_remote if self.closure_env["closure_compiler"] == REMOTE_COMPILER else self.do_compile_local json_data = do_compile(params, target_filename) if self.report_errors(target_filename, filenames, json_data): From a7a5f41a7a14885f4c7e8f747a0f77a5bad6c1ef Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 9 May 2018 15:34:36 -0700 Subject: [PATCH 0504/2135] Split events.js into multiple files and update tests --- core/block_dragger.js | 6 +- core/block_events.js | 4 +- core/events.js | 132 ++----------------------------------- core/events_abstract.js | 4 +- core/scratch_events.js | 125 +++++++++++++++++++++++++++++++++++ tests/jsunit/event_test.js | 59 +++++++++++++++-- 6 files changed, 193 insertions(+), 137 deletions(-) create mode 100644 core/scratch_events.js diff --git a/core/block_dragger.js b/core/block_dragger.js index 5e50f40dae..d2689da872 100644 --- a/core/block_dragger.js +++ b/core/block_dragger.js @@ -28,6 +28,8 @@ goog.provide('Blockly.BlockDragger'); goog.require('Blockly.BlockAnimations'); goog.require('Blockly.Events.BlockMove'); +goog.require('Blockly.Events.DragBlockOutside'); +goog.require('Blockly.Events.EndBlockDrag'); goog.require('Blockly.InsertionMarkerManager'); goog.require('goog.math.Coordinate'); @@ -292,7 +294,7 @@ Blockly.BlockDragger.prototype.endBlockDrag = function(e, currentDragDeltaXY) { * @private */ Blockly.BlockDragger.prototype.fireDragOutsideEvent_ = function(isOutside) { - var event = new Blockly.Events.BlockDragOutside(this.draggingBlock_); + var event = new Blockly.Events.DragBlockOutside(this.draggingBlock_); event.isOutside = isOutside; Blockly.Events.fire(event); }; @@ -303,7 +305,7 @@ Blockly.BlockDragger.prototype.fireDragOutsideEvent_ = function(isOutside) { * @private */ Blockly.BlockDragger.prototype.fireEndDragEvent_ = function(isOutside) { - var event = new Blockly.Events.BlockEndDrag(this.draggingBlock_, isOutside); + var event = new Blockly.Events.EndBlockDrag(this.draggingBlock_, isOutside); Blockly.Events.fire(event); }; diff --git a/core/block_events.js b/core/block_events.js index 131fb5d0d0..a680d069fe 100644 --- a/core/block_events.js +++ b/core/block_events.js @@ -145,7 +145,7 @@ Blockly.Events.Change.prototype.fromJson = function(json) { /** * Does this event record any change of state? - * @return {boolean} True if something changed. + * @return {boolean} False if something changed. */ Blockly.Events.Change.prototype.isNull = function() { return this.oldValue == this.newValue; @@ -470,7 +470,7 @@ Blockly.Events.Move.prototype.currentLocation_ = function() { /** * Does this event record any change of state? - * @return {boolean} True if something changed. + * @return {boolean} False if something changed. */ Blockly.Events.Move.prototype.isNull = function() { return this.oldParentId == this.newParentId && diff --git a/core/events.js b/core/events.js index 0eb678ab8b..421bb14882 100644 --- a/core/events.js +++ b/core/events.js @@ -339,6 +339,12 @@ Blockly.Events.fromJson = function(json, workspace) { case Blockly.Events.UI: event = new Blockly.Events.Ui(null); break; + case Blockly.Events.DRAG_OUTSIDE: + event = new Blockly.Events.DragBlockOutside(null); + break; + case Blockly.Events.END_DRAG: + event = new Blockly.Events.EndBlockDrag(null, false); + break; default: throw 'Unknown event type.'; } @@ -347,132 +353,6 @@ Blockly.Events.fromJson = function(json, workspace) { return event; }; -/** - * Class for a block drag event. Fired when block dragged into or out of - * the blocks UI. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.DragOutside = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.DragOutside.superClass_.constructor.call(this, block); -}; -goog.inherits(Blockly.Events.DragOutside, Blockly.Events.Abstract); - -/** - * Class for a block drag event. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.BlockDragOutside = Blockly.Events.DragOutside; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.DragOutside.prototype.type = Blockly.Events.DRAG_OUTSIDE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.DragOutside.prototype.toJson = function() { - var json = Blockly.Events.DragOutside.superClass_.toJson.call(this); - if (this.isOutside) { - json['isOutside'] = this.isOutside; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.DragOutside.prototype.fromJson = function(json) { - Blockly.Events.DragOutside.superClass_.fromJson.call(this, json); - this.isOutside = json['isOutside']; -}; - -/** - * Does this event record any change of state? - * @return {boolean} True if something changed. - */ -Blockly.Events.DragOutside.prototype.isNull = function() { - return false; -}; - -/** - * Class for a block end drag event. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @param {boolean} isOutside True if the moved block is outside of the - * blocks workspace. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.EndDrag = function(block, isOutside) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.EndDrag.superClass_.constructor.call(this, block); - this.isOutside = isOutside; - // If drag ends outside the blocks workspace, send the block XML - if (isOutside) { - this.xml = Blockly.Xml.blockToDom(block, true /* opt_noId */); - } -}; -goog.inherits(Blockly.Events.EndDrag, Blockly.Events.Abstract); - -/** - * Class for a block end drag event. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.BlockEndDrag = Blockly.Events.EndDrag; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.EndDrag.prototype.type = Blockly.Events.END_DRAG; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.EndDrag.prototype.toJson = function() { - var json = Blockly.Events.EndDrag.superClass_.toJson.call(this); - if (this.isOutside) { - json['isOutside'] = this.isOutside; - } - if (this.xml) { - json['xml'] = this.xml; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.EndDrag.prototype.fromJson = function(json) { - Blockly.Events.EndDrag.superClass_.fromJson.call(this, json); - this.isOutside = json['isOutside']; - this.xml = json['xml']; -}; - -/** - * Does this event record any change of state? - * @return {boolean} True if something changed. - */ -Blockly.Events.EndDrag.prototype.isNull = function() { - return false; -}; - /** * Enable/disable a block depending on whether it is properly connected. * Use this on applications where all blocks should be connected to a top block. diff --git a/core/events_abstract.js b/core/events_abstract.js index 17eea76d1b..2af78c3fc5 100644 --- a/core/events_abstract.js +++ b/core/events_abstract.js @@ -81,7 +81,9 @@ Blockly.Events.Abstract.prototype.fromJson = function(json) { /** * Does this event record any change of state? - * @return {boolean} True if null, false if something changed. + * By default we assume events are non-null. Subclasses may override to + * indicate that they do not change state. + * @return {boolean} False if something changed. */ Blockly.Events.Abstract.prototype.isNull = function() { return false; diff --git a/core/scratch_events.js b/core/scratch_events.js new file mode 100644 index 0000000000..7c6bac0cfb --- /dev/null +++ b/core/scratch_events.js @@ -0,0 +1,125 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Events fired as a result of UI actions in a Scratch-Blocks + * editor that are not fired in Blockly. + * @author fenichel@google.com (Rachel Fenichel) + */ +'use strict'; + +goog.provide('Blockly.Events.DragBlockOutside'); +goog.provide('Blockly.Events.EndBlockDrag'); + +goog.require('Blockly.Events'); +goog.require('Blockly.Events.BlockBase'); + +goog.require('goog.array'); +goog.require('goog.math.Coordinate'); + +/** + * Class for a block drag event. Fired when block dragged into or out of + * the blocks UI. + * @param {Blockly.Block} block The moved block. Null for a blank event. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.DragBlockOutside = function(block) { + Blockly.Events.DragBlockOutside.superClass_.constructor.call(this, block); + this.recordUndo = false; +}; +goog.inherits(Blockly.Events.DragBlockOutside, Blockly.Events.BlockBase); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.DragBlockOutside.prototype.type = Blockly.Events.DRAG_OUTSIDE; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.DragBlockOutside.prototype.toJson = function() { + var json = Blockly.Events.DragBlockOutside.superClass_.toJson.call(this); + if (this.isOutside) { + json['isOutside'] = this.isOutside; + } + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.DragBlockOutside.prototype.fromJson = function(json) { + Blockly.Events.DragBlockOutside.superClass_.fromJson.call(this, json); + this.isOutside = json['isOutside']; +}; + +/** + * Class for a block end drag event. + * @param {Blockly.Block} block The moved block. Null for a blank event. + * @param {boolean} isOutside True if the moved block is outside of the + * blocks workspace. + * @extends {Blockly.Events.BlockBase} + * @constructor + */ +Blockly.Events.EndBlockDrag = function(block, isOutside) { + Blockly.Events.EndBlockDrag.superClass_.constructor.call(this, block); + this.isOutside = isOutside; + // If drag ends outside the blocks workspace, send the block XML + if (isOutside) { + this.xml = Blockly.Xml.blockToDom(block, true /* opt_noId */); + } + this.recordUndo = false; +}; +goog.inherits(Blockly.Events.EndBlockDrag, Blockly.Events.BlockBase); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.EndBlockDrag.prototype.type = Blockly.Events.END_DRAG; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.EndBlockDrag.prototype.toJson = function() { + var json = Blockly.Events.EndBlockDrag.superClass_.toJson.call(this); + if (this.isOutside) { + json['isOutside'] = this.isOutside; + } + if (this.xml) { + json['xml'] = this.xml; + } + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.EndBlockDrag.prototype.fromJson = function(json) { + Blockly.Events.EndBlockDrag.superClass_.fromJson.call(this, json); + this.isOutside = json['isOutside']; + this.xml = json['xml']; +}; diff --git a/tests/jsunit/event_test.js b/tests/jsunit/event_test.js index 159b65eb46..8708f85aca 100644 --- a/tests/jsunit/event_test.js +++ b/tests/jsunit/event_test.js @@ -76,14 +76,14 @@ function eventTest_tearDownWithMockBlocks() { delete Blockly.Blocks.field_variable_test_block; } -function test_abstract_constructor_block() { +function test_block_base_constructor() { eventTest_setUpWithMockBlocks(); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1'); try { var block = createSimpleTestBlock(workspace); // Here's the event we care about. - var event = new Blockly.Events.Abstract(block); + var event = new Blockly.Events.BlockBase(block); assertUndefined(event.varId); checkExactEventValues(event, {'blockId': '1', 'workspaceId': workspace.id, 'group': '', 'recordUndo': true}); @@ -92,13 +92,13 @@ function test_abstract_constructor_block() { } } -function test_abstract_constructor_variable() { +function test_var_base_constructor() { eventTest_setUpWithMockBlocks(); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1'); try { var variable = workspace.createVariable('name1', 'type1', 'id1'); - var event = new Blockly.Events.Abstract(variable); + var event = new Blockly.Events.VarBase(variable); assertUndefined(event.blockId); checkExactEventValues(event, {'varId': 'id1', 'workspaceId': workspace.id, 'group': '', 'recordUndo': true}); @@ -107,12 +107,13 @@ function test_abstract_constructor_variable() { } } -function test_abstract_constructor_null() { +function test_abstract_constructor() { eventTest_setUpWithMockBlocks(); try { - var event = new Blockly.Events.Abstract(null); + var event = new Blockly.Events.Abstract(); assertUndefined(event.blockId); assertUndefined(event.workspaceId); + assertUndefined(event.varId); checkExactEventValues(event, {'group': '', 'recordUndo': true}); } finally { eventTest_tearDownWithMockBlocks(); @@ -311,6 +312,52 @@ function test_blockMove_constructoroldParentId() { } } +function test_uiEvent_constructor_null() { + try { + Blockly.Events.setGroup('testGroup'); + var event = new Blockly.Events.Ui(null, 'foo', 'bar', 'baz'); + checkExactEventValues(event, + { + 'blockId': null, + 'workspaceId': null, + 'type': 'ui', + 'oldValue': 'bar', + 'newValue': 'baz', + 'element': 'foo', + 'recordUndo': false, + 'group': 'testGroup' + } + ); + } finally { + Blockly.Events.setGroup(false); + } +} + +function test_uiEvent_constructor_block() { + eventTest_setUpWithMockBlocks(); + setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']); + try { + var block1 = createSimpleTestBlock(workspace); + Blockly.Events.setGroup('testGroup'); + var event = new Blockly.Events.Ui(block1, 'foo', 'bar', 'baz'); + checkExactEventValues(event, + { + 'blockId': '1', + 'workspaceId': workspace.id, + 'type': 'ui', + 'oldValue': 'bar', + 'newValue': 'baz', + 'element': 'foo', + 'recordUndo': false, + 'group': 'testGroup' + } + ); + } finally { + Blockly.Events.setGroup(false); + eventTest_tearDownWithMockBlocks(); + } +} + function test_varCreate_constructor() { eventTest_setUp(); try { From 3c9d08b75c096fb76481fa494aa1b0f9094e253f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 10 May 2018 15:14:19 -0400 Subject: [PATCH 0505/2135] Add an id to each category --- blocks_vertical/default_toolbox.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/blocks_vertical/default_toolbox.js b/blocks_vertical/default_toolbox.js index cb7f4fce8b..4aeab003b7 100644 --- a/blocks_vertical/default_toolbox.js +++ b/blocks_vertical/default_toolbox.js @@ -29,7 +29,7 @@ goog.require('Blockly.Blocks'); */ Blockly.Blocks.defaultToolbox = '