From 6c2d63300d879fa298aea0a283435839b96e1952 Mon Sep 17 00:00:00 2001 From: Johnny Yang Date: Mon, 16 Apr 2018 01:33:04 +1000 Subject: [PATCH 001/101] mark properties @protected instead of @private so they can be used by Blockly.BlockSvg --- core/block.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/block.js b/core/block.js index 5c244cf8c..249e48125 100644 --- a/core/block.js +++ b/core/block.js @@ -87,13 +87,13 @@ Blockly.Block = function(workspace, prototypeName, opt_id) { /** * @type {Blockly.Block} - * @private + * @protected */ this.parentBlock_ = null; /** * @type {!Array.} - * @private + * @protected */ this.childBlocks_ = []; @@ -123,7 +123,7 @@ Blockly.Block = function(workspace, prototypeName, opt_id) { /** * @type {boolean} - * @private + * @protected */ this.collapsed_ = false; @@ -382,7 +382,7 @@ Blockly.Block.prototype.lastConnectionInStack_ = function() { /** * Bump unconnected blocks out of alignment. Two blocks which aren't actually * connected should not coincidentally line up on screen. - * @private + * @protected */ Blockly.Block.prototype.bumpNeighbours_ = function() { console.warn('Not expected to reach this bumpNeighbours_ function. The ' + @@ -1290,7 +1290,7 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { * @param {string} name Language-neutral identifier which may used to find this * input again. Should be unique to this block. * @return {!Blockly.Input} The input object created. - * @private + * @protected */ Blockly.Block.prototype.appendInput_ = function(type, name) { var connection = null; From 419fe9e8a5beb11888f0bb754ac760b1ebc6c671 Mon Sep 17 00:00:00 2001 From: Johnny Yang Date: Mon, 16 Apr 2018 01:34:52 +1000 Subject: [PATCH 002/101] mark properties @protected instead of @private so they can be used by Blockly.RenderedConnection --- core/connection.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/connection.js b/core/connection.js index 95c63afc3..3ff6a1b31 100644 --- a/core/connection.js +++ b/core/connection.js @@ -41,7 +41,7 @@ goog.require('goog.dom'); Blockly.Connection = function(source, type) { /** * @type {!Blockly.Block} - * @private + * @protected */ this.sourceBlock_ = source; /** @type {number} */ @@ -89,28 +89,28 @@ Blockly.Connection.prototype.shadowDom_ = null; /** * Horizontal location of this connection. * @type {number} - * @private + * @protected */ Blockly.Connection.prototype.x_ = 0; /** * Vertical location of this connection. * @type {number} - * @private + * @protected */ Blockly.Connection.prototype.y_ = 0; /** * Has this connection been added to the connection database? * @type {boolean} - * @private + * @protected */ Blockly.Connection.prototype.inDB_ = false; /** * Connection database for connections of this type on the current workspace. * @type {Blockly.ConnectionDB} - * @private + * @protected */ Blockly.Connection.prototype.db_ = null; @@ -118,14 +118,14 @@ Blockly.Connection.prototype.db_ = null; * Connection database for connections compatible with this type on the * current workspace. * @type {Blockly.ConnectionDB} - * @private + * @protected */ Blockly.Connection.prototype.dbOpposite_ = null; /** * Whether this connections is hidden (not tracked in a database) or not. * @type {boolean} - * @private + * @protected */ Blockly.Connection.prototype.hidden_ = null; @@ -133,7 +133,7 @@ Blockly.Connection.prototype.hidden_ = null; * Connect two connections together. This is the connection on the superior * block. * @param {!Blockly.Connection} childConnection Connection on inferior block. - * @private + * @protected */ Blockly.Connection.prototype.connect_ = function(childConnection) { var parentConnection = this; @@ -498,7 +498,7 @@ Blockly.Connection.prototype.disconnect = function() { * Disconnect two blocks that are connected by this connection. * @param {!Blockly.Block} parentBlock The superior block. * @param {!Blockly.Block} childBlock The inferior block. - * @private + * @protected */ Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock, childBlock) { @@ -518,7 +518,7 @@ Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock, /** * Respawn the shadow block if there was one connected to the this connection. - * @private + * @protected */ Blockly.Connection.prototype.respawnShadow_ = function() { var parentBlock = this.getSourceBlock(); @@ -552,7 +552,7 @@ Blockly.Connection.prototype.targetBlock = function() { * value type system. E.g. square_root("Hello") is not compatible. * @param {!Blockly.Connection} otherConnection Connection to compare against. * @return {boolean} True if the connections share a type. - * @private + * @protected */ Blockly.Connection.prototype.checkType_ = function(otherConnection) { if (!this.check_ || !otherConnection.check_) { From 7dbe0bda6638f24ccab261f076a3d412f3ea3e1c Mon Sep 17 00:00:00 2001 From: Johnny Yang Date: Mon, 16 Apr 2018 01:36:28 +1000 Subject: [PATCH 003/101] mark properties @protected instead of @private so they can be used by Blockly.FlyoutVertical & Blockly.FlyoutHorizontal --- core/flyout_base.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/flyout_base.js b/core/flyout_base.js index ea21ec8bf..f27c6f541 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; @@ -64,7 +64,7 @@ Blockly.Flyout = function(workspaceOptions) { /** * Position of the toolbox and flyout relative to the workspace. * @type {number} - * @private + * @protected */ this.toolboxPosition_ = workspaceOptions.toolboxPosition; @@ -86,7 +86,7 @@ Blockly.Flyout = function(workspaceOptions) { /** * List of visible buttons. * @type {!Array.} - * @private + * @protected */ this.buttons_ = []; @@ -165,14 +165,14 @@ Blockly.Flyout.prototype.SCROLLBAR_PADDING = 2; /** * Width of flyout. * @type {number} - * @private + * @protected */ Blockly.Flyout.prototype.width_ = 0; /** * Height of flyout. * @type {number} - * @private + * @protected */ Blockly.Flyout.prototype.height_ = 0; @@ -190,7 +190,7 @@ Blockly.Flyout.prototype.height_ = 0; * This is used to know when to create a new block and when to scroll the * flyout. Setting it to 360 means that all drags create a new block. * @type {number} - * @private + * @protected */ Blockly.Flyout.prototype.dragAngleRange_ = 70; @@ -366,7 +366,7 @@ Blockly.Flyout.prototype.updateDisplay_ = function() { * @param {number} height The computed height of the flyout's SVG group. * @param {number} x The computed x origin of the flyout's SVG group. * @param {number} y The computed y origin of the flyout's SVG group. - * @private + * @protected */ Blockly.Flyout.prototype.positionAt_ = function(width, height, x, y) { this.svgGroup_.setAttribute("width", width); @@ -537,7 +537,7 @@ Blockly.Flyout.prototype.clearOldBlocks_ = function() { * @param {!Blockly.Block} block The block to add listeners for. * @param {!Element} rect The invisible rectangle under the block that acts as * a mat for that block. - * @private + * @protected */ Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { this.listeners_.push(Blockly.bindEventWithChecks_(root, 'mousedown', null, @@ -629,7 +629,7 @@ Blockly.Flyout.prototype.createBlock = function(originalBlock) { * @param {!Blockly.FlyoutButton} button The button to initialize and place. * @param {number} x The x position of the cursor during this layout pass. * @param {number} y The y position of the cursor during this layout pass. - * @private + * @protected */ Blockly.Flyout.prototype.initFlyoutButton_ = function(button, x, y) { var buttonSvg = button.createDom(); @@ -655,7 +655,7 @@ Blockly.Flyout.prototype.initFlyoutButton_ = function(button, x, y) { * placed. * @return {!SVGElement} Newly created SVG element for the rectangle behind the * block. - * @private + * @protected */ Blockly.Flyout.prototype.createRect_ = function(block, x, y, blockHW, index) { // Create an invisible rectangle under the block to act as a button. Just @@ -683,7 +683,7 @@ Blockly.Flyout.prototype.createRect_ = function(block, x, y, blockHW, index) { * hats, and any other protrusions we invent. * @param {!SVGElement} rect The rectangle to move directly behind the block. * @param {!Blockly.BlockSvg} block The block the rectangle should be behind. - * @private + * @protected */ Blockly.Flyout.prototype.moveRectToBlock_ = function(rect, block) { var blockHW = block.getHeightWidth(); From 4e3b1148324ec1073e43d008302057ac8441cd71 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 16 Apr 2018 15:28:52 -0700 Subject: [PATCH 004/101] Move block animation code to a new file and rebuild --- blockly_compressed.js | 25 ++-- blockly_uncompressed.js | 10 +- blocks_compressed.js | 6 +- core/block_animations.js | 213 +++++++++++++++++++++++++++++ core/block_dragger.js | 7 +- core/block_svg.js | 176 +----------------------- core/dragged_connection_manager.js | 4 +- core/gesture.js | 3 +- 8 files changed, 245 insertions(+), 199 deletions(-) create mode 100644 core/block_animations.js diff --git a/blockly_compressed.js b/blockly_compressed.js index d89c342da..f544695e9 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -1025,7 +1025,13 @@ Blockly.ConnectionDB.prototype.getNeighbours=function(a,b){function c(a){var c=e Blockly.ConnectionDB.prototype.searchForClosest=function(a,b,c){if(!this.length)return{connection:null,radius:b};var d=a.y_,e=a.x_;a.x_=e+c.x;a.y_=d+c.y;var f=this.findPositionForConnection_(a);c=null;for(var g=b,h,k=f-1;0<=k&&this.isInYRange_(k,a.y_,b);)h=this[k],a.isConnectionAllowed(h,g)&&(c=h,g=h.distanceFrom(a)),k--;for(;fc)){var d=b.getSvgXY(a.getSvgRoot());a.outputConnection?(d.x+=(a.RTL?3:-3)*c,d.y+=13*c):a.previousConnection&&(d.x+=(a.RTL?-23:23)*c,d.y+=3*c);a=Blockly.utils.createSvgElement("circle",{cx:d.x,cy:d.y,r:0,fill:"none",stroke:"#888","stroke-width":10},b.getParentSvg());Blockly.BlockAnimations.connectionUiStep_(a,new Date,c)}}; +Blockly.BlockAnimations.connectionUiStep_=function(a,b,c){var d=(new Date-b)/150;1a.workspace.scale)){var b=a.getHeightWidth().height;b=Math.atan(10/b)/Math.PI*180;a.RTL||(b*=-1);Blockly.BlockAnimations.disconnectUiStep_(a.getSvgRoot(),b,new Date)}}; +Blockly.BlockAnimations.disconnectUiStep_=function(a,b,c){var d=(new Date-c)/200;1this.workspace.scale)){var a=this.workspace.getSvgXY(this.svgGroup_);this.outputConnection?(a.x+=(this.RTL?3:-3)*this.workspace.scale,a.y+=13*this.workspace.scale):this.previousConnection&&(a.x+=(this.RTL?-23:23)*this.workspace.scale,a.y+=3*this.workspace.scale);a=Blockly.utils.createSvgElement("circle",{cx:a.x,cy:a.y,r:0,fill:"none",stroke:"#888","stroke-width":10},this.workspace.getParentSvg()); -Blockly.BlockSvg.connectionUiStep_(a,new Date,this.workspace.scale)}};Blockly.BlockSvg.connectionUiStep_=function(a,b,c){var d=(new Date-b)/150;1this.workspace.scale)){var a=this.getHeightWidth().height;a=Math.atan(10/a)/Math.PI*180;this.RTL||(a*=-1);Blockly.BlockSvg.disconnectUiStep_(this.svgGroup_,a,new Date)}}; -Blockly.BlockSvg.disconnectUiStep_=function(a,b,c){var d=(new Date-c)/200;1\u200f",GTE:"\u200f\u2265\u200f"},b=this.getField("OP");if(b){b=b.getOptions();for(var c=0;ce;e++){var f=1==e?b:c;f&&!f.outputConnection.checkType_(d)&&(Blockly.Events.setGroup(a.group),d===this.prevParentConnection_?(this.unplug(),d.getSourceBlock().bumpNeighbours_()):(f.unplug(),f.bumpNeighbours_()),Blockly.Events.setGroup(!1))}this.prevParentConnection_= d}};Blockly.Extensions.registerMixin("logic_ternary",Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN);Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120; Blockly.defineBlocksWithJsonArray([{type:"controls_repeat_ext",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"input_value",name:"TIMES",check:"Number"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,colour:"%{BKY_LOOPS_HUE}",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_repeat",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"field_number",name:"TIMES", diff --git a/core/block_animations.js b/core/block_animations.js new file mode 100644 index 000000000..63031d515 --- /dev/null +++ b/core/block_animations.js @@ -0,0 +1,213 @@ +/** + * @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 Methods animating a block on connection and disconnection. + * @author fenichel@google.com (Rachel Fenichel) + */ +'use strict'; + +goog.provide('Blockly.BlockAnimations'); + + +/** + * PID of disconnect UI animation. There can only be one at a time. + * @type {number} + * @private + */ +Blockly.BlockAnimations.disconnectPid_ = 0; + +/** + * SVG group of wobbling block. There can only be one at a time. + * @type {Element} + * @private + */ +Blockly.BlockAnimations.disconnectGroup_ = null; + +/** + * Play some UI effects (sound, animation) when disposing of a block. + * @param {!Blockly.BlockSvg} block The block being disposed of. + * @package + */ +Blockly.BlockAnimations.disposeUiEffect = function(block) { + var workspace = block.workspace; + var svgGroup = block.getSvgRoot(); + workspace.getAudioManager().play('delete'); + + var xy = workspace.getSvgXY(svgGroup); + // Deeply clone the current block. + var clone = svgGroup.cloneNode(true); + clone.translateX_ = xy.x; + clone.translateY_ = xy.y; + clone.setAttribute('transform', 'translate(' + xy.x + ',' + xy.y + ')'); + workspace.getParentSvg().appendChild(clone); + clone.bBox_ = clone.getBBox(); + // Start the animation. + Blockly.BlockAnimations.disposeUiStep_(clone, workspace.RTL, new Date, + workspace.scale); +}; + +/** + * Animate a cloned block and eventually dispose of it. + * This is a class method, not an instance method since the original block has + * been destroyed and is no longer accessible. + * @param {!Element} clone SVG element to animate and dispose of. + * @param {boolean} rtl True if RTL, false if LTR. + * @param {!Date} start Date of animation's start. + * @param {number} workspaceScale Scale of workspace. + * @private + */ +Blockly.BlockAnimations.disposeUiStep_ = function(clone, rtl, start, + workspaceScale) { + var ms = new Date - start; + var percent = ms / 150; + if (percent > 1) { + goog.dom.removeNode(clone); + } else { + var x = clone.translateX_ + + (rtl ? -1 : 1) * clone.bBox_.width * workspaceScale / 2 * percent; + var y = clone.translateY_ + clone.bBox_.height * workspaceScale * percent; + var scale = (1 - percent) * workspaceScale; + clone.setAttribute('transform', 'translate(' + x + ',' + y + ')' + + ' scale(' + scale + ')'); + setTimeout(Blockly.BlockAnimations.disposeUiStep_, 10, clone, rtl, start, + workspaceScale); + } +}; + +/** + * Play some UI effects (sound, ripple) after a connection has been established. + * @param {!Blockly.BlockSvg} block The block being connected. + * @package + */ +Blockly.BlockAnimations.connectionUiEffect = function(block) { + var workspace = block.workspace; + var scale = workspace.scale; + workspace.getAudioManager().play('click'); + if (scale < 1) { + return; // Too small to care about visual effects. + } + // Determine the absolute coordinates of the inferior block. + var xy = workspace.getSvgXY(block.getSvgRoot()); + // Offset the coordinates based on the two connection types, fix scale. + if (block.outputConnection) { + xy.x += (block.RTL ? 3 : -3) * scale; + xy.y += 13 * scale; + } else if (block.previousConnection) { + xy.x += (block.RTL ? -23 : 23) * scale; + xy.y += 3 * scale; + } + var ripple = Blockly.utils.createSvgElement('circle', + { + 'cx': xy.x, + 'cy': xy.y, + 'r': 0, + 'fill': 'none', + 'stroke': '#888', + 'stroke-width': 10 + }, + workspace.getParentSvg()); + // Start the animation. + Blockly.BlockAnimations.connectionUiStep_(ripple, new Date, scale); +}; + +/** + * Expand a ripple around a connection. + * @param {!Element} ripple Element to animate. + * @param {!Date} start Date of animation's start. + * @param {number} scale Scale of workspace. + * @private + */ +Blockly.BlockAnimations.connectionUiStep_ = function(ripple, start, scale) { + var ms = new Date - start; + var percent = ms / 150; + if (percent > 1) { + goog.dom.removeNode(ripple); + } else { + ripple.setAttribute('r', percent * 25 * scale); + ripple.style.opacity = 1 - percent; + Blockly.BlockAnimations.disconnectPid_ = setTimeout( + Blockly.BlockAnimations.connectionUiStep_, 10, ripple, start, scale); + } +}; + +/** + * Play some UI effects (sound, animation) when disconnecting a block. + * @param {!Blockly.BlockSvg} block The block being disconnected. + * @package + */ +Blockly.BlockAnimations.disconnectUiEffect = function(block) { + block.workspace.getAudioManager().play('disconnect'); + if (block.workspace.scale < 1) { + return; // Too small to care about visual effects. + } + // Horizontal distance for bottom of block to wiggle. + var DISPLACEMENT = 10; + // Scale magnitude of skew to height of block. + var height = block.getHeightWidth().height; + var magnitude = Math.atan(DISPLACEMENT / height) / Math.PI * 180; + if (!block.RTL) { + magnitude *= -1; + } + // Start the animation. + Blockly.BlockAnimations.disconnectUiStep_( + block.getSvgRoot(), magnitude, new Date); +}; +/** + * Animate a brief wiggle of a disconnected block. + * @param {!Element} group SVG element to animate. + * @param {number} magnitude Maximum degrees skew (reversed for RTL). + * @param {!Date} start Date of animation's start. + * @private + */ +Blockly.BlockAnimations.disconnectUiStep_ = function(group, magnitude, start) { + var DURATION = 200; // Milliseconds. + var WIGGLES = 3; // Half oscillations. + + var ms = new Date - start; + var percent = ms / DURATION; + + if (percent > 1) { + group.skew_ = ''; + } else { + var skew = Math.round( + Math.sin(percent * Math.PI * WIGGLES) * (1 - percent) * magnitude); + group.skew_ = 'skewX(' + skew + ')'; + Blockly.BlockAnimations.disconnectGroup_ = group; + Blockly.BlockAnimations.disconnectPid_ = + setTimeout(Blockly.BlockAnimations.disconnectUiStep_, 10, group, + magnitude, start); + } + group.setAttribute('transform', group.translate_ + group.skew_); +}; + +/** + * Stop the disconnect UI animation immediately. + * @package + */ +Blockly.BlockAnimations.disconnectUiStop = function() { + if (Blockly.BlockAnimations.disconnectGroup_) { + clearTimeout(Blockly.BlockAnimations.disconnectPid_); + var group = Blockly.BlockAnimations.disconnectGroup_; + group.skew_ = ''; + group.setAttribute('transform', group.translate_); + Blockly.BlockAnimations.disconnectGroup_ = null; + } +}; diff --git a/core/block_dragger.js b/core/block_dragger.js index dd1feba98..f785106e1 100644 --- a/core/block_dragger.js +++ b/core/block_dragger.js @@ -26,6 +26,7 @@ goog.provide('Blockly.BlockDragger'); +goog.require('Blockly.BlockAnimations'); goog.require('Blockly.DraggedConnectionManager'); goog.require('Blockly.Events.BlockMove'); @@ -153,7 +154,7 @@ Blockly.BlockDragger.prototype.startBlockDrag = function(currentDragDeltaXY, hea } this.workspace_.setResizesEnabled(false); - Blockly.BlockSvg.disconnectUiStop_(); + Blockly.BlockAnimations.disconnectUiStop(); if (this.draggingBlock_.getParent() || (healStack && this.draggingBlock_.nextConnection && @@ -163,7 +164,7 @@ Blockly.BlockDragger.prototype.startBlockDrag = function(currentDragDeltaXY, hea var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); this.draggingBlock_.translate(newLoc.x, newLoc.y); - this.draggingBlock_.disconnectUiEffect(); + Blockly.BlockAnimations.disconnectUiEffect(this.draggingBlock_); } this.draggingBlock_.setDragging(true); // For future consideration: we may be able to put moveToDragSurface inside @@ -212,7 +213,7 @@ Blockly.BlockDragger.prototype.endBlockDrag = function(e, currentDragDeltaXY) { this.dragBlock(e, currentDragDeltaXY); this.dragIconData_ = []; - Blockly.BlockSvg.disconnectUiStop_(); + Blockly.BlockAnimations.disconnectUiStop(); var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); diff --git a/core/block_svg.js b/core/block_svg.js index 2e5727367..c6b2a93e0 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -27,6 +27,7 @@ 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'); @@ -807,7 +808,7 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { if (animate && this.rendered) { this.unplug(healStack); - this.disposeUiEffect(); + Blockly.BlockAnimations.disposeUiEffect(this); } // Stop rerendering. this.rendered = false; @@ -841,179 +842,6 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { Blockly.Field.stopCache(); }; -/** - * Play some UI effects (sound, animation) when disposing of a block. - */ -Blockly.BlockSvg.prototype.disposeUiEffect = function() { - this.workspace.getAudioManager().play('delete'); - - var xy = this.workspace.getSvgXY(/** @type {!Element} */ (this.svgGroup_)); - // Deeply clone the current block. - var clone = this.svgGroup_.cloneNode(true); - clone.translateX_ = xy.x; - clone.translateY_ = xy.y; - clone.setAttribute('transform', - 'translate(' + clone.translateX_ + ',' + clone.translateY_ + ')'); - this.workspace.getParentSvg().appendChild(clone); - clone.bBox_ = clone.getBBox(); - // Start the animation. - Blockly.BlockSvg.disposeUiStep_(clone, this.RTL, new Date, - this.workspace.scale); -}; - -/** - * Animate a cloned block and eventually dispose of it. - * This is a class method, not an instance method since the original block has - * been destroyed and is no longer accessible. - * @param {!Element} clone SVG element to animate and dispose of. - * @param {boolean} rtl True if RTL, false if LTR. - * @param {!Date} start Date of animation's start. - * @param {number} workspaceScale Scale of workspace. - * @private - */ -Blockly.BlockSvg.disposeUiStep_ = function(clone, rtl, start, workspaceScale) { - var ms = new Date - start; - var percent = ms / 150; - if (percent > 1) { - goog.dom.removeNode(clone); - } else { - var x = clone.translateX_ + - (rtl ? -1 : 1) * clone.bBox_.width * workspaceScale / 2 * percent; - var y = clone.translateY_ + clone.bBox_.height * workspaceScale * percent; - var scale = (1 - percent) * workspaceScale; - clone.setAttribute('transform', 'translate(' + x + ',' + y + ')' + - ' scale(' + scale + ')'); - setTimeout( - Blockly.BlockSvg.disposeUiStep_, 10, clone, rtl, start, workspaceScale); - } -}; - -/** - * Play some UI effects (sound, ripple) after a connection has been established. - */ -Blockly.BlockSvg.prototype.connectionUiEffect = function() { - this.workspace.getAudioManager().play('click'); - if (this.workspace.scale < 1) { - return; // Too small to care about visual effects. - } - // Determine the absolute coordinates of the inferior block. - var xy = this.workspace.getSvgXY(/** @type {!Element} */ (this.svgGroup_)); - // Offset the coordinates based on the two connection types, fix scale. - if (this.outputConnection) { - xy.x += (this.RTL ? 3 : -3) * this.workspace.scale; - xy.y += 13 * this.workspace.scale; - } else if (this.previousConnection) { - xy.x += (this.RTL ? -23 : 23) * this.workspace.scale; - xy.y += 3 * this.workspace.scale; - } - var ripple = Blockly.utils.createSvgElement('circle', - { - 'cx': xy.x, - 'cy': xy.y, - 'r': 0, - 'fill': 'none', - 'stroke': '#888', - 'stroke-width': 10 - }, - this.workspace.getParentSvg()); - // Start the animation. - Blockly.BlockSvg.connectionUiStep_(ripple, new Date, this.workspace.scale); -}; - -/** - * Expand a ripple around a connection. - * @param {!Element} ripple Element to animate. - * @param {!Date} start Date of animation's start. - * @param {number} workspaceScale Scale of workspace. - * @private - */ -Blockly.BlockSvg.connectionUiStep_ = function(ripple, start, workspaceScale) { - var ms = new Date - start; - var percent = ms / 150; - if (percent > 1) { - goog.dom.removeNode(ripple); - } else { - ripple.setAttribute('r', percent * 25 * workspaceScale); - ripple.style.opacity = 1 - percent; - Blockly.BlockSvg.disconnectUiStop_.pid_ = setTimeout( - Blockly.BlockSvg.connectionUiStep_, 10, ripple, start, workspaceScale); - } -}; - -/** - * Play some UI effects (sound, animation) when disconnecting a block. - */ -Blockly.BlockSvg.prototype.disconnectUiEffect = function() { - this.workspace.getAudioManager().play('disconnect'); - if (this.workspace.scale < 1) { - return; // Too small to care about visual effects. - } - // Horizontal distance for bottom of block to wiggle. - var DISPLACEMENT = 10; - // Scale magnitude of skew to height of block. - var height = this.getHeightWidth().height; - var magnitude = Math.atan(DISPLACEMENT / height) / Math.PI * 180; - if (!this.RTL) { - magnitude *= -1; - } - // Start the animation. - Blockly.BlockSvg.disconnectUiStep_(this.svgGroup_, magnitude, new Date); -}; - -/** - * Animate a brief wiggle of a disconnected block. - * @param {!Element} group SVG element to animate. - * @param {number} magnitude Maximum degrees skew (reversed for RTL). - * @param {!Date} start Date of animation's start. - * @private - */ -Blockly.BlockSvg.disconnectUiStep_ = function(group, magnitude, start) { - var DURATION = 200; // Milliseconds. - var WIGGLES = 3; // Half oscillations. - - var ms = new Date - start; - var percent = ms / DURATION; - - if (percent > 1) { - group.skew_ = ''; - } else { - var skew = Math.round( - Math.sin(percent * Math.PI * WIGGLES) * (1 - percent) * magnitude); - group.skew_ = 'skewX(' + skew + ')'; - Blockly.BlockSvg.disconnectUiStop_.group = group; - Blockly.BlockSvg.disconnectUiStop_.pid = - setTimeout( - Blockly.BlockSvg.disconnectUiStep_, 10, group, magnitude, start); - } - group.setAttribute('transform', group.translate_ + group.skew_); -}; - -/** - * Stop the disconnect UI animation immediately. - * @private - */ -Blockly.BlockSvg.disconnectUiStop_ = function() { - if (Blockly.BlockSvg.disconnectUiStop_.group) { - clearTimeout(Blockly.BlockSvg.disconnectUiStop_.pid); - var group = Blockly.BlockSvg.disconnectUiStop_.group; - group.skew_ = ''; - group.setAttribute('transform', group.translate_); - Blockly.BlockSvg.disconnectUiStop_.group = null; - } -}; - -/** - * PID of disconnect UI animation. There can only be one at a time. - * @type {number} - */ -Blockly.BlockSvg.disconnectUiStop_.pid = 0; - -/** - * SVG group of wobbling block. There can only be one at a time. - * @type {Element} - */ -Blockly.BlockSvg.disconnectUiStop_.group = null; - /** * Change the colour of a block. */ diff --git a/core/dragged_connection_manager.js b/core/dragged_connection_manager.js index c51d31eab..5763a9a9d 100644 --- a/core/dragged_connection_manager.js +++ b/core/dragged_connection_manager.js @@ -26,6 +26,7 @@ goog.provide('Blockly.DraggedConnectionManager'); +goog.require('Blockly.BlockAnimations'); goog.require('Blockly.RenderedConnection'); goog.require('goog.math.Coordinate'); @@ -138,7 +139,8 @@ Blockly.DraggedConnectionManager.prototype.applyConnections = function() { // Determine which connection is inferior (lower in the source stack). var inferiorConnection = this.localConnection_.isSuperior() ? this.closestConnection_ : this.localConnection_; - inferiorConnection.getSourceBlock().connectionUiEffect(); + Blockly.BlockAnimations.connectionUiEffect( + inferiorConnection.getSourceBlock()); // Bring the just-edited stack to the front. var rootBlock = this.topBlock_.getRootBlock(); rootBlock.bringToFront(); diff --git a/core/gesture.js b/core/gesture.js index 070e94ac7..deac26c06 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -27,6 +27,7 @@ goog.provide('Blockly.Gesture'); +goog.require('Blockly.BlockAnimations'); goog.require('Blockly.BlockDragger'); goog.require('Blockly.BubbleDragger'); goog.require('Blockly.constants'); @@ -482,7 +483,7 @@ Blockly.Gesture.prototype.doStart = function(e) { } this.hasStarted_ = true; - Blockly.BlockSvg.disconnectUiStop_(); + Blockly.BlockAnimations.disconnectUiStop(); this.startWorkspace_.updateScreenCalculationsIfScrolled(); if (this.startWorkspace_.isMutator) { // Mutator's coordinate system could be out of date because the bubble was From 100af3836d225643f2b6cfaff20f77a338f86cd5 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 16 Apr 2018 16:42:18 -0700 Subject: [PATCH 005/101] Fix #1788 --- core/variables.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/variables.js b/core/variables.js index af8415dbf..dff68c30e 100644 --- a/core/variables.js +++ b/core/variables.js @@ -459,7 +459,7 @@ Blockly.Variables.generateVariableFieldXmlString = function(variableModel) { * @param {!Blockly.VariableModel} variableModel The variable model to * represent. * @return {Element} The generated DOM. - * @package + * @public */ Blockly.Variables.generateVariableFieldDom = function(variableModel) { var xmlFieldString = From 662d79443f4e9a59334e19e2802399e6f427812c Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 17 Apr 2018 11:41:03 -0700 Subject: [PATCH 006/101] Add 'ordered' option to descendant getting functions. (#1786) --- core/block.js | 35 ++++++++++++++++++++++++++++------- core/block_dragger.js | 2 +- core/block_svg.js | 2 +- core/contextmenu.js | 7 ++++--- core/events/events.js | 4 ++-- core/flyout_base.js | 2 +- core/flyout_horizontal.js | 2 +- core/flyout_vertical.js | 2 +- core/generator.js | 2 +- core/mutator.js | 2 +- core/rendered_connection.js | 2 +- core/toolbox.js | 2 +- core/workspace.js | 22 +++++++++++++++++----- core/workspace_svg.js | 4 ++-- core/xml.js | 4 ++-- 15 files changed, 64 insertions(+), 30 deletions(-) diff --git a/core/block.js b/core/block.js index 249e48125..55536627e 100644 --- a/core/block.js +++ b/core/block.js @@ -457,12 +457,30 @@ Blockly.Block.prototype.getRootBlock = function() { /** * Find all the blocks that are directly nested inside this one. - * Includes value and block inputs, as well as any following statement. + * Includes value and statement inputs, as well as any following statement. * Excludes any connection on an output tab or any preceding statement. + * Blocks are optionally sorted by position; top to bottom. + * @param {boolean} ordered Sort the list if true. * @return {!Array.} Array of blocks. */ -Blockly.Block.prototype.getChildren = function() { - return this.childBlocks_; +Blockly.Block.prototype.getChildren = function(ordered) { + if (!ordered) { + return this.childBlocks_; + } + var blocks = []; + for (var i = 0, input; input = this.inputList[i]; i++) { + if (input.connection) { + var child = input.connection.targetBlock(); + if (child) { + blocks.push(child); + } + } + } + var next = this.getNextBlock(); + if (next) { + blocks.push(next); + } + return blocks; }; /** @@ -504,14 +522,17 @@ Blockly.Block.prototype.setParent = function(newParent) { /** * Find all the blocks that are directly or indirectly nested inside this one. * Includes this block in the list. - * Includes value and block inputs, as well as any following statements. + * Includes value and statement inputs, as well as any following statements. * Excludes any connection on an output tab or any preceding statements. + * Blocks are optionally sorted by position; top to bottom. + * @param {boolean} ordered Sort the list if true. * @return {!Array.} Flattened array of blocks. */ -Blockly.Block.prototype.getDescendants = function() { +Blockly.Block.prototype.getDescendants = function(ordered) { var blocks = [this]; - for (var child, x = 0; child = this.childBlocks_[x]; x++) { - blocks.push.apply(blocks, child.getDescendants()); + var childBlocks = this.getChildren(ordered); + for (var child, i = 0; child = childBlocks[i]; i++) { + blocks.push.apply(blocks, child.getDescendants(ordered)); } return blocks; }; diff --git a/core/block_dragger.js b/core/block_dragger.js index dd1feba98..7a423403d 100644 --- a/core/block_dragger.js +++ b/core/block_dragger.js @@ -124,7 +124,7 @@ Blockly.BlockDragger.prototype.dispose = function() { Blockly.BlockDragger.initIconData_ = function(block) { // Build a list of icons that need to be moved and where they started. var dragIconData = []; - var descendants = block.getDescendants(); + var descendants = block.getDescendants(false); for (var i = 0, descendant; descendant = descendants[i]; i++) { var icons = descendant.getIcons(); for (var j = 0; j < icons.length; j++) { diff --git a/core/block_svg.js b/core/block_svg.js index 2e5727367..a74cd9889 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1070,7 +1070,7 @@ Blockly.BlockSvg.prototype.updateDisabled = function() { this.updateColour(); } } - var children = this.getChildren(); + var children = this.getChildren(false); for (var i = 0, child; child = children[i]; i++) { child.updateDisabled(); } diff --git a/core/contextmenu.js b/core/contextmenu.js index 904dfe9c7..bcece8129 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -215,11 +215,11 @@ Blockly.ContextMenu.callbackFactory = function(block, xml) { Blockly.ContextMenu.blockDeleteOption = function(block) { // Option to delete this block but not blocks lower in the stack. // Count the number of blocks that are nested in this block. - var descendantCount = block.getDescendants(true).length; + var descendantCount = block.getDescendants(false).length; var nextBlock = block.getNextBlock(); if (nextBlock) { // Blocks in the current stack would survive this block's deletion. - descendantCount -= nextBlock.getDescendants(true).length; + descendantCount -= nextBlock.getDescendants(false).length; } var deleteOption = { text: descendantCount == 1 ? Blockly.Msg.DELETE_BLOCK : @@ -260,7 +260,8 @@ Blockly.ContextMenu.blockHelpOption = function(block) { */ Blockly.ContextMenu.blockDuplicateOption = function(block) { var enabled = true; - if (block.getDescendants().length > block.workspace.remainingCapacity()) { + if (block.getDescendants(false).length > + block.workspace.remainingCapacity()) { enabled = false; } var duplicateOption = { diff --git a/core/events/events.js b/core/events/events.js index b68dd778d..446e49153 100644 --- a/core/events/events.js +++ b/core/events/events.js @@ -287,7 +287,7 @@ Blockly.Events.setGroup = function(state) { */ Blockly.Events.getDescendantIds_ = function(block) { var ids = []; - var descendants = block.getDescendants(); + var descendants = block.getDescendants(false); for (var i = 0, descendant; descendant = descendants[i]; i++) { ids[i] = descendant.id; } @@ -350,7 +350,7 @@ Blockly.Events.disableOrphans = function(event) { var block = workspace.getBlockById(event.blockId); if (block) { if (block.getParent() && !block.getParent().disabled) { - var children = block.getDescendants(); + var children = block.getDescendants(false); for (var i = 0, child; child = children[i]; i++) { child.setDisabled(false); } diff --git a/core/flyout_base.js b/core/flyout_base.js index f27c6f541..8a3aeaaf6 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -717,7 +717,7 @@ Blockly.Flyout.prototype.filterForCapacity_ = function() { var blocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; block = blocks[i]; i++) { if (this.permanentlyDisabled_.indexOf(block) == -1) { - var allBlocks = block.getDescendants(); + var allBlocks = block.getDescendants(false); block.setDisabled(allBlocks.length > remainingCapacity); } } diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js index 9243c0ef7..8dd8032f6 100644 --- a/core/flyout_horizontal.js +++ b/core/flyout_horizontal.js @@ -261,7 +261,7 @@ Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) { for (var i = 0, item; item = contents[i]; i++) { if (item.type == 'block') { var block = item.block; - var allBlocks = block.getDescendants(); + var allBlocks = block.getDescendants(false); for (var j = 0, child; child = allBlocks[j]; j++) { // Mark blocks as being inside a flyout. This is used to detect and // prevent the closure of the flyout if the user right-clicks on such a diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index c95de2607..78057a643 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -247,7 +247,7 @@ Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) { for (var i = 0, item; item = contents[i]; i++) { if (item.type == 'block') { var block = item.block; - var allBlocks = block.getDescendants(); + var allBlocks = block.getDescendants(false); for (var j = 0, child; child = allBlocks[j]; j++) { // Mark blocks as being inside a flyout. This is used to detect and // prevent the closure of the flyout if the user right-clicks on such a diff --git a/core/generator.js b/core/generator.js index 6ec0c7a32..3c8e397a9 100644 --- a/core/generator.js +++ b/core/generator.js @@ -142,7 +142,7 @@ Blockly.Generator.prototype.prefixLines = function(text, prefix) { */ Blockly.Generator.prototype.allNestedComments = function(block) { var comments = []; - var blocks = block.getDescendants(); + var blocks = block.getDescendants(true); for (var i = 0; i < blocks.length; i++) { var comment = blocks[i].getCommentText(); if (comment) { diff --git a/core/mutator.js b/core/mutator.js index 575da3137..1ffb1fd0b 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -258,7 +258,7 @@ Blockly.Mutator.prototype.setVisible = function(visible) { } this.rootBlock_ = this.block_.decompose(this.workspace_); - var blocks = this.rootBlock_.getDescendants(); + var blocks = this.rootBlock_.getDescendants(false); for (var i = 0, child; child = blocks[i]; i++) { child.render(); } diff --git a/core/rendered_connection.js b/core/rendered_connection.js index cfd0473d1..c68dc490d 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -286,7 +286,7 @@ Blockly.RenderedConnection.prototype.setHidden = function(hidden) { Blockly.RenderedConnection.prototype.hideAll = function() { this.setHidden(true); if (this.targetConnection) { - var blocks = this.targetBlock().getDescendants(); + var blocks = this.targetBlock().getDescendants(false); for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; // Hide all connections of all children. diff --git a/core/toolbox.js b/core/toolbox.js index 149b4e159..4ea684723 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -396,7 +396,7 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { */ Blockly.Toolbox.prototype.addColour_ = function(opt_tree) { var tree = opt_tree || this.tree_; - var children = tree.getChildren(); + var children = tree.getChildren(false); for (var i = 0, child; child = children[i]; i++) { var element = child.getRowElement(); if (element) { diff --git a/core/workspace.js b/core/workspace.js index 436cf135b..c9459fa69 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -171,13 +171,25 @@ Blockly.Workspace.prototype.getTopBlocks = function(ordered) { }; /** - * Find all blocks in workspace. No particular order. + * Find all blocks in workspace. Blocks are optionally sorted + * by position; top to bottom (with slight LTR or RTL bias). + * @param {boolean} ordered Sort the list if true. * @return {!Array.} Array of blocks. */ -Blockly.Workspace.prototype.getAllBlocks = function() { - var blocks = this.getTopBlocks(false); - for (var i = 0; i < blocks.length; i++) { - blocks.push.apply(blocks, blocks[i].getChildren()); +Blockly.Workspace.prototype.getAllBlocks = function(ordered) { + if (ordered) { + // Slow, but ordered. + var topBlocks = this.getTopBlocks(true); + var blocks = []; + for (var i = 0; i < topBlocks.length; i++) { + blocks.push.apply(blocks, topBlocks[i].getDescendants(true)); + } + } else { + // Fast, but in no particular order. + var blocks = this.getTopBlocks(false); + for (var i = 0; i < blocks.length; i++) { + blocks.push.apply(blocks, blocks[i].getChildren(false)); + } } return blocks; }; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index c3e10c987..fe7273e95 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1268,9 +1268,9 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) { var deleteList = []; function addDeletableBlocks(block) { if (block.isDeletable()) { - deleteList = deleteList.concat(block.getDescendants()); + deleteList = deleteList.concat(block.getDescendants(false)); } else { - var children = block.getChildren(); + var children = block.getChildren(false); for (var i = 0; i < children.length; i++) { addDeletableBlocks(children[i]); } diff --git a/core/xml.js b/core/xml.js index ec6089075..9af982ca2 100644 --- a/core/xml.js +++ b/core/xml.js @@ -517,7 +517,7 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) { try { var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace); // Generate list of all blocks. - var blocks = topBlock.getDescendants(); + var blocks = topBlock.getDescendants(false); if (workspace.rendered) { // Hide connections to speed up assembly. topBlock.setConnectionsHidden(true); @@ -735,7 +735,7 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { } if (xmlBlock.nodeName.toLowerCase() == 'shadow') { // Ensure all children are also shadows. - var children = block.getChildren(); + var children = block.getChildren(false); for (var i = 0, child; child = children[i]; i++) { goog.asserts.assert( child.isShadow(), 'Shadow block not allowed non-shadow child.'); From 49be85b89db54f3dfdb5b847a327333549e2f32e Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Tue, 17 Apr 2018 12:38:06 -0700 Subject: [PATCH 007/101] Correct block color reference in demos/graph (#1794) --- demos/graph/index.html | 52 ++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/demos/graph/index.html b/demos/graph/index.html index f8ebcdbe2..e7c2b6a98 100644 --- a/demos/graph/index.html +++ b/demos/graph/index.html @@ -192,42 +192,34 @@ if (typeof google == 'object') { } // Define the custom blocks and their JS generators. -Blockly.Blocks['graph_get_x'] = { - // x variable getter. - init: function() { - this.jsonInit({ - "message0": "x", - "output": "Number", - "colour": Blockly.Blocks.variables.HUE, - "tooltip": Blockly.Msg.VARIABLES_GET_TOOLTIP, - "helpUrl": Blockly.Msg.VARIABLES_GET_HELPURL - }); - } -}; +Blockly.defineBlocksWithJsonArray([{ + "type": "graph_get_x", + "message0": "x", + "output": "Number", + "colour": Blockly.Msg.VARIABLES_HUE, + "tooltip": Blockly.Msg.VARIABLES_GET_TOOLTIP, + "helpUrl": Blockly.Msg.VARIABLES_GET_HELPURL +}]); Blockly.JavaScript['graph_get_x'] = function(block) { // x variable getter. return ['x', Blockly.JavaScript.ORDER_ATOMIC]; }; -Blockly.Blocks['graph_set_y'] = { - // y variable setter. - init: function() { - this.jsonInit({ - "message0": "y = %1", - "args0": [ - { - "type": "input_value", - "name": "VALUE", - "check": "Number" - } - ], - "colour": Blockly.Blocks.variables.HUE, - "tooltip": Blockly.Msg.VARIABLES_SET_TOOLTIP, - "helpUrl": Blockly.Msg.VARIABLES_SET_HELPURL - }); - } -}; +Blockly.defineBlocksWithJsonArray([{ + "type": "graph_set_y", + "message0": "y = %1", + "args0": [ + { + "type": "input_value", + "name": "VALUE", + "check": "Number" + } + ], + "colour": Blockly.Msg.VARIABLES_HUE, + "tooltip": Blockly.Msg.VARIABLES_SET_TOOLTIP, + "helpUrl": Blockly.Msg.VARIABLES_SET_HELPURL +}]); Blockly.JavaScript['graph_set_y'] = function(block) { // y variable setter. From b2cb96b5b1cd6ab37b065cb525189bf36ba062fb Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Tue, 17 Apr 2018 13:29:50 -0700 Subject: [PATCH 008/101] Mark Blockly.Constants.Colour and .Lists depreacted. (#1797) Both are only used by the hue constants, which are already deprecated. --- blocks/colour.js | 2 +- blocks/lists.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blocks/colour.js b/blocks/colour.js index b0eb506f7..737fe9466 100644 --- a/blocks/colour.js +++ b/blocks/colour.js @@ -30,7 +30,7 @@ 'use strict'; goog.provide('Blockly.Blocks.colour'); // Deprecated -goog.provide('Blockly.Constants.Colour'); +goog.provide('Blockly.Constants.Colour'); // deprecated, 2018 April 5 goog.require('Blockly.Blocks'); goog.require('Blockly'); diff --git a/blocks/lists.js b/blocks/lists.js index c401a99ad..45f596ddc 100644 --- a/blocks/lists.js +++ b/blocks/lists.js @@ -30,7 +30,7 @@ 'use strict'; goog.provide('Blockly.Blocks.lists'); // Deprecated -goog.provide('Blockly.Constants.Lists'); +goog.provide('Blockly.Constants.Lists'); // deprecated, 2018 April 5 goog.require('Blockly.Blocks'); goog.require('Blockly'); From b4450b7dee33723878e3649e2181b4ac4eea5832 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 17 Apr 2018 11:34:32 -0700 Subject: [PATCH 009/101] Reverse deprecated argument order to womtoblock in accessible. --- accessible/block-connection.service.js | 2 +- accessible/field-segment.component.js | 2 +- accessible/media/accessible.css | 2 +- accessible/toolbox-modal.service.js | 6 +++--- accessible/variable-add-modal.component.js | 4 ++-- accessible/variable-remove-modal.component.js | 8 ++++---- accessible/variable-rename-modal.component.js | 6 +++--- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/accessible/block-connection.service.js b/accessible/block-connection.service.js index c1ee03aac..d2c5cd866 100644 --- a/accessible/block-connection.service.js +++ b/accessible/block-connection.service.js @@ -101,7 +101,7 @@ blocklyApp.BlockConnectionService = ng.core.Class({ }, attachToMarkedConnection: function(block) { var xml = Blockly.Xml.blockToDom(block); - var reconstitutedBlock = Blockly.Xml.domToBlock(blocklyApp.workspace, xml); + var reconstitutedBlock = Blockly.Xml.domToBlock(xml, blocklyApp.workspace); var targetConnection = null; if (this.markedConnection_.targetBlock() && diff --git a/accessible/field-segment.component.js b/accessible/field-segment.component.js index 8dda20d71..c68afb455 100644 --- a/accessible/field-segment.component.js +++ b/accessible/field-segment.component.js @@ -182,7 +182,7 @@ blocklyApp.FieldSegmentComponent = ng.core.Component({ }, // Sets the value on a dropdown input. setDropdownValue: function(optionValue) { - this.optionValue = optionValue + this.optionValue = optionValue; if (this.optionValue == 'NO_ACTION') { return; } diff --git a/accessible/media/accessible.css b/accessible/media/accessible.css index 500ee7323..285fe8a95 100644 --- a/accessible/media/accessible.css +++ b/accessible/media/accessible.css @@ -26,7 +26,7 @@ } .blocklySidebarButton[disabled] { border: 1px solid #ccc; - opacity: 0.5; + opacity: .5; } .blocklyAriaLiveStatus { diff --git a/accessible/toolbox-modal.service.js b/accessible/toolbox-modal.service.js index 5d93cea88..70c665c4d 100644 --- a/accessible/toolbox-modal.service.js +++ b/accessible/toolbox-modal.service.js @@ -71,7 +71,7 @@ blocklyApp.ToolboxModalService = ng.core.Class({ this.allToolboxCategories = Array.from(toolboxCategoryElts).map( function(categoryElt) { var tmpWorkspace = new Blockly.Workspace(); - var custom = categoryElt.attributes.custom + var custom = categoryElt.attributes.custom; // TODO (corydiers): Implement custom flyouts once #1153 is solved. if (custom && custom.value == Blockly.VARIABLE_CATEGORY_NAME) { var varBlocks = @@ -95,7 +95,7 @@ blocklyApp.ToolboxModalService = ng.core.Class({ // containing all the top-level blocks. var tmpWorkspace = new Blockly.Workspace(); Array.from(toolboxXmlElt.children).forEach(function(topLevelNode) { - Blockly.Xml.domToBlock(tmpWorkspace, topLevelNode); + Blockly.Xml.domToBlock(topLevelNode, tmpWorkspace); }); that.allToolboxCategories = [{ @@ -214,7 +214,7 @@ blocklyApp.ToolboxModalService = ng.core.Class({ this.showModal_(this.toolboxCategoriesForNewGroup, function(block) { var blockDescription = that.utilsService.getBlockDescription(block); var xml = Blockly.Xml.blockToDom(block); - var newBlockId = Blockly.Xml.domToBlock(blocklyApp.workspace, xml).id; + var newBlockId = Blockly.Xml.domToBlock(xml, blocklyApp.workspace).id; // Invoke a digest cycle, so that the DOM settles. setTimeout(function() { diff --git a/accessible/variable-add-modal.component.js b/accessible/variable-add-modal.component.js index 1965e4e5e..2c3f77bbc 100644 --- a/accessible/variable-add-modal.component.js +++ b/accessible/variable-add-modal.component.js @@ -71,7 +71,7 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({ this.workspace = blocklyApp.workspace; this.variableModalService = variableService; this.audioService = audioService; - this.keyboardInputService = keyboardService + this.keyboardInputService = keyboardService; this.modalIsVisible = false; this.activeButtonIndex = -1; @@ -103,7 +103,7 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({ getInteractiveElements: Blockly.CommonModal.getInteractiveElements, // Gets the container with interactive elements. getInteractiveContainer: function() { - return document.getElementById("varForm"); + return document.getElementById('varForm'); }, // Submits the name change for the variable. submit: function() { diff --git a/accessible/variable-remove-modal.component.js b/accessible/variable-remove-modal.component.js index b542e88a5..ce27f5c25 100644 --- a/accessible/variable-remove-modal.component.js +++ b/accessible/variable-remove-modal.component.js @@ -74,17 +74,17 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({ this.treeService = treeService; this.variableModalService = variableService; this.audioService = audioService; - this.keyboardInputService = keyboardService + this.keyboardInputService = keyboardService; this.modalIsVisible = false; this.activeButtonIndex = -1; - this.currentVariableName = ""; + this.currentVariableName = ''; this.count = 0; var that = this; this.variableModalService.registerPreRemoveShowHook( function(name, count) { that.currentVariableName = name; - that.count = count + that.count = count; that.modalIsVisible = true; Blockly.CommonModal.setupKeyboardOverrides(that); @@ -106,7 +106,7 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({ getInteractiveElements: Blockly.CommonModal.getInteractiveElements, // Gets the container with interactive elements. getInteractiveContainer: function() { - return document.getElementById("varForm"); + return document.getElementById('varForm'); }, getNumVariables: function() { return this.variableModalService.getNumVariables(this.currentVariableName); diff --git a/accessible/variable-rename-modal.component.js b/accessible/variable-rename-modal.component.js index e276e739c..3bb92e75b 100644 --- a/accessible/variable-rename-modal.component.js +++ b/accessible/variable-rename-modal.component.js @@ -72,10 +72,10 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({ this.workspace = blocklyApp.workspace; this.variableModalService = variableService; this.audioService = audioService; - this.keyboardInputService = keyboardService + this.keyboardInputService = keyboardService; this.modalIsVisible = false; this.activeButtonIndex = -1; - this.currentVariableName = ""; + this.currentVariableName = ''; var that = this; this.variableModalService.registerPreRenameShowHook( @@ -106,7 +106,7 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({ getInteractiveElements: Blockly.CommonModal.getInteractiveElements, // Gets the container with interactive elements. getInteractiveContainer: function() { - return document.getElementById("varForm"); + return document.getElementById('varForm'); }, // Submits the name change for the variable. submit: function() { From 1a37bebd89b9dddc7c0511970102ac4a7e0817ce Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Wed, 18 Apr 2018 08:40:45 -0700 Subject: [PATCH 010/101] Refactor of checkBlockColourConstant_() (#1798) Rewrote Blockly.checkBlockColourConstant_(..). * Last argument is now the expected value of the constant (replacing the `removed` argument). The prevents warnings when a Msg colour constant is overridden (the correct way). * If a value for the constant is not found, do not warn (#1790). --- core/blockly.js | 52 +++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/core/blockly.js b/core/blockly.js index ad6d61b87..1c1c80983 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -576,54 +576,52 @@ Blockly.isNumber = function(str) { */ Blockly.checkBlockColourConstants = function() { Blockly.checkBlockColourConstant_( - 'LOGIC_HUE', ['Blocks', 'logic', 'HUE'], /* removed */ true); + 'LOGIC_HUE', ['Blocks', 'logic', 'HUE'], undefined); Blockly.checkBlockColourConstant_( - 'LOGIC_HUE', ['Constants', 'Logic', 'HUE'], /* removed */ false); + 'LOGIC_HUE', ['Constants', 'Logic', 'HUE'], 210); Blockly.checkBlockColourConstant_( - 'LOOPS_HUE', ['Blocks', 'loops', 'HUE'], /* removed */ true); + 'LOOPS_HUE', ['Blocks', 'loops', 'HUE'], undefined); Blockly.checkBlockColourConstant_( - 'LOOPS_HUE', ['Constants', 'Loops', 'HUE'], /* removed */ false); + 'LOOPS_HUE', ['Constants', 'Loops', 'HUE'], 120); Blockly.checkBlockColourConstant_( - 'MATH_HUE', ['Blocks', 'math', 'HUE'], /* removed */ true); + 'MATH_HUE', ['Blocks', 'math', 'HUE'], undefined); Blockly.checkBlockColourConstant_( - 'MATH_HUE', ['Constants', 'Math', 'HUE'], /* removed */ false); + 'MATH_HUE', ['Constants', 'Math', 'HUE'], 230); Blockly.checkBlockColourConstant_( - 'TEXTS_HUE', ['Blocks', 'texts', 'HUE'], /* removed */ true); + 'TEXTS_HUE', ['Blocks', 'texts', 'HUE'], undefined); Blockly.checkBlockColourConstant_( - 'TEXTS_HUE', ['Constants', 'Text', 'HUE'], /* removed */ false); + 'TEXTS_HUE', ['Constants', 'Text', 'HUE'], 160); Blockly.checkBlockColourConstant_( - 'LISTS_HUE', ['Blocks', 'lists', 'HUE'], /* removed */ true); + 'LISTS_HUE', ['Blocks', 'lists', 'HUE'], undefined); Blockly.checkBlockColourConstant_( - 'LISTS_HUE', ['Constants', 'Lists', 'HUE'], /* removed */ false); + 'LISTS_HUE', ['Constants', 'Lists', 'HUE'], 260); Blockly.checkBlockColourConstant_( - 'COLOUR_HUE', ['Blocks', 'colour', 'HUE'], /* removed */ true); + 'COLOUR_HUE', ['Blocks', 'colour', 'HUE'], undefined); Blockly.checkBlockColourConstant_( - 'COLOUR_HUE', ['Constants', 'Colour', 'HUE'], /* removed */ false); + 'COLOUR_HUE', ['Constants', 'Colour', 'HUE'], 20); Blockly.checkBlockColourConstant_( - 'VARIABLES_HUE', ['Blocks', 'variables', 'HUE'], /* removed */ true); + 'VARIABLES_HUE', ['Blocks', 'variables', 'HUE'], undefined); Blockly.checkBlockColourConstant_( - 'VARIABLES_HUE', ['Constants', 'Variables', 'HUE'], /* removed */ false); + 'VARIABLES_HUE', ['Constants', 'Variables', 'HUE'], 330); // Blockly.Blocks.variables_dynamic.HUE never existed. Blockly.checkBlockColourConstant_( - 'VARIABLES_DYNAMIC_HUE', ['Constants', 'VariablesDynamic', 'HUE'], /* removed */ false); + 'VARIABLES_DYNAMIC_HUE', ['Constants', 'VariablesDynamic', 'HUE'], 310); Blockly.checkBlockColourConstant_( - 'PROCEDURES_HUE', ['Blocks', 'procedures', 'HUE'], /* removed */ true); + 'PROCEDURES_HUE', ['Blocks', 'procedures', 'HUE'], undefined); // Blockly.Constants.Procedures.HUE never existed. }; /** - * Checks for a constant in the Blockly namespace, verifying it either does - * not exist (if flagged as removed) or has the same value as the Msg - * constant. Prints a warning if this is not true. + * Checks for a constant in the Blockly namespace, verifying it is undefined or + * has the old/original value. Prints a warning if this is not true. * @param {string} msgName The Msg constant identifier. * @param {Array} blocklyNamePath The name parts of the tested * constant. - * @param {boolean} removed Whether the constant was already removed and should - * evaluate to undefined. + * @param {number|undefined} expectedValue The expected value of the constant. * @private */ Blockly.checkBlockColourConstant_ = function( - msgName, blocklyNamePath, removed) { + msgName, blocklyNamePath, expectedValue) { var namePath = 'Blockly'; var value = Blockly; for (var i =0; i < blocklyNamePath.length; ++i) { @@ -632,12 +630,10 @@ Blockly.checkBlockColourConstant_ = function( value = value[blocklyNamePath[i]]; } } - var isExpectedValue = removed ? - value === undefined : - value == Blockly.Msg[msgName]; // Intentionally coercing the value. - if (!isExpectedValue) { - var warningPattern = removed ? - '%1 is unused and has been removed. Override Blockly.Msg.%2.' : + + if (value && value !== expectedValue) { + var warningPattern = (expectedValue === undefined) ? + '%1 has been removed. Use Blockly.Msg.%2.' : '%1 is deprecated and unused. Override Blockly.Msg.%2.'; var warning = warningPattern.replace('%1', namePath).replace('%2', msgName); console.warn(warning); From 2bfff4a3351026c897e20ccb2676aab2e476a7a1 Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Wed, 18 Apr 2018 13:35:30 -0700 Subject: [PATCH 011/101] Warn if jsonInit() receives a colour attribute without a value. (#1795) * Warn if jsonInit() receives a colour attribute without a value. * Extract colour init code into function. * Adding block type name to prior warnings. --- core/block.js | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/core/block.js b/core/block.js index 55536627e..50762ea05 100644 --- a/core/block.js +++ b/core/block.js @@ -1086,23 +1086,15 @@ Blockly.Block.prototype.appendDummyInput = function(opt_name) { * @param {!Object} json Structured data describing the block. */ Blockly.Block.prototype.jsonInit = function(json) { - var blockTypeName = json['type']; + 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.'); + warningPrefix + 'Must not have both an output and a previousStatement.'); // Set basic properties of block. - if (json['colour'] !== undefined) { - var rawValue = json['colour']; - try { - this.setColour(rawValue); - } catch (colorError) { - console.warn( - 'Block "' + blockTypeName + '": Illegal color value: ', rawValue); - } - } + this.jsonInitColour_(json, warningPrefix); // Interpolate the message blocks. var i = 0; @@ -1140,8 +1132,10 @@ Blockly.Block.prototype.jsonInit = function(json) { this.setHelpUrl(localizedValue); } if (goog.isString(json['extensions'])) { - console.warn('JSON attribute \'extensions\' should be an array of ' + - 'strings. Found raw string in JSON for \'' + json['type'] + '\' block.'); + console.warn( + warningPrefix + 'JSON attribute \'extensions\' should be an array of' + + ' strings. Found raw string in JSON for \'' + json['type'] + + '\' block.'); json['extensions'] = [json['extensions']]; // Correct and continue. } @@ -1159,6 +1153,27 @@ Blockly.Block.prototype.jsonInit = function(json) { } }; +/** + * Initialize the colour of this block from the JSON description. + * @param {!Object} json Structured data describing the block. + * @param {string} warningPrefix Warning prefix string identifying block. + * @private + */ +Blockly.Block.prototype.jsonInitColour_ = function(json, warningPrefix) { + if ('colour' in json) { + if (json['colour'] === undefined) { + console.warn(warningPrefix + 'Undefined color value.'); + } else { + var rawValue = json['colour']; + try { + this.setColour(rawValue); + } catch (colorError) { + console.warn(warningPrefix + 'Illegal color value: ', rawValue); + } + } + } +}; + /** * Add key/values from mixinObj to this block object. By default, this method * will check that the keys in mixinObj will not overwrite existing values in From 75459abfddda176a0c779ec555154d9563a2d67e Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Wed, 18 Apr 2018 15:32:26 -0700 Subject: [PATCH 012/101] Removing namespace declarations for deprecated and unused namespaces. (#1796) These namespaces use to contain the original block hue constants, but were deprecated (via comment, except for procedures) over a year ago. With the removal of those constants, the declarations were creating empty, unused objects. --- blocks/colour.js | 1 - blocks/lists.js | 1 - blocks/logic.js | 1 - blocks/loops.js | 1 - blocks/math.js | 1 - blocks/procedures.js | 2 -- blocks/text.js | 1 - blocks/variables.js | 1 - 8 files changed, 9 deletions(-) diff --git a/blocks/colour.js b/blocks/colour.js index 737fe9466..696e2eaf7 100644 --- a/blocks/colour.js +++ b/blocks/colour.js @@ -29,7 +29,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.colour'); // Deprecated goog.provide('Blockly.Constants.Colour'); // deprecated, 2018 April 5 goog.require('Blockly.Blocks'); diff --git a/blocks/lists.js b/blocks/lists.js index 45f596ddc..e5f6c4548 100644 --- a/blocks/lists.js +++ b/blocks/lists.js @@ -29,7 +29,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.lists'); // Deprecated goog.provide('Blockly.Constants.Lists'); // deprecated, 2018 April 5 goog.require('Blockly.Blocks'); diff --git a/blocks/logic.js b/blocks/logic.js index 15b08170f..f0b818759 100644 --- a/blocks/logic.js +++ b/blocks/logic.js @@ -29,7 +29,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.logic'); // Deprecated goog.provide('Blockly.Constants.Logic'); goog.require('Blockly.Blocks'); diff --git a/blocks/loops.js b/blocks/loops.js index a6db80d11..cc569b263 100644 --- a/blocks/loops.js +++ b/blocks/loops.js @@ -29,7 +29,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.loops'); // Deprecated goog.provide('Blockly.Constants.Loops'); goog.require('Blockly.Blocks'); diff --git a/blocks/math.js b/blocks/math.js index 378149e5f..ba64482bf 100644 --- a/blocks/math.js +++ b/blocks/math.js @@ -29,7 +29,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.math'); // Deprecated goog.provide('Blockly.Constants.Math'); goog.require('Blockly.Blocks'); diff --git a/blocks/procedures.js b/blocks/procedures.js index 4811c1a26..987141d75 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -24,8 +24,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.procedures'); - goog.require('Blockly.Blocks'); goog.require('Blockly'); diff --git a/blocks/text.js b/blocks/text.js index 5b5ae7d2f..6c963d1ff 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -24,7 +24,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.texts'); // Deprecated goog.provide('Blockly.Constants.Text'); goog.require('Blockly.Blocks'); diff --git a/blocks/variables.js b/blocks/variables.js index cf8fa2d96..b10941601 100644 --- a/blocks/variables.js +++ b/blocks/variables.js @@ -29,7 +29,6 @@ */ 'use strict'; -goog.provide('Blockly.Blocks.variables'); // Deprecated. goog.provide('Blockly.Constants.Variables'); goog.require('Blockly.Blocks'); From d8202f8caecb9e7dc179d644f7da8883ee64f85b Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 23 Mar 2018 12:24:56 -0700 Subject: [PATCH 013/101] Create base classes for block and variable events, instead of stuffing everything into Abstract --- blockly_compressed.js | 312 +++++++++++++++------------------ blockly_uncompressed.js | 89 ++++------ core/events/abstract.js | 37 +--- core/events/block/base.js | 69 ++++++++ core/events/block/change.js | 8 +- core/events/block/create.js | 8 +- core/events/block/delete.js | 8 +- core/events/block/move.js | 8 +- core/events/ui/ui_base.js | 12 +- core/events/variable/base.js | 70 ++++++++ core/events/variable/create.js | 6 +- core/events/variable/delete.js | 6 +- core/events/variable/rename.js | 6 +- tests/jsunit/event_test.js | 13 +- 14 files changed, 358 insertions(+), 294 deletions(-) create mode 100644 core/events/block/base.js create mode 100644 core/events/variable/base.js diff --git a/blockly_compressed.js b/blockly_compressed.js index f544695e9..30e2ab3b7 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -2,30 +2,27 @@ 'use strict'; var $jscomp=$jscomp||{};$jscomp.scope={};var COMPILED=!0,goog=goog||{};goog.global=this;goog.isDef=function(a){return void 0!==a};goog.isString=function(a){return"string"==typeof a};goog.isBoolean=function(a){return"boolean"==typeof a};goog.isNumber=function(a){return"number"==typeof a}; -goog.exportPath_=function(a,b,c){a=a.split(".");c=c||goog.global;a[0]in c||"undefined"==typeof c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)!a.length&&goog.isDef(b)?c[d]=b:c=c[d]&&c[d]!==Object.prototype[d]?c[d]:c[d]={}}; +goog.exportPath_=function(a,b,c){a=a.split(".");c=c||goog.global;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)!a.length&&goog.isDef(b)?c[d]=b:c=c[d]&&c[d]!==Object.prototype[d]?c[d]:c[d]={}}; goog.define=function(a,b){var c=b;if(!COMPILED){var d=goog.global.CLOSURE_UNCOMPILED_DEFINES,e=goog.global.CLOSURE_DEFINES;d&&void 0===d.nodeType&&Object.prototype.hasOwnProperty.call(d,a)?c=d[a]:e&&void 0===e.nodeType&&Object.prototype.hasOwnProperty.call(e,a)&&(c=e[a])}goog.exportPath_(a,c)};goog.DEBUG=!1;goog.LOCALE="en";goog.TRUSTED_SITE=!0;goog.STRICT_MODE_COMPATIBLE=!1;goog.DISALLOW_TEST_ONLY_CODE=COMPILED&&!goog.DEBUG;goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING=!1; -goog.provide=function(a){if(goog.isInModuleLoader_())throw Error("goog.provide can not be used within a module.");if(!COMPILED&&goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');goog.constructNamespace_(a)};goog.constructNamespace_=function(a,b){if(!COMPILED){delete goog.implicitNamespaces_[a];for(var c=a;(c=c.substring(0,c.lastIndexOf(".")))&&!goog.getObjectByName(c);)goog.implicitNamespaces_[c]=!0}goog.exportPath_(a,b)};goog.VALID_MODULE_RE_=/^[a-zA-Z_$][a-zA-Z0-9._$]*$/; -goog.module=function(a){if(!goog.isString(a)||!a||-1==a.search(goog.VALID_MODULE_RE_))throw Error("Invalid module identifier");if(!goog.isInGoogModuleLoader_())throw Error("Module "+a+" has been loaded incorrectly. Note, modules cannot be loaded as normal scripts. They require some kind of pre-processing step. You're likely trying to load a module via a script tag or as a part of a concatenated bundle without rewriting the module. For more info see: https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide."); -if(goog.moduleLoaderState_.moduleName)throw Error("goog.module may only be called once per module.");goog.moduleLoaderState_.moduleName=a;if(!COMPILED){if(goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');delete goog.implicitNamespaces_[a]}}; -goog.module.get=function(a){if(!COMPILED&&a in goog.loadedModules_){if(goog.loadedModules_[a].type!=goog.ModuleType.GOOG)throw Error("Can only goog.module.get for goog.modules.");if(goog.loadedModules_[a].moduleId!=a)throw Error("Cannot goog.module.get by path.");}return goog.module.getInternal_(a)};goog.module.getInternal_=function(a){if(!COMPILED){if(a in goog.loadedModules_)return goog.loadedModules_[a].exports;if(!goog.implicitNamespaces_[a])return a=goog.getObjectByName(a),null!=a?a:null}return null}; -goog.ModuleType={ES6:"es6",GOOG:"goog"};goog.moduleLoaderState_=null;goog.isInModuleLoader_=function(){return goog.isInGoogModuleLoader_()||goog.isInEs6ModuleLoader_()};goog.isInGoogModuleLoader_=function(){return!!goog.moduleLoaderState_&&goog.moduleLoaderState_.type==goog.ModuleType.GOOG};goog.isInEs6ModuleLoader_=function(){return!!goog.moduleLoaderState_&&goog.moduleLoaderState_.type==goog.ModuleType.ES6};goog.getModulePath_=function(){return goog.moduleLoaderState_&&goog.moduleLoaderState_.path}; -goog.module.declareLegacyNamespace=function(){if(!COMPILED&&!goog.isInGoogModuleLoader_())throw Error("goog.module.declareLegacyNamespace must be called from within a goog.module");if(!COMPILED&&!goog.moduleLoaderState_.moduleName)throw Error("goog.module must be called prior to goog.module.declareLegacyNamespace.");goog.moduleLoaderState_.declareLegacyNamespace=!0}; +goog.provide=function(a){if(goog.isInModuleLoader_())throw Error("goog.provide can not be used within a goog.module.");if(!COMPILED&&goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');goog.constructNamespace_(a)};goog.constructNamespace_=function(a,b){if(!COMPILED){delete goog.implicitNamespaces_[a];for(var c=a;(c=c.substring(0,c.lastIndexOf(".")))&&!goog.getObjectByName(c);)goog.implicitNamespaces_[c]=!0}goog.exportPath_(a,b)};goog.VALID_MODULE_RE_=/^[a-zA-Z_$][a-zA-Z0-9._$]*$/; +goog.module=function(a){if(!goog.isString(a)||!a||-1==a.search(goog.VALID_MODULE_RE_))throw Error("Invalid module identifier");if(!goog.isInModuleLoader_())throw Error("Module "+a+" has been loaded incorrectly. Note, modules cannot be loaded as normal scripts. They require some kind of pre-processing step. You're likely trying to load a module via a script tag or as a part of a concatenated bundle without rewriting the module. For more info see: https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide."); +if(goog.moduleLoaderState_.moduleName)throw Error("goog.module may only be called once per module.");goog.moduleLoaderState_.moduleName=a;if(!COMPILED){if(goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');delete goog.implicitNamespaces_[a]}};goog.module.get=function(a){return goog.module.getInternal_(a)}; +goog.module.getInternal_=function(a){if(!COMPILED){if(a in goog.loadedModules_)return goog.loadedModules_[a];if(!goog.implicitNamespaces_[a])return a=goog.getObjectByName(a),null!=a?a:null}return null};goog.moduleLoaderState_=null;goog.isInModuleLoader_=function(){return null!=goog.moduleLoaderState_}; +goog.module.declareLegacyNamespace=function(){if(!COMPILED&&!goog.isInModuleLoader_())throw Error("goog.module.declareLegacyNamespace must be called from within a goog.module");if(!COMPILED&&!goog.moduleLoaderState_.moduleName)throw Error("goog.module must be called prior to goog.module.declareLegacyNamespace.");goog.moduleLoaderState_.declareLegacyNamespace=!0}; goog.setTestOnly=function(a){if(goog.DISALLOW_TEST_ONLY_CODE)throw a=a||"",Error("Importing test-only code into non-debug environment"+(a?": "+a:"."));};goog.forwardDeclare=function(a){};COMPILED||(goog.isProvided_=function(a){return a in goog.loadedModules_||!goog.implicitNamespaces_[a]&&goog.isDefAndNotNull(goog.getObjectByName(a))},goog.implicitNamespaces_={"goog.module":!0}); -goog.getObjectByName=function(a,b){for(var c=a.split("."),d=b||goog.global,e=0;e>>0);goog.uidCounter_=0;goog.getHashCode=goog.getUid; -goog.removeHashCode=goog.removeUid;goog.cloneObject=function(a){var b=goog.typeOf(a);if("object"==b||"array"==b){if("function"===typeof a.clone)return a.clone();b="array"==b?[]:{};for(var c in a)b[c]=goog.cloneObject(a[c]);return b}return a};goog.bindNative_=function(a,b,c){return a.call.apply(a.bind,arguments)}; +goog.removeHashCode=goog.removeUid;goog.cloneObject=function(a){var b=goog.typeOf(a);if("object"==b||"array"==b){if(a.clone)return a.clone();b="array"==b?[]:{};for(var c in a)b[c]=goog.cloneObject(a[c]);return b}return a};goog.bindNative_=function(a,b,c){return a.call.apply(a.bind,arguments)}; goog.bindJs_=function(a,b,c){if(!a)throw Error();if(2Number(a[1])?!1:b('(()=>{"use strict";class X{constructor(){if(new.target!=String)throw 1;this.x=42}}let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof String))throw 1;for(const a of[2,3]){if(a==2)continue;function f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()==3}})()')});a("es6-impl",function(){return!0});a("es7",function(){return b("2 ** 2 == 4")}); -a("es8",function(){return b("async () => 1, true")});a("es9",function(){return b("({...rest} = {}), true")});a("es_next",function(){return b("({...rest} = {}), true")});return c},goog.Transpiler.prototype.needsTranspile=function(a,b){if("always"==goog.TRANSPILE)return!0;if("never"==goog.TRANSPILE)return!1;this.requiresTranspilation_||(this.requiresTranspilation_=this.createRequiresTranspilation_());if(a in this.requiresTranspilation_)return this.requiresTranspilation_[a];throw Error("Unknown language mode: "+ -a);},goog.Transpiler.prototype.transpile=function(a,b){return goog.transpile_(a,b)},goog.transpiler_=new goog.Transpiler,goog.protectScriptTag_=function(a){return a.replace(/<\/(SCRIPT)/ig,"\\x3c/$1")},goog.DebugLoader_=function(){this.dependencies_={};this.idToPath_={};this.written_={};this.loadingDeps_=[];this.depsToLoad_=[];this.paused_=!1;this.factory_=new goog.DependencyFactory(goog.transpiler_);this.deferredCallbacks_={};this.deferredQueue_=[]},goog.DebugLoader_.prototype.bootstrap=function(a, -b){function c(){d&&(goog.global.setTimeout(d,0),d=null)}var d=b;if(a.length){for(var e=[],f=0;f\x3c/script>")}else{var d=b.createElement("script");d.defer=goog.Dependency.defer_;d.async=!1;d.type="text/javascript";goog.DebugLoader_.IS_OLD_IE_?(a.pause(),d.onreadystatechange=function(){if("loaded"==d.readyState||"complete"==d.readyState)a.loaded(),a.resume()}):d.onload=function(){d.onload= -null;a.loaded()};d.src=this.path;b.head.appendChild(d)}}else goog.logToConsole_("Cannot use default debug loader outside of HTML documents."),"deps.js"==this.relativePath?(goog.logToConsole_("Consider setting CLOSURE_IMPORT_SCRIPT before loading base.js, or seting CLOSURE_NO_DEPS to true."),a.loaded()):a.pause()},goog.Es6ModuleDependency=function(a,b,c,d){goog.Dependency.call(this,a,b,[],c,d)},goog.inherits(goog.Es6ModuleDependency,goog.Dependency),goog.Es6ModuleDependency.prototype.load=function(a){function b(a, -b){b?d.write(' + diff --git a/tests/jsunit/workspace_comment_test.js b/tests/jsunit/workspace_comment_test.js new file mode 100644 index 000000000..a36c2cdf5 --- /dev/null +++ b/tests/jsunit/workspace_comment_test.js @@ -0,0 +1,86 @@ +/** + * @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'); + +var workspace; + +function workspaceCommentTest_setUp() { + workspace = new Blockly.Workspace(); +} + +function workspaceCommentTest_tearDown() { + workspace.dispose(); +} + +function test_noWorkspaceComments() { + workspaceCommentTest_setUp(); + try { + assertEquals('Empty workspace: no comments (1).', 0, workspace.getTopComments(true).length); + assertEquals('Empty workspace: no comments (2).', 0, workspace.getTopComments(false).length); + workspace.clear(); + assertEquals('Empty workspace: no comments (3).', 0, workspace.getTopComments(true).length); + assertEquals('Empty workspace: no comments (4).', 0, workspace.getTopComments(false).length); + } finally { + workspaceCommentTest_tearDown(); + } +} + +function test_oneWorkspaceComment() { + workspaceCommentTest_setUp(); + try { + var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, 'comment id'); + assertEquals('One comment on workspace (1).', 1, workspace.getTopComments(true).length); + assertEquals('One comment on workspace (2).', 1, workspace.getTopComments(false).length); + assertEquals('Comment db contains this comment.', comment, workspace.commentDB_['comment id']); + workspace.clear(); + assertEquals('Cleared workspace: no comments (3).', 0, workspace.getTopComments(true).length); + assertEquals('Cleared workspace: no comments (4).', 0, workspace.getTopComments(false).length); + assertFalse('Comment DB does not contain this comment.', 'comment id' in workspace.commentDB_); + } finally { + workspaceCommentTest_tearDown(); + } +} + +function test_getWorkspaceCommentById() { + workspaceCommentTest_setUp(); + try { + var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, 'comment id'); + assertEquals('Getting a comment by id.', comment, workspace.getCommentById('comment id')); + assertEquals('No comment found.', null, workspace.getCommentById('not a comment')); + comment.dispose(); + assertEquals('Can\'t find the comment.', null, workspace.getCommentById('comment id')); + } finally { + workspaceCommentTest_tearDown(); + } +} + +function test_disposeWsCommentTwice() { + workspaceCommentTest_setUp(); + try { + var comment = new Blockly.WorkspaceComment(workspace, 'comment text', 0, 0, 'comment id'); + comment.dispose(); + // Nothing should go wrong the second time dispose is called. + comment.dispose(); + }finally { + workspaceCommentTest_tearDown(); + } +} From 8fe1ab802e8431515629782c41c102ebd77c9e00 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 30 Apr 2018 08:18:49 +0200 Subject: [PATCH 034/101] Localisation updates from https://translatewiki.net. --- msg/json/it.json | 7 +++++-- msg/json/tr.json | 11 +++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/msg/json/it.json b/msg/json/it.json index d68bd6d52..ba105455d 100644 --- a/msg/json/it.json +++ b/msg/json/it.json @@ -10,7 +10,8 @@ "JackLantern", "Selven", "Samuele2002", - "Greis" + "Greis", + "Sarah Bernabei" ] }, "VARIABLES_DEFAULT_NAME": "elemento", @@ -18,6 +19,7 @@ "DUPLICATE_BLOCK": "Duplica", "ADD_COMMENT": "Aggiungi commento", "REMOVE_COMMENT": "Rimuovi commento", + "DUPLICATE_COMMENT": "Duplicare il commento", "EXTERNAL_INPUTS": "Ingressi esterni", "INLINE_INPUTS": "Ingressi in linea", "DELETE_BLOCK": "Cancella blocco", @@ -341,5 +343,6 @@ "PROCEDURES_CREATE_DO": "Crea '%1'", "PROCEDURES_IFRETURN_TOOLTIP": "Se un valore è vero allora restituisce un secondo valore.", "PROCEDURES_IFRETURN_HELPURL": "http://c2.com/cgi/wiki?GuardClause", - "PROCEDURES_IFRETURN_WARNING": "Attenzioneː Questo blocco può essere usato solo all'interno di una definizione di funzione." + "PROCEDURES_IFRETURN_WARNING": "Attenzioneː Questo blocco può essere usato solo all'interno di una definizione di funzione.", + "WORKSPACE_COMMENT_DEFAULT_TEXT": "Scrivi qualcosa..." } diff --git a/msg/json/tr.json b/msg/json/tr.json index 0d6a76285..6ae71ccb3 100644 --- a/msg/json/tr.json +++ b/msg/json/tr.json @@ -16,7 +16,8 @@ "Alpkant", "Bulgu", "By erdo can", - "Azerhan Özen" + "Azerhan Özen", + "Hedda" ] }, "VARIABLES_DEFAULT_NAME": "öge", @@ -24,6 +25,7 @@ "DUPLICATE_BLOCK": "Çoğalt", "ADD_COMMENT": "Yorum Ekle", "REMOVE_COMMENT": "Yorumu Sil", + "DUPLICATE_COMMENT": "Yinelenen Yorum", "EXTERNAL_INPUTS": "Harici Girişler", "INLINE_INPUTS": "Satır içi girdiler", "DELETE_BLOCK": "Bloğu Sil", @@ -43,6 +45,10 @@ "RENAME_VARIABLE": "Değişkeni yeniden adlandır...", "RENAME_VARIABLE_TITLE": "Tüm '%1' değişkenlerini yeniden isimlendir:", "NEW_VARIABLE": "Değişken oluştur...", + "NEW_STRING_VARIABLE": "Dizi değişkeni oluştur...", + "NEW_NUMBER_VARIABLE": "Sayı değişkeni oluştur...", + "NEW_COLOUR_VARIABLE": "Renk değişkeni oluştur...", + "NEW_VARIABLE_TYPE_TITLE": "Yeni değişken tipi:", "NEW_VARIABLE_TITLE": "Yeni değişken ismi :", "VARIABLE_ALREADY_EXISTS": "'%1' isimli değişken adı zaten var.", "VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE": "'%1' isimli değişken '%2' tipli başka bir değişkende tanımlı.", @@ -372,5 +378,6 @@ "PROCEDURES_CREATE_DO": "'%1' oluştur", "PROCEDURES_IFRETURN_TOOLTIP": "Eğer değer doğruysa, ikinci değere geri dön.", "PROCEDURES_IFRETURN_HELPURL": "http://c2.com/cgi/wiki?GuardClause", - "PROCEDURES_IFRETURN_WARNING": "Uyarı: Bu blok yalnızca bir fonksiyon tanımı içinde kullanılır." + "PROCEDURES_IFRETURN_WARNING": "Uyarı: Bu blok yalnızca bir fonksiyon tanımı içinde kullanılır.", + "WORKSPACE_COMMENT_DEFAULT_TEXT": "Bir şeyler söyle..." } From 744bb9b6eb48d0a6c26eaaba36a517b8d00822df Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 30 Apr 2018 13:01:04 -0700 Subject: [PATCH 035/101] Add comments explaining unique IDs on clip paths --- core/zoom_controls.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/zoom_controls.js b/core/zoom_controls.js index 8359a28f9..48eddb2ba 100644 --- a/core/zoom_controls.js +++ b/core/zoom_controls.js @@ -95,6 +95,10 @@ Blockly.ZoomControls.prototype.top_ = 0; Blockly.ZoomControls.prototype.createDom = function() { this.svgGroup_ = Blockly.utils.createSvgElement('g', {'class': 'blocklyZoom'}, null); + + // Each filter/pattern needs a unique ID for the case of multiple Blockly + // instances on a page. Browser behaviour becomes undefined otherwise. + // https://neil.fraser.name/news/2015/11/01/ var rnd = String(Math.random()).substring(2); this.createZoomOutSvg_(rnd); this.createZoomInSvg_(rnd); @@ -160,7 +164,9 @@ Blockly.ZoomControls.prototype.position = function() { /** * Create the zoom in icon and its event handler. - * @param {string} rnd The random string to use to identify the clip paths. + * @param {string} rnd The random string to use as a suffix in the clip path's + * ID. These IDs must be unique in case there are multiple Blockly + * instances on the same page. * @private */ Blockly.ZoomControls.prototype.createZoomOutSvg_ = function(rnd) { @@ -207,7 +213,9 @@ Blockly.ZoomControls.prototype.createZoomOutSvg_ = function(rnd) { /** * Create the zoom out icon and its event handler. - * @param {string} rnd The random string to use to identify the clip paths. + * @param {string} rnd The random string to use as a suffix in the clip path's + * ID. These IDs must be unique in case there are multiple Blockly + * instances on the same page. * @private */ Blockly.ZoomControls.prototype.createZoomInSvg_ = function(rnd) { @@ -255,7 +263,9 @@ Blockly.ZoomControls.prototype.createZoomInSvg_ = function(rnd) { /** * Create the zoom reset icon and its event handler. - * @param {string} rnd The random string to use to identify the clip paths. + * @param {string} rnd The random string to use as a suffix in the clip path's + * ID. These IDs must be unique in case there are multiple Blockly + * instances on the same page. * @private */ Blockly.ZoomControls.prototype.createZoomResetSvg_ = function(rnd) { From 9ce9c6f7d9ad3017ef2e544df36b7fc4e18b95d8 Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Mon, 30 Apr 2018 13:08:02 -0700 Subject: [PATCH 036/101] Using %{BKY_} notation for toolbox category colours. (#1826) --- demos/custom-dialogs/index.html | 6 ++--- demos/generator/index.html | 8 +++---- demos/graph/index.html | 6 ++--- demos/interpreter/async-execution.html | 15 +++++++----- demos/interpreter/step-execution.html | 15 +++++++----- demos/maxBlocks/index.html | 8 +++---- demos/rtl/index.html | 19 ++++++++------- demos/storage/index.html | 8 +++---- demos/toolbox/index.html | 17 ++++++++------ tests/multi_playground.html | 16 ++++++------- tests/playground.html | 32 +++++++++++++------------- 11 files changed, 81 insertions(+), 69 deletions(-) diff --git a/demos/custom-dialogs/index.html b/demos/custom-dialogs/index.html index 517ca12b4..2b2b7be07 100644 --- a/demos/custom-dialogs/index.html +++ b/demos/custom-dialogs/index.html @@ -33,7 +33,7 @@