From 1d44036cf93d92bf58307c197d96aeb4b1145cc8 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Fri, 12 Feb 2016 19:34:51 -0800 Subject: [PATCH 1/3] Add missing move when unshadowing and missing XY when deleting. --- core/block.js | 7 +++++++ core/events.js | 10 ++++------ core/xml.js | 20 +++++++++++++++----- demos/maxBlocks/index.html | 2 +- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/core/block.js b/core/block.js index 83618b77e..0e92d660a 100644 --- a/core/block.js +++ b/core/block.js @@ -514,6 +514,13 @@ Blockly.Block.prototype.setShadow = function(shadow) { // Fire a creation event. var xmlBlock = Blockly.Xml.blockToDom(this); Blockly.Events.fire(new Blockly.Events.Create(this.workspace, xmlBlock)); + var moveEvent = new Blockly.Events.Move(this); + // Claim that the block was at 0,0 and is being connected. + moveEvent.oldParentId = undefined; + moveEvent.oldInputName = undefined; + moveEvent.oldCoordinate = new goog.math.Coordinate(0, 0); + moveEvent.recordNew(); + Blockly.Events.fire(moveEvent); } }; diff --git a/core/events.js b/core/events.js index a6715328f..ddef12665 100644 --- a/core/events.js +++ b/core/events.js @@ -193,15 +193,13 @@ goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract); * @constructor */ Blockly.Events.Delete = function(block) { + if (block.getParent()) { + throw 'Connected blocks cannot be deleted.'; + } this.type = Blockly.Events.DELETE; this.workspaceId = block.workspace.id; this.blockId = block.id; - this.oldXml = Blockly.Xml.blockToDom(block); - var parent = block.getParent(); - if (parent) { - this.oldParentId = parent.id; - this.oldInput = getInputWithBlock(block).name - } + this.oldXml = Blockly.Xml.blockToDomWithXY(block); }; goog.inherits(Blockly.Events.Delete, Blockly.Events.Abstract); diff --git a/core/xml.js b/core/xml.js index 1472ecc31..a1c905a0c 100644 --- a/core/xml.js +++ b/core/xml.js @@ -44,15 +44,25 @@ Blockly.Xml.workspaceToDom = function(workspace) { var xml = goog.dom.createDom('xml'); var blocks = workspace.getTopBlocks(true); for (var i = 0, block; block = blocks[i]; i++) { - var element = Blockly.Xml.blockToDom(block); - var xy = block.getRelativeToSurfaceXY(); - element.setAttribute('x', Math.round(workspace.RTL ? width - xy.x : xy.x)); - element.setAttribute('y', Math.round(xy.y)); - xml.appendChild(element); + xml.appendChild(Blockly.Xml.blockToDomWithXY(block)); } return xml; }; +/** + * Encode a block subtree as XML with XY coordinates. + * @param {!Blockly.Block} block The root block to encode. + * @return {!Element} Tree of XML elements. + */ +Blockly.Xml.blockToDomWithXY = function(block) { + var element = Blockly.Xml.blockToDom(block); + var xy = block.getRelativeToSurfaceXY(); + element.setAttribute('x', + Math.round(block.workspace.RTL ? width - xy.x : xy.x)); + element.setAttribute('y', Math.round(xy.y)); + return element; +}; + /** * Encode a block subtree as XML. * @param {!Blockly.Block} block The root block to encode. diff --git a/demos/maxBlocks/index.html b/demos/maxBlocks/index.html index fd6b7eab0..841120121 100644 --- a/demos/maxBlocks/index.html +++ b/demos/maxBlocks/index.html @@ -85,7 +85,7 @@ maxBlocks: 5, toolbox: document.getElementById('toolbox')}); - function onchange() { + function onchange(event) { document.getElementById('capacity').innerHTML = workspace.remainingCapacity(); } From a0a61d2f3386285297ed416bb2634c348170241b Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Fri, 12 Feb 2016 22:09:21 -0800 Subject: [PATCH 2/3] Add event grouping. Add event monitoring to playground. --- core/block.js | 2 ++ core/block_svg.js | 2 ++ core/events.js | 52 +++++++++++++++++++++++++++++++++--------- tests/playground.html | 53 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 94 insertions(+), 15 deletions(-) diff --git a/core/block.js b/core/block.js index 0e92d660a..16c8604a3 100644 --- a/core/block.js +++ b/core/block.js @@ -511,6 +511,7 @@ Blockly.Block.prototype.setShadow = function(shadow) { } this.isShadow_ = shadow; if (Blockly.Events.isEnabled() && !shadow) { + Blockly.Events.group = Blockly.genUid(); // Fire a creation event. var xmlBlock = Blockly.Xml.blockToDom(this); Blockly.Events.fire(new Blockly.Events.Create(this.workspace, xmlBlock)); @@ -521,6 +522,7 @@ Blockly.Block.prototype.setShadow = function(shadow) { moveEvent.oldCoordinate = new goog.math.Coordinate(0, 0); moveEvent.recordNew(); Blockly.Events.fire(moveEvent); + Blockly.Events.group = ''; } }; diff --git a/core/block_svg.js b/core/block_svg.js index aab5cffa0..70db3bbbe 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -460,6 +460,7 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) { // dragged instead. return; } else { + Blockly.Events.group = Blockly.genUid(); // Left-click (or middle click) Blockly.removeAllRanges(); Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); @@ -528,6 +529,7 @@ Blockly.BlockSvg.prototype.onMouseUp_ = function(e) { Blockly.highlightedConnection_ = null; } Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN); + Blockly.Events.group = ''; }; /** diff --git a/core/events.js b/core/events.js index ddef12665..222737f1c 100644 --- a/core/events.js +++ b/core/events.js @@ -27,6 +27,12 @@ goog.provide('Blockly.Events'); +/** + * Group ID for new events. Grouped events are indivisible. + * @type {string} + */ +Blockly.Events.group = ''; + /** * Allow change events to be created and fired. * @type {number} @@ -159,10 +165,14 @@ Blockly.Events.isEnabled = function() { }; /** - * Abstract class for a change event. + * Abstract class for an event. + * @param {!Blockly.Workspace} workspace The workspace. * @constructor */ -Blockly.Events.Abstract = function() {}; +Blockly.Events.Abstract = function(workspace) { + this.workspaceId = workspace.id; + this.group = Blockly.Events.group; +}; /** * Does this event record any change of state? @@ -180,12 +190,17 @@ Blockly.Events.Abstract.prototype.isNull = function() { * @constructor */ Blockly.Events.Create = function(workspace, xml) { - this.type = Blockly.Events.CREATE; - this.workspaceId = workspace.id; + Blockly.Events.Create.superClass_.constructor.call(this, workspace); this.xml = xml; }; goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract); +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Create.prototype.type = Blockly.Events.CREATE; + /** * Class for a block deletion event. * @param {!Blockly.Block} block The deleted block. @@ -196,13 +211,18 @@ Blockly.Events.Delete = function(block) { if (block.getParent()) { throw 'Connected blocks cannot be deleted.'; } - this.type = Blockly.Events.DELETE; - this.workspaceId = block.workspace.id; + Blockly.Events.Delete.superClass_.constructor.call(this, block.workspace); this.blockId = block.id; this.oldXml = Blockly.Xml.blockToDomWithXY(block); }; goog.inherits(Blockly.Events.Delete, Blockly.Events.Abstract); +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Delete.prototype.type = Blockly.Events.DELETE; + /** * Class for a block change event. * @param {!Blockly.Block} block The deleted block. @@ -214,15 +234,20 @@ goog.inherits(Blockly.Events.Delete, Blockly.Events.Abstract); * @constructor */ Blockly.Events.Change = function(block, element, name, oldValue, newValue) { - this.type = Blockly.Events.CHANGE; - this.workspaceId = block.workspace.id; + Blockly.Events.Change.superClass_.constructor.call(this, block.workspace); this.blockId = block.id; this.element = element; this.name = name; this.oldValue = oldValue; this.newValue = newValue; }; -goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract); +goog.inherits(Blockly.Events.Change, Blockly.Events.Abstract); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Change.prototype.type = Blockly.Events.CHANGE; /** * Does this event record any change of state? @@ -239,8 +264,7 @@ Blockly.Events.Change.prototype.isNull = function() { * @constructor */ Blockly.Events.Move = function(block) { - this.type = Blockly.Events.MOVE; - this.workspaceId = block.workspace.id; + Blockly.Events.Move.superClass_.constructor.call(this, block.workspace); this.blockId = block.id; var location = this.currentLocation_(); @@ -250,6 +274,12 @@ Blockly.Events.Move = function(block) { }; goog.inherits(Blockly.Events.Move, Blockly.Events.Abstract); +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.Move.prototype.type = Blockly.Events.MOVE; + /** * Record the block's new location. Called after the move. */ diff --git a/tests/playground.html b/tests/playground.html index d8fadd60e..2f3d98167 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -81,6 +81,15 @@ function start() { scaleSpeed: 1.1 }, }); + // Restore previously displayed text. + var text = sessionStorage.getItem('textarea'); + if (text) { + document.getElementById('importExport').value = text; + } + taChange(); + // Restore event logging state. + var state = sessionStorage.getItem('logEvents'); + logEvents(Boolean(state)); } function toXml() { @@ -89,17 +98,49 @@ function toXml() { output.value = Blockly.Xml.domToPrettyText(xml); output.focus(); output.select(); + taChange(); } function fromXml() { var input = document.getElementById('importExport'); var xml = Blockly.Xml.textToDom(input.value); Blockly.Xml.domToWorkspace(workspace, xml); + taChange(); } function toCode(lang) { var output = document.getElementById('importExport'); output.value = Blockly[lang].workspaceToCode(workspace); + taChange(); +} + +// Disable the "Import from XML" button if the XML is invalid. +// Preserve text between page reloads. +function taChange() { + var textarea = document.getElementById('importExport'); + sessionStorage.setItem('textarea', textarea.value) + var valid = true; + try { + Blockly.Xml.textToDom(textarea.value); + } catch (e) { + valid = false; + } + document.getElementById('import').disabled = !valid; +} + +function logEvents(state) { + var checkbox = document.getElementById('logCheck'); + checkbox.checked = state; + sessionStorage.setItem('logEvents', Number(state)); + if (state) { + workspace.addChangeListener(logger); + } else { + workspace.removeChangeListener(logger); + } +} + +function logger(e) { + console.log(e); } function airstrike(n) { @@ -526,7 +567,7 @@ h1 {

  - +
  @@ -536,15 +577,19 @@ h1 {  
- +

-

- Stress test: + Stress test:  

+

+ Log events:   + +

From 5297344a8b7eaa4f55f728b2017606bae1cd5746 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 16 Feb 2016 13:04:47 -0800 Subject: [PATCH 3/3] Fix RTL XML. --- blockly_compressed.js | 20 +++++++++++--------- core/block.js | 2 +- core/block_svg.js | 2 +- core/utils.js | 1 + core/xml.js | 8 ++++---- msg/js/az.js | 32 ++++++++++++++++---------------- 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/blockly_compressed.js b/blockly_compressed.js index fe261af93..56b4e9e71 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -974,7 +974,7 @@ Blockly.ZoomControls.prototype.createDom=function(){var a=this.workspace_;this.s d.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a.options.pathToMedia+Blockly.SPRITE.url);c=Blockly.createSvgElement("clipPath",{id:"blocklyZoominClipPath"+b},this.svgGroup_);Blockly.createSvgElement("rect",{width:32,height:32,y:43},c);var e=Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,height:Blockly.SPRITE.height,x:-32,y:-49,"clip-path":"url(#blocklyZoominClipPath"+b+")"},this.svgGroup_);e.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a.options.pathToMedia+ Blockly.SPRITE.url);c=Blockly.createSvgElement("clipPath",{id:"blocklyZoomresetClipPath"+b},this.svgGroup_);Blockly.createSvgElement("rect",{width:32,height:32},c);b=Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,height:Blockly.SPRITE.height,y:-92,"clip-path":"url(#blocklyZoomresetClipPath"+b+")"},this.svgGroup_);b.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a.options.pathToMedia+Blockly.SPRITE.url);Blockly.bindEvent_(b,"mousedown",a,a.zoomReset);Blockly.bindEvent_(e, "mousedown",null,function(b){a.zoomCenter(1);b.stopPropagation()});Blockly.bindEvent_(d,"mousedown",null,function(b){a.zoomCenter(-1);b.stopPropagation()});return this.svgGroup_};Blockly.ZoomControls.prototype.init=function(a){this.bottom_=this.MARGIN_BOTTOM_+a;return this.bottom_+this.HEIGHT_};Blockly.ZoomControls.prototype.dispose=function(){this.svgGroup_&&(goog.dom.removeNode(this.svgGroup_),this.svgGroup_=null);this.workspace_=null}; -Blockly.ZoomControls.prototype.position=function(){var a=this.workspace_.getMetrics();a&&(this.left_=this.workspace_.RTL?this.MARGIN_SIDE_+Blockly.Scrollbar.scrollbarThickness:a.viewWidth+a.absoluteLeft-this.WIDTH_-this.MARGIN_SIDE_-Blockly.Scrollbar.scrollbarThickness,this.top_=a.viewHeight+a.absoluteTop-this.HEIGHT_-this.bottom_,this.svgGroup_.setAttribute("transform","translate("+this.left_+","+this.top_+")"))};Blockly.Xml={};Blockly.Xml.workspaceToDom=function(a){var b;a.RTL&&(b=a.getWidth());for(var c=goog.dom.createDom("xml"),d=a.getTopBlocks(!0),e=0,f;f=d[e];e++){var g=Blockly.Xml.blockToDom(f);f=f.getRelativeToSurfaceXY();g.setAttribute("x",Math.round(a.RTL?b-f.x:f.x));g.setAttribute("y",Math.round(f.y));c.appendChild(g)}return c}; +Blockly.ZoomControls.prototype.position=function(){var a=this.workspace_.getMetrics();a&&(this.left_=this.workspace_.RTL?this.MARGIN_SIDE_+Blockly.Scrollbar.scrollbarThickness:a.viewWidth+a.absoluteLeft-this.WIDTH_-this.MARGIN_SIDE_-Blockly.Scrollbar.scrollbarThickness,this.top_=a.viewHeight+a.absoluteTop-this.HEIGHT_-this.bottom_,this.svgGroup_.setAttribute("transform","translate("+this.left_+","+this.top_+")"))};Blockly.Xml={};Blockly.Xml.workspaceToDom=function(a){var b=goog.dom.createDom("xml");a=a.getTopBlocks(!0);for(var c=0,d;d=a[c];c++)b.appendChild(Blockly.Xml.blockToDomWithXY(d));return b};Blockly.Xml.blockToDomWithXY=function(a){var b;workspace.RTL&&(b=workspace.getWidth());var c=Blockly.Xml.blockToDom(a),d=a.getRelativeToSurfaceXY();c.setAttribute("x",Math.round(a.workspace.RTL?b-d.x:d.x));c.setAttribute("y",Math.round(d.y));return c}; Blockly.Xml.blockToDom=function(a){var b=goog.dom.createDom(a.isShadow()?"shadow":"block");b.setAttribute("type",a.type);b.setAttribute("id",a.id);if(a.mutationToDom){var c=a.mutationToDom();c&&(c.hasChildNodes()||c.hasAttributes())&&b.appendChild(c)}for(var c=0,d;d=a.inputList[c];c++)for(var e=0,f;f=d.fieldRow[e];e++)if(f.name&&f.EDITABLE){var g=goog.dom.createDom("field",null,f.getValue());g.setAttribute("name",f.name);b.appendChild(g)}if(c=a.getCommentText())c=goog.dom.createDom("comment",null, c),"object"==typeof a.comment&&(c.setAttribute("pinned",a.comment.isVisible()),d=a.comment.getBubbleSize(),c.setAttribute("h",d.height),c.setAttribute("w",d.width)),b.appendChild(c);a.data&&(c=goog.dom.createDom("data",null,a.data),b.appendChild(c));for(c=0;d=a.inputList[c];c++){var h;f=!0;d.type!=Blockly.DUMMY_INPUT&&(g=d.connection.targetBlock(),d.type==Blockly.INPUT_VALUE?h=goog.dom.createDom("value"):d.type==Blockly.NEXT_STATEMENT&&(h=goog.dom.createDom("statement")),e=d.connection.getShadowDom(), !e||g&&g.isShadow()||h.appendChild(Blockly.Xml.cloneShadow_(e)),g&&(h.appendChild(Blockly.Xml.blockToDom(g)),f=!1),h.setAttribute("name",d.name),f||b.appendChild(h))}a.inputsInlineDefault!=a.inputsInline&&b.setAttribute("inline",a.inputsInline);a.isCollapsed()&&b.setAttribute("collapsed",!0);a.disabled&&b.setAttribute("disabled",!0);a.isDeletable()||a.isShadow()||b.setAttribute("deletable",!1);a.isMovable()||a.isShadow()||b.setAttribute("movable",!1);a.isEditable()||b.setAttribute("editable",!1); @@ -1051,7 +1051,8 @@ Blockly.Block.prototype.getInputWithBlock=function(a){for(var b=0,c;c=this.input Blockly.Block.prototype.getRootBlock=function(){var a,b=this;do a=b,b=a.parentBlock_;while(b);return a};Blockly.Block.prototype.getChildren=function(){return this.childBlocks_}; Blockly.Block.prototype.setParent=function(a){var b;Blockly.Events.isEnabled()&&!this.isShadow()&&(b=new Blockly.Events.Move(this));if(this.parentBlock_){for(var c=this.parentBlock_.childBlocks_,d,e=0;d=c[e];e++)if(d==this){c.splice(e,1);break}this.parentBlock_=null;this.previousConnection&&this.previousConnection.targetConnection&&this.previousConnection.disconnect();this.outputConnection&&this.outputConnection.targetConnection&&this.outputConnection.disconnect()}else this.workspace.removeTopBlock(this); (this.parentBlock_=a)?a.childBlocks_.push(this):this.workspace.addTopBlock(this);b&&(b.recordNew(),Blockly.Events.fire(b))};Blockly.Block.prototype.getDescendants=function(){for(var a=[this],b,c=0;b=this.childBlocks_[c];c++)a.push.apply(a,b.getDescendants());return a};Blockly.Block.prototype.isDeletable=function(){return this.deletable_&&!this.isShadow_&&!(this.workspace&&this.workspace.options.readOnly)};Blockly.Block.prototype.setDeletable=function(a){this.deletable_=a}; -Blockly.Block.prototype.isMovable=function(){return this.movable_&&!this.isShadow_&&!(this.workspace&&this.workspace.options.readOnly)};Blockly.Block.prototype.setMovable=function(a){this.movable_=a};Blockly.Block.prototype.isShadow=function(){return this.isShadow_};Blockly.Block.prototype.setShadow=function(a){this.isShadow_!=a&&(this.isShadow_=a,Blockly.Events.isEnabled()&&!a&&(a=Blockly.Xml.blockToDom(this),Blockly.Events.fire(new Blockly.Events.Create(this.workspace,a))))}; +Blockly.Block.prototype.isMovable=function(){return this.movable_&&!this.isShadow_&&!(this.workspace&&this.workspace.options.readOnly)};Blockly.Block.prototype.setMovable=function(a){this.movable_=a};Blockly.Block.prototype.isShadow=function(){return this.isShadow_}; +Blockly.Block.prototype.setShadow=function(a){this.isShadow_!=a&&(this.isShadow_=a,Blockly.Events.isEnabled()&&!a&&(Blockly.Events.group=Blockly.genUid(),a=Blockly.Xml.blockToDom(this),Blockly.Events.fire(new Blockly.Events.Create(this.workspace,a)),a=new Blockly.Events.Move(this),a.oldParentId=void 0,a.oldInputName=void 0,a.oldCoordinate=new goog.math.Coordinate(0,0),a.recordNew(),Blockly.Events.fire(a),Blockly.Events.group=""))}; Blockly.Block.prototype.isEditable=function(){return this.editable_&&!(this.workspace&&this.workspace.options.readOnly)};Blockly.Block.prototype.setEditable=function(a){this.editable_=a;a=0;for(var b;b=this.inputList[a];a++)for(var c=0,d;d=b.fieldRow[c];c++)d.updateEditable()}; Blockly.Block.prototype.setConnectionsHidden=function(a){if(!a&&this.isCollapsed()){if(this.outputConnection&&this.outputConnection.setHidden(a),this.previousConnection&&this.previousConnection.setHidden(a),this.nextConnection){this.nextConnection.setHidden(a);var b=this.nextConnection.targetBlock();b&&b.setConnectionsHidden(a)}}else for(var c=this.getConnections_(!0),d=0;b=c[d];d++)b.setHidden(a),b.isSuperior()&&(b=b.targetBlock())&&b.setConnectionsHidden(a)}; Blockly.Block.prototype.setHelpUrl=function(a){this.helpUrl=a};Blockly.Block.prototype.setTooltip=function(a){this.tooltip=a};Blockly.Block.prototype.getColour=function(){return this.colour_};Blockly.Block.prototype.setColour=function(a){var b=parseFloat(a);if(isNaN(b))if(goog.isString(a)&&a.match(/^#[0-9a-fA-F]{6}$/))this.colour_=a;else throw"Invalid colour: "+a;else this.colour_=Blockly.hueToRgb(b);this.rendered&&this.updateColour()}; @@ -1093,10 +1094,10 @@ Blockly.BlockSvg.prototype.snapToGrid=function(){if(this.workspace&&0==Blockly.d Blockly.BlockSvg.prototype.getHeightWidth=function(){var a=this.height,b=this.width,c=this.getNextBlock();c?(c=c.getHeightWidth(),a+=c.height-4,b=Math.max(b,c.width)):this.nextConnection||this.outputConnection||(a+=2);return{height:a,width:b}}; Blockly.BlockSvg.prototype.setCollapsed=function(a){if(this.collapsed_!=a){for(var b=[],c=0,d;d=this.inputList[c];c++)b.push.apply(b,d.setVisible(!a));if(a){d=this.getIcons();for(c=0;cthis.workspace.remainingCapacity()&&(d.enabled=!1);c.push(d);this.isEditable()&&!this.collapsed_&&this.workspace.options.comments&&(d={enabled:!goog.userAgent.IE},this.comment?(d.text=Blockly.Msg.REMOVE_COMMENT, d.callback=function(){b.setCommentText(null)}):(d.text=Blockly.Msg.ADD_COMMENT,d.callback=function(){b.setCommentText("")}),c.push(d));if(!this.collapsed_)for(d=1;d