Merge branch 'develop' into feature/ghost_blocks

This commit is contained in:
Rachel Fenichel 2016-03-01 15:18:49 -08:00
commit 391e449c9c
44 changed files with 5922 additions and 5843 deletions

10
.editorconfig Normal file
View file

@ -0,0 +1,10 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

View file

@ -913,9 +913,10 @@ Blockly.Connection.prototype.hideAll=function(){this.setHidden(!0);if(this.targe
Blockly.Connection.prototype.unhideAll=function(){this.setHidden(!1);var a=[];if(this.type!=Blockly.INPUT_VALUE&&this.type!=Blockly.NEXT_STATEMENT)return a;var b=this.targetBlock();if(b){var c;b.isCollapsed()?(c=[],b.outputConnection&&c.push(b.outputConnection),b.nextConnection&&c.push(b.nextConnection),b.previousConnection&&c.push(b.previousConnection)):c=b.getConnections_(!0);for(var d=0;d<c.length;d++)a.push.apply(a,c[d].unhideAll());0==a.length&&(a[0]=b)}return a};Blockly.ConnectionDB=function(){};
Blockly.ConnectionDB.prototype=[];Blockly.ConnectionDB.constructor=Blockly.ConnectionDB;Blockly.ConnectionDB.prototype.addConnection_=function(a){if(a.inDB_)throw"Connection already in database.";if(!a.sourceBlock_.isInFlyout){for(var b=0,c=this.length;b<c;){var d=Math.floor((b+c)/2);if(this[d].y_<a.y_)b=d+1;else if(this[d].y_>a.y_)c=d;else{b=d;break}}this.splice(b,0,a);a.inDB_=!0}};
Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw"Connection not in database.";a.inDB_=!1;for(var b=0,c=this.length-2,d=c;b<d;)this[d].y_<a.y_?b=d:c=d,d=Math.floor((b+c)/2);for(c=b=d;0<=b&&this[b].y_==a.y_;){if(this[b]==a){this.splice(b,1);return}b--}do{if(this[c]==a){this.splice(c,1);return}c++}while(c<this.length&&this[c].y_==a.y_);throw"Unable to find connection in connectionDB.";};
Blockly.ConnectionDB.init=function(a){var b=[];b[Blockly.INPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.OUTPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.NEXT_STATEMENT]=new Blockly.ConnectionDB;b[Blockly.PREVIOUS_STATEMENT]=new Blockly.ConnectionDB;a.connectionDBList=b};Blockly.Field=function(a,b){this.size_=new goog.math.Size(0,25);this.setValue(a);this.setValidator(b)};Blockly.Field.cacheWidths_=null;Blockly.Field.cacheReference_=0;Blockly.Field.prototype.name=void 0;Blockly.Field.prototype.maxDisplayLength=50;Blockly.Field.prototype.text_="";Blockly.Field.prototype.sourceBlock_=null;Blockly.Field.prototype.visible_=!0;Blockly.Field.prototype.validator_=null;Blockly.Field.NBSP="\u00a0";Blockly.Field.prototype.EDITABLE=!0;
Blockly.Field.prototype.init=function(a){this.sourceBlock_||(this.sourceBlock_=a,this.fieldGroup_=Blockly.createSvgElement("g",{},null),this.visible_||(this.fieldGroup_.style.display="none"),this.borderRect_=Blockly.createSvgElement("rect",{rx:4,ry:4,x:-Blockly.BlockSvg.SEP_SPACE_X/2,y:0,height:16},this.fieldGroup_,this.sourceBlock_.workspace),this.textElement_=Blockly.createSvgElement("text",{"class":"blocklyText",y:this.size_.height-12.5},this.fieldGroup_),this.updateEditable(),a.getSvgRoot().appendChild(this.fieldGroup_),
this.mouseUpWrapper_=Blockly.bindEvent_(this.fieldGroup_,"mouseup",this,this.onMouseUp_),this.updateTextNode_(),Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,"",this.getValue())))};
Blockly.ConnectionDB.init=function(a){var b=[];b[Blockly.INPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.OUTPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.NEXT_STATEMENT]=new Blockly.ConnectionDB;b[Blockly.PREVIOUS_STATEMENT]=new Blockly.ConnectionDB;a.connectionDBList=b};Blockly.Field=function(a,b){this.size_=new goog.math.Size(Blockly.BlockSvg.FIELD_WIDTH,Blockly.BlockSvg.FIELD_HEIGHT);this.setValue(a);this.setValidator(b)};Blockly.Field.cacheWidths_=null;Blockly.Field.cacheReference_=0;Blockly.Field.prototype.name=void 0;Blockly.Field.prototype.maxDisplayLength=5;Blockly.Field.prototype.text_="";Blockly.Field.prototype.sourceBlock_=null;Blockly.Field.prototype.visible_=!0;Blockly.Field.prototype.validator_=null;Blockly.Field.NBSP="\u00a0";
Blockly.Field.prototype.EDITABLE=!0;
Blockly.Field.prototype.init=function(a){this.sourceBlock_||(this.sourceBlock_=a,this.fieldGroup_=Blockly.createSvgElement("g",{},null),this.visible_||(this.fieldGroup_.style.display="none"),this.borderRect_=Blockly.createSvgElement("rect",{rx:4,ry:4,x:-Blockly.BlockSvg.SEP_SPACE_X/2,y:0,height:Blockly.BlockSvg.FIELD_HEIGHT},this.fieldGroup_,this.sourceBlock_.workspace),this.textElement_=Blockly.createSvgElement("text",{"class":"blocklyText",y:this.size_.height/2+6.25,x:Blockly.BlockSvg.FIELD_WIDTH/
2,width:Blockly.BlockSvg.FIELD_WIDTH-Blockly.BlockSvg.SEP_SPACE_X,"text-anchor":"middle"},this.fieldGroup_),this.updateEditable(),a.getSvgRoot().appendChild(this.fieldGroup_),this.mouseUpWrapper_=Blockly.bindEvent_(this.fieldGroup_,"mouseup",this,this.onMouseUp_),this.updateTextNode_(),Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,"",this.getValue())))};
Blockly.Field.prototype.dispose=function(){this.mouseUpWrapper_&&(Blockly.unbindEvent_(this.mouseUpWrapper_),this.mouseUpWrapper_=null);this.sourceBlock_=null;goog.dom.removeNode(this.fieldGroup_);this.validator_=this.borderRect_=this.textElement_=this.fieldGroup_=null};
Blockly.Field.prototype.updateEditable=function(){this.EDITABLE&&this.sourceBlock_&&(this.sourceBlock_.isEditable()?(Blockly.addClass_(this.fieldGroup_,"blocklyEditableText"),Blockly.removeClass_(this.fieldGroup_,"blocklyNoNEditableText"),this.fieldGroup_.style.cursor=this.CURSOR):(Blockly.addClass_(this.fieldGroup_,"blocklyNonEditableText"),Blockly.removeClass_(this.fieldGroup_,"blocklyEditableText"),this.fieldGroup_.style.cursor=""))};Blockly.Field.prototype.isVisible=function(){return this.visible_};
Blockly.Field.prototype.setVisible=function(a){if(this.visible_!=a){this.visible_=a;var b=this.getSvgRoot();b&&(b.style.display=a?"block":"none",this.render_())}};Blockly.Field.prototype.setValidator=function(a){this.validator_=a};Blockly.Field.prototype.getSvgRoot=function(){return this.fieldGroup_};
@ -1094,7 +1095,6 @@ c.oldCoordinate=a.dragStartXY_;c.recordNew();Blockly.Events.fire(c);a.moveConnec
Blockly.BlockSvg.prototype.setParent=function(a){var b=this.getSvgRoot();if(this.parentBlock_&&b){var c=this.getRelativeToSurfaceXY();this.workspace.getCanvas().appendChild(b);b.setAttribute("transform","translate("+c.x+","+c.y+")")}Blockly.Field.startCache();Blockly.BlockSvg.superClass_.setParent.call(this,a);Blockly.Field.stopCache();a&&(c=this.getRelativeToSurfaceXY(),a.getSvgRoot().appendChild(b),a=this.getRelativeToSurfaceXY(),this.moveConnections_(a.x-c.x,a.y-c.y))};
Blockly.BlockSvg.prototype.getRelativeToSurfaceXY=function(){var a=0,b=0,c=this.getSvgRoot();if(c){do var d=Blockly.getRelativeXY_(c),a=a+d.x,b=b+d.y,c=c.parentNode;while(c&&c!=this.workspace.getCanvas())}return new goog.math.Coordinate(a,b)};Blockly.BlockSvg.prototype.moveBy=function(a,b){var c=new Blockly.Events.Move(this),d=this.getRelativeToSurfaceXY();this.getSvgRoot().setAttribute("transform","translate("+(d.x+a)+","+(d.y+b)+")");this.moveConnections_(a,b);c.recordNew();Blockly.Events.fire(c)};
Blockly.BlockSvg.prototype.snapToGrid=function(){if(this.workspace&&0==Blockly.dragMode_&&!this.getParent()&&!this.isInFlyout&&this.workspace.options.gridOptions&&this.workspace.options.gridOptions.snap){var a=this.workspace.options.gridOptions.spacing,b=a/2,c=this.getRelativeToSurfaceXY(),d=Math.round((c.x-b)/a)*a+b-c.x,a=Math.round((c.y-b)/a)*a+b-c.y,d=Math.round(d),a=Math.round(a);0==d&&0==a||this.moveBy(d,a)}};
Blockly.BlockSvg.prototype.getHeightWidth=function(){var a=this.height,b=this.width,c=this.getNextBlock();c?(c=c.getHeightWidth(),b+=c.width,a=Math.max(a,c.height)):this.nextConnection||this.outputConnection||(a+=2);return{height:a,width:b}};
Blockly.BlockSvg.prototype.tab=function(a,b){for(var c=[],d=0,e;e=this.inputList[d];d++){for(var f=0,g;g=e.fieldRow[f];f++)g instanceof Blockly.FieldTextInput&&c.push(g);e.connection&&(e=e.connection.targetBlock())&&c.push(e)}d=c.indexOf(a);-1==d&&(d=b?-1:c.length);(c=c[b?d+1:d-1])?c instanceof Blockly.Field?c.showEditor_():c.tab(null,b):(c=this.getParent())&&c.tab(this,b)};
Blockly.BlockSvg.prototype.onMouseDown_=function(a){if(!this.isInFlyout)if(this.workspace.markFocused(),Blockly.svgResize(this.workspace),Blockly.terminateDrag_(),this.select(),Blockly.hideChaff(),this.workspace.recordDeleteAreas(),Blockly.isRightButton(a))this.showContextMenu_(a);else if(this.isMovable()){Blockly.Events.group=Blockly.genUid();Blockly.removeAllRanges();Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);this.dragStartXY_=this.getRelativeToSurfaceXY();this.workspace.startDrag(a,this.dragStartXY_.x,
this.dragStartXY_.y);Blockly.dragMode_=1;Blockly.BlockSvg.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,this.onMouseUp_);Blockly.BlockSvg.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.onMouseMove_);this.draggedBubbles_=[];for(var b=this.getDescendants(),c=0,d;d=b[c];c++){d=d.getIcons();for(var e=0;e<d.length;e++){var f=d[e].getIconLocation();f.bubble=d[e];this.draggedBubbles_.push(f)}}}else return;a.stopPropagation()};
@ -1108,40 +1108,45 @@ Blockly.BlockSvg.prototype.onMouseMove_=function(a){if(!("mousemove"==a.type&&1>
b.x-this.dragStartXY_.x,b=b.y-this.dragStartXY_.y;d.translate_="translate("+c.x+","+c.y+")";d.setAttribute("transform",d.translate_+d.skew_);for(c=0;c<this.draggedBubbles_.length;c++)d=this.draggedBubbles_[c],d.bubble.setIconLocation(d.x+e,d.y+b);for(var d=this.getConnections_(!1),f=null,g=null,h=Blockly.SNAP_RADIUS,c=0;c<d.length;c++){var k=d[c],l=k.closest(h,e,b);l.connection&&(f=l.connection,g=k,h=l.radius)}Blockly.highlightedConnection_&&Blockly.highlightedConnection_!=f&&(Blockly.highlightedConnection_.unhighlight(),
Blockly.highlightedConnection_=null,Blockly.localConnection_=null);f&&f!=Blockly.highlightedConnection_&&(f.highlight(),Blockly.highlightedConnection_=f,Blockly.localConnection_=g);this.isDeletable()&&this.workspace.isDeleteArea(a)}}a.stopPropagation()};Blockly.BlockSvg.prototype.updateMovable=function(){this.isMovable()?Blockly.addClass_(this.svgGroup_,"blocklyDraggable"):Blockly.removeClass_(this.svgGroup_,"blocklyDraggable")};
Blockly.BlockSvg.prototype.setMovable=function(a){Blockly.BlockSvg.superClass_.setMovable.call(this,a);this.updateMovable()};Blockly.BlockSvg.prototype.setEditable=function(a){Blockly.BlockSvg.superClass_.setEditable.call(this,a);if(this.rendered)for(a=0;a<this.icons_.length;a++)this.icons_[a].updateEditable()};Blockly.BlockSvg.prototype.setShadow=function(a){Blockly.BlockSvg.superClass_.setShadow.call(this,a);this.updateColour()};Blockly.BlockSvg.prototype.getSvgRoot=function(){return this.svgGroup_};
Blockly.BlockSvg.SEP_SPACE_X=8;Blockly.BlockSvg.SEP_SPACE_Y=8;Blockly.BlockSvg.INLINE_PADDING_Y=5;Blockly.BlockSvg.MIN_BLOCK_Y=25;Blockly.BlockSvg.TAB_WIDTH=8;Blockly.BlockSvg.CORNER_RADIUS=4;Blockly.BlockSvg.HAT_CORNER_RADIUS=16;Blockly.BlockSvg.NOTCH_RADIUS=2.5;Blockly.BlockSvg.NOTCH_BASE_HEIGHT=32;Blockly.BlockSvg.NOTCH_HEIGHT=Blockly.BlockSvg.NOTCH_BASE_HEIGHT+Blockly.BlockSvg.NOTCH_RADIUS;Blockly.BlockSvg.NOTCH_WIDTH=Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4;
Blockly.BlockSvg.prototype.dispose=function(a,b){Blockly.Field.startCache();Blockly.selected==this&&Blockly.terminateDrag_();Blockly.ContextMenu.currentBlock==this&&Blockly.ContextMenu.hide();b&&this.rendered&&(this.unplug(a),this.disposeUiEffect());this.rendered=!1;Blockly.BlockSvg.superClass_.dispose.call(this,a);Blockly.Events.disable();for(var c=this.getIcons(),d=0;d<c.length;d++)c[d].dispose();Blockly.Events.enable();goog.dom.removeNode(this.svgGroup_);this.svgPath_=this.svgGroup_=null;Blockly.Field.stopCache()};
Blockly.BlockSvg.prototype.disposeUiEffect=function(){this.workspace.playAudio("delete");var a=Blockly.getSvgXY_(this.svgGroup_,this.workspace),b=this.svgGroup_.cloneNode(!0);b.translateX_=a.x;b.translateY_=a.y;b.setAttribute("transform","translate("+b.translateX_+","+b.translateY_+")");this.workspace.getParentSvg().appendChild(b);b.bBox_=b.getBBox();Blockly.BlockSvg.disposeUiStep_(b,this.RTL,new Date,this.workspace.scale)};
Blockly.BlockSvg.disposeUiStep_=function(a,b,c,d){var e=(new Date-c)/150;1<e?goog.dom.removeNode(a):(a.setAttribute("transform","translate("+(a.translateX_+(b?-1:1)*a.bBox_.width*d/2*e)+","+(a.translateY_+a.bBox_.height*d*e)+") scale("+(1-e)*d+")"),setTimeout(function(){Blockly.BlockSvg.disposeUiStep_(a,b,c,d)},10))};
Blockly.BlockSvg.prototype.disconnectUiEffect=function(){this.workspace.playAudio("disconnect");if(!(1>this.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<d?a.skew_="":(a.skew_="skewX("+Math.round(Math.sin(d*Math.PI*3)*(1-d)*b)+")",Blockly.BlockSvg.disconnectUiStop_.group=a,Blockly.BlockSvg.disconnectUiStop_.pid=setTimeout(function(){Blockly.BlockSvg.disconnectUiStep_(a,b,c)},10));a.setAttribute("transform",a.translate_+a.skew_)};
Blockly.BlockSvg.disconnectUiStop_=function(){if(Blockly.BlockSvg.disconnectUiStop_.group){clearTimeout(Blockly.BlockSvg.disconnectUiStop_.pid);var a=Blockly.BlockSvg.disconnectUiStop_.group;a.skew_="";a.setAttribute("transform",a.translate_);Blockly.BlockSvg.disconnectUiStop_.group=null}};Blockly.BlockSvg.disconnectUiStop_.pid=0;Blockly.BlockSvg.disconnectUiStop_.group=null;Blockly.BlockSvg.prototype.updateDisabled=function(){};
Blockly.BlockSvg.prototype.getCommentText=function(){return this.comment?this.comment.getText().replace(/\s+$/,"").replace(/ +\n/g,"\n"):""};Blockly.BlockSvg.prototype.setCommentText=function(a){var b=!1;goog.isString(a)?(this.comment||(this.comment=new Blockly.Comment(this),b=!0),this.comment.setText(a)):this.comment&&(this.comment.dispose(),b=!0);b&&this.rendered&&(this.render(),this.bumpNeighbours_())};
Blockly.BlockSvg.prototype.setWarningText=function(a,b){this.setWarningText.pid_||(this.setWarningText.pid_=Object.create(null));var c=b||"";if(c)this.setWarningText.pid_[c]&&(clearTimeout(this.setWarningText.pid_[c]),delete this.setWarningText.pid_[c]);else for(var d in this.setWarningText.pid_)clearTimeout(this.setWarningText.pid_[d]),delete this.setWarningText.pid_[d];if(2==Blockly.dragMode_){var e=this;this.setWarningText.pid_[c]=setTimeout(function(){e.workspace&&(delete e.setWarningText.pid_[c],
e.setWarningText(a,c))},100)}else{this.isInFlyout&&(a=null);d=!1;if(goog.isString(a))this.warning||(this.warning=new Blockly.Warning(this),d=!0),this.warning.setText(a,c);else if(this.warning&&!c)this.warning.dispose(),d=!0;else if(this.warning){d=this.warning.getText();this.warning.setText("",c);var f=this.warning.getText();f||this.warning.dispose();d=d==f}d&&this.rendered&&(this.render(),this.bumpNeighbours_())}};
Blockly.BlockSvg.prototype.setMutator=function(a){this.mutator&&this.mutator!==a&&this.mutator.dispose();a&&(a.block_=this,this.mutator=a,a.createIcon())};Blockly.BlockSvg.prototype.addSelect=function(){Blockly.addClass_(this.svgGroup_,"blocklySelected");this.svgGroup_.parentNode.appendChild(this.svgGroup_)};Blockly.BlockSvg.prototype.removeSelect=function(){Blockly.removeClass_(this.svgGroup_,"blocklySelected")};Blockly.BlockSvg.prototype.addDragging=function(){Blockly.addClass_(this.svgGroup_,"blocklyDragging")};
Blockly.BlockSvg.prototype.removeDragging=function(){Blockly.removeClass_(this.svgGroup_,"blocklyDragging")};Blockly.BlockSvg.render={};Blockly.BlockSvg.SEP_SPACE_X=8;Blockly.BlockSvg.SEP_SPACE_Y=8;Blockly.BlockSvg.INLINE_PADDING_Y=5;Blockly.BlockSvg.FIELD_HEIGHT=32;Blockly.BlockSvg.FIELD_WIDTH=48;Blockly.BlockSvg.NUMBER_FIELD_CORNER_RADIUS=16;Blockly.BlockSvg.TEXT_FIELD_CORNER_RADIUS=4;Blockly.BlockSvg.MIN_BLOCK_X=40;Blockly.BlockSvg.TAB_WIDTH=8;Blockly.BlockSvg.CORNER_RADIUS=4;Blockly.BlockSvg.HAT_CORNER_RADIUS=16;Blockly.BlockSvg.NOTCH_RADIUS=2.5;Blockly.BlockSvg.NOTCH_BASE_HEIGHT=32;
Blockly.BlockSvg.NOTCH_HEIGHT=Blockly.BlockSvg.NOTCH_BASE_HEIGHT+Blockly.BlockSvg.NOTCH_RADIUS;Blockly.BlockSvg.NOTCH_WIDTH=Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4;
Blockly.BlockSvg.NOTCH_PATH_DOWN="a "+-Blockly.BlockSvg.NOTCH_RADIUS+","+Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 0 "+Blockly.BlockSvg.NOTCH_RADIUS/2+","+Blockly.BlockSvg.NOTCH_RADIUS+" l "+(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4-Blockly.BlockSvg.NOTCH_RADIUS)+","+(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4-Blockly.BlockSvg.NOTCH_RADIUS)+" a "+-Blockly.BlockSvg.NOTCH_RADIUS+","+Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 1 "+Blockly.BlockSvg.NOTCH_RADIUS/2+","+Blockly.BlockSvg.NOTCH_RADIUS+" v "+(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/
2-Blockly.BlockSvg.NOTCH_RADIUS)+" a "+-Blockly.BlockSvg.NOTCH_RADIUS+","+Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 1 "+-Blockly.BlockSvg.NOTCH_RADIUS/2+","+Blockly.BlockSvg.NOTCH_RADIUS+" l "+(-Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4+Blockly.BlockSvg.NOTCH_RADIUS)+","+(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4-Blockly.BlockSvg.NOTCH_RADIUS)+" a "+-Blockly.BlockSvg.NOTCH_RADIUS+","+Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 0 "+-Blockly.BlockSvg.NOTCH_RADIUS/2+","+Blockly.BlockSvg.NOTCH_RADIUS;
Blockly.BlockSvg.NOTCH_PATH_UP="a "+Blockly.BlockSvg.NOTCH_RADIUS+","+-Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 1 "+Blockly.BlockSvg.NOTCH_RADIUS/2+","+-Blockly.BlockSvg.NOTCH_RADIUS+" l "+(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4-Blockly.BlockSvg.NOTCH_RADIUS)+","+-1*(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4-Blockly.BlockSvg.NOTCH_RADIUS)+" a "+Blockly.BlockSvg.NOTCH_RADIUS+","+-Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 0 "+Blockly.BlockSvg.NOTCH_RADIUS/2+","+-Blockly.BlockSvg.NOTCH_RADIUS+" v "+-1*(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/
2-Blockly.BlockSvg.NOTCH_RADIUS)+" a "+Blockly.BlockSvg.NOTCH_RADIUS+","+-Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 0 "+-Blockly.BlockSvg.NOTCH_RADIUS/2+","+-Blockly.BlockSvg.NOTCH_RADIUS+"l "+(-Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4+Blockly.BlockSvg.NOTCH_RADIUS)+","+(-Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4+Blockly.BlockSvg.NOTCH_RADIUS)+" a "+Blockly.BlockSvg.NOTCH_RADIUS+","+-Blockly.BlockSvg.NOTCH_RADIUS+" 0 0 1 "+-Blockly.BlockSvg.NOTCH_RADIUS/2+","+-Blockly.BlockSvg.NOTCH_RADIUS;
Blockly.BlockSvg.TOP_LEFT_CORNER_START="m "+Blockly.BlockSvg.CORNER_RADIUS+",0";Blockly.BlockSvg.TOP_LEFT_CORNER="A "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 0,"+Blockly.BlockSvg.CORNER_RADIUS;Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START="m "+Blockly.BlockSvg.HAT_CORNER_RADIUS+",0";Blockly.BlockSvg.HAT_TOP_LEFT_CORNER="A "+Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS+" 0 0,0 0,"+Blockly.BlockSvg.HAT_CORNER_RADIUS;
Blockly.BlockSvg.INNER_TOP_LEFT_CORNER=Blockly.BlockSvg.NOTCH_PATH_UP+" h -"+(Blockly.BlockSvg.NOTCH_HEIGHT-15-Blockly.BlockSvg.CORNER_RADIUS)+" h -0.5 a "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 -"+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS;Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER="a "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS;
Blockly.BlockSvg.prototype.dispose=function(a,b){Blockly.Field.startCache();Blockly.selected==this&&Blockly.terminateDrag_();Blockly.ContextMenu.currentBlock==this&&Blockly.ContextMenu.hide();b&&this.rendered&&(this.unplug(a),this.disposeUiEffect());this.rendered=!1;Blockly.BlockSvg.superClass_.dispose.call(this,a);Blockly.Events.disable();for(var c=this.getIcons(),d=0;d<c.length;d++)c[d].dispose();Blockly.Events.enable();goog.dom.removeNode(this.svgGroup_);this.svgPath_=this.svgGroup_=null;Blockly.Field.stopCache()};
Blockly.BlockSvg.prototype.disposeUiEffect=function(){this.workspace.playAudio("delete");var a=Blockly.getSvgXY_(this.svgGroup_,this.workspace),b=this.svgGroup_.cloneNode(!0);b.translateX_=a.x;b.translateY_=a.y;b.setAttribute("transform","translate("+b.translateX_+","+b.translateY_+")");this.workspace.getParentSvg().appendChild(b);b.bBox_=b.getBBox();Blockly.BlockSvg.disposeUiStep_(b,this.RTL,new Date,this.workspace.scale)};
Blockly.BlockSvg.disposeUiStep_=function(a,b,c,d){var e=(new Date-c)/150;1<e?goog.dom.removeNode(a):(a.setAttribute("transform","translate("+(a.translateX_+(b?-1:1)*a.bBox_.width*d/2*e)+","+(a.translateY_+a.bBox_.height*d*e)+") scale("+(1-e)*d+")"),setTimeout(function(){Blockly.BlockSvg.disposeUiStep_(a,b,c,d)},10))};
Blockly.BlockSvg.prototype.connectionUiEffect=function(){this.workspace.playAudio("click");if(!(1>this.workspace.scale)){var a=Blockly.getSvgXY_(this.svgGroup_,this.workspace);a.x+=8*this.workspace.scale;a.y+=this.height-(2*Blockly.BlockSvg.CORNER_RADIUS+Blockly.BlockSvg.NOTCH_HEIGHT/2)-8*this.workspace.scale;a=Blockly.createSvgElement("circle",{cx:a.x,cy:a.y,r:0,fill:"none",stroke:"#EEE","stroke-width":8},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;1<d?goog.dom.removeNode(a):(a.setAttribute("r",25*d*c),a.style.opacity=.8-d,Blockly.BlockSvg.disconnectUiStop_.pid_=setTimeout(function(){Blockly.BlockSvg.connectionUiStep_(a,b,c)},10))};
Blockly.BlockSvg.prototype.disconnectUiEffect=function(){this.workspace.playAudio("disconnect");if(!(1>this.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<d?a.skew_="":(a.skew_="skewX("+Math.round(Math.sin(d*Math.PI*3)*(1-d)*b)+")",Blockly.BlockSvg.disconnectUiStop_.group=a,Blockly.BlockSvg.disconnectUiStop_.pid=setTimeout(function(){Blockly.BlockSvg.disconnectUiStep_(a,b,c)},10));a.setAttribute("transform",a.translate_+a.skew_)};
Blockly.BlockSvg.disconnectUiStop_=function(){if(Blockly.BlockSvg.disconnectUiStop_.group){clearTimeout(Blockly.BlockSvg.disconnectUiStop_.pid);var a=Blockly.BlockSvg.disconnectUiStop_.group;a.skew_="";a.setAttribute("transform",a.translate_);Blockly.BlockSvg.disconnectUiStop_.group=null}};Blockly.BlockSvg.disconnectUiStop_.pid=0;Blockly.BlockSvg.disconnectUiStop_.group=null;
Blockly.BlockSvg.prototype.updateColour=function(){var a=this.getColour(),b=goog.color.hexToRgb(a);this.isShadow()&&(b=goog.color.lighten(b,.6),a=goog.color.rgbArrayToHex(b));this.svgPath_.setAttribute("fill",a);a=goog.color.darken(b,.1);a=goog.color.rgbArrayToHex(a);this.svgPath_.setAttribute("stroke",a);for(a=0;b=this.inputList[a];a++)for(var c=0,d;d=b.fieldRow[c];c++)d.setText(null)};Blockly.BlockSvg.prototype.updateDisabled=function(){};
Blockly.BlockSvg.prototype.getCommentText=function(){return this.comment?this.comment.getText().replace(/\s+$/,"").replace(/ +\n/g,"\n"):""};Blockly.BlockSvg.prototype.setCommentText=function(a){var b=!1;goog.isString(a)?(this.comment||(this.comment=new Blockly.Comment(this),b=!0),this.comment.setText(a)):this.comment&&(this.comment.dispose(),b=!0);b&&this.rendered&&(this.render(),this.bumpNeighbours_())};
Blockly.BlockSvg.prototype.setWarningText=function(a,b){this.setWarningText.pid_||(this.setWarningText.pid_=Object.create(null));var c=b||"";if(c)this.setWarningText.pid_[c]&&(clearTimeout(this.setWarningText.pid_[c]),delete this.setWarningText.pid_[c]);else for(var d in this.setWarningText.pid_)clearTimeout(this.setWarningText.pid_[d]),delete this.setWarningText.pid_[d];if(2==Blockly.dragMode_){var e=this;this.setWarningText.pid_[c]=setTimeout(function(){e.workspace&&(delete e.setWarningText.pid_[c],
e.setWarningText(a,c))},100)}else{this.isInFlyout&&(a=null);d=!1;if(goog.isString(a))this.warning||(this.warning=new Blockly.Warning(this),d=!0),this.warning.setText(a,c);else if(this.warning&&!c)this.warning.dispose(),d=!0;else if(this.warning){d=this.warning.getText();this.warning.setText("",c);var f=this.warning.getText();f||this.warning.dispose();d=d==f}d&&this.rendered&&(this.render(),this.bumpNeighbours_())}};
Blockly.BlockSvg.prototype.setMutator=function(a){this.mutator&&this.mutator!==a&&this.mutator.dispose();a&&(a.block_=this,this.mutator=a,a.createIcon())};Blockly.BlockSvg.prototype.addSelect=function(){Blockly.addClass_(this.svgGroup_,"blocklySelected");this.svgGroup_.parentNode.appendChild(this.svgGroup_)};Blockly.BlockSvg.prototype.removeSelect=function(){Blockly.removeClass_(this.svgGroup_,"blocklySelected")};Blockly.BlockSvg.prototype.addDragging=function(){Blockly.addClass_(this.svgGroup_,"blocklyDragging")};
Blockly.BlockSvg.prototype.removeDragging=function(){Blockly.removeClass_(this.svgGroup_,"blocklyDragging")};Blockly.BlockSvg.prototype.render=function(a){Blockly.Field.startCache();this.rendered=!0;var b=this.renderCompute_();this.height=b.height;this.width=b.width;this.renderDraw_(b);!1!==a&&((a=this.getParent())?a.render(!0):Blockly.fireUiEvent(window,"resize"));Blockly.Field.stopCache()};
Blockly.BlockSvg.prototype.renderCompute_=function(){for(var a={statement:null,icon:null,width:0,height:0,bayHeight:0,bayWidth:0},b=0,c;c=this.inputList[b];b++){if(c.type==Blockly.NEXT_STATEMENT&&(a.statement=c,a.bayHeight=Blockly.BlockSvg.NOTCH_HEIGHT+16+3*Blockly.BlockSvg.CORNER_RADIUS,a.bayWidth=2*Blockly.BlockSvg.NOTCH_WIDTH+Blockly.BlockSvg.SEP_SPACE_X,c.connection&&c.connection.targetConnection)){var d=c.connection.targetBlock().getHeightWidth();a.bayHeight=Math.max(a.bayHeight,d.height);a.bayWidth=
Math.max(a.bayWidth,d.width)}for(var d=0,e;e=c.fieldRow[d];d++)e instanceof Blockly.FieldImage&&(a.icon=e)}b=a.icon?a.icon.getSize():new goog.math.Size(0,0);a.width=2*Blockly.BlockSvg.SEP_SPACE_X+b.width+a.bayWidth;a.statement&&(a.width+=2*Blockly.BlockSvg.CORNER_RADIUS+8);a.height=Math.max(2*Blockly.BlockSvg.SEP_SPACE_Y+b.height,Blockly.BlockSvg.NOTCH_HEIGHT+16+2*Blockly.BlockSvg.CORNER_RADIUS,a.bayHeight+Blockly.BlockSvg.SEP_SPACE_Y);return a};
Blockly.BlockSvg.prototype.updateColour=function(){var a=this.parentBlock_?this.parentBlock_.getColour():this.getColour(),b=goog.color.hexToRgb(a);this.isShadow()?this.svgPath_.setAttribute("fill","#ffffff"):this.svgPath_.setAttribute("fill",a);a=goog.color.darken(b,.1);a=goog.color.rgbArrayToHex(a);this.svgPath_.setAttribute("stroke",a);for(a=0;b=this.inputList[a];a++)for(var c=0,d;d=b.fieldRow[c];c++)d.setText(null)};
Blockly.BlockSvg.prototype.getHeightWidth=function(){var a=this.height,b=this.width,c=this.getNextBlock();c?(c=c.getHeightWidth(),b+=c.width,a=Math.max(a,c.height)):this.nextConnection||this.outputConnection||(a+=2);return{height:a,width:b}};
Blockly.BlockSvg.prototype.render=function(a){Blockly.Field.startCache();this.rendered=!0;var b=this.renderCompute_();this.height=b.height;this.width=b.width;this.renderDraw_(b);!1!==a&&((a=this.getParent())?a.render(!0):Blockly.fireUiEvent(window,"resize"));Blockly.Field.stopCache()};
Blockly.BlockSvg.prototype.renderCompute_=function(){var a={statement:null,valueInput:null,icon:null,width:0,height:0,bayHeight:0,bayWidth:0,fieldWidth:0,fieldHeight:0,fieldRadius:0,startHat:!1,endHat:!1};this.nextConnection&&!this.previousConnection&&(a.startHat=!0);this.previousConnection&&!this.nextConnection&&(a.endHat=!0);for(var b=0,c;c=this.inputList[b];b++){if(c.type==Blockly.NEXT_STATEMENT&&(a.statement=c,a.bayHeight=Blockly.BlockSvg.NOTCH_HEIGHT+16+3*Blockly.BlockSvg.CORNER_RADIUS,a.bayWidth=
2*Blockly.BlockSvg.NOTCH_WIDTH+Blockly.BlockSvg.MIN_BLOCK_X,c.connection&&c.connection.targetConnection)){var d=c.connection.targetBlock().getHeightWidth();a.bayHeight=Math.max(a.bayHeight,d.height);a.bayWidth=Math.max(a.bayWidth,d.width)}for(var d=0,e;e=c.fieldRow[d];d++)if(e instanceof Blockly.FieldImage&&(a.icon=e),e instanceof Blockly.FieldTextInput){var f=e.textElement_.getBBox();a.fieldWidth=f.width+Blockly.BlockSvg.SEP_SPACE_X;a.fieldHeight=f.height;a.fieldRadius="math_number"===e.sourceBlock_.type?
Blockly.BlockSvg.NUMBER_FIELD_CORNER_RADIUS:Blockly.BlockSvg.TEXT_FIELD_CORNER_RADIUS}}for(b=0;c=this.childBlocks_[b];b++)c.isShadow()&&(a.valueInput=c);b=a.icon?a.icon.getSize():new goog.math.Size(0,0);a.width=2*Blockly.BlockSvg.SEP_SPACE_X+b.width+a.bayWidth;a.statement&&(a.width+=2*Blockly.BlockSvg.CORNER_RADIUS+8);this.outputConnection?(a.height=Blockly.BlockSvg.FIELD_HEIGHT,a.width=Blockly.BlockSvg.FIELD_WIDTH):a.height=Math.max(2*Blockly.BlockSvg.SEP_SPACE_Y+b.height,Blockly.BlockSvg.NOTCH_HEIGHT+
16+2*Blockly.BlockSvg.CORNER_RADIUS,a.bayHeight+Blockly.BlockSvg.SEP_SPACE_Y);return a};
Blockly.BlockSvg.prototype.renderDraw_=function(a){var b=this.getRelativeToSurfaceXY(),c=[];this.renderDrawLeft_(c,b,a);this.renderDrawBottom_(c,b,a);this.renderDrawRight_(c,b,a);this.renderDrawTop_(c,b,a);b=c.join(" ");this.svgPath_.setAttribute("d",b);this.RTL&&this.svgPath_.setAttribute("transform","scale(-1 1)");a.icon&&(b=a.icon.getSvgRoot(),c=a.icon.getSize(),b.setAttribute("transform","translate("+(a.width-c.width-Blockly.BlockSvg.SEP_SPACE_X/2)+","+(a.height-c.height-Blockly.BlockSvg.SEP_SPACE_Y)+
")"))};
Blockly.BlockSvg.prototype.renderDrawLeft_=function(a,b,c){this.previousConnection?(a.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START),a.push(Blockly.BlockSvg.TOP_LEFT_CORNER),a.push("V",c.height-Blockly.BlockSvg.CORNER_RADIUS-8-Blockly.BlockSvg.NOTCH_HEIGHT),a.push(Blockly.BlockSvg.NOTCH_PATH_DOWN),this.previousConnection.moveTo(b.x,b.y+c.height-2*Blockly.BlockSvg.CORNER_RADIUS),a.push("V",c.height-Blockly.BlockSvg.CORNER_RADIUS)):(a.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START),a.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER),a.push("V",
c.height-Blockly.BlockSvg.HAT_CORNER_RADIUS))};
Blockly.BlockSvg.prototype.renderDrawBottom_=function(a,b,c){this.previousConnection?a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS):a.push("a",Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS);c.statement&&(a.push("h",8),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+
" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS),a.push("v",-8),a.push(Blockly.BlockSvg.NOTCH_PATH_UP),a.push("v",-c.bayHeight+3*Blockly.BlockSvg.CORNER_RADIUS+Blockly.BlockSvg.NOTCH_HEIGHT+8),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,1 "+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS),a.push("h",c.bayWidth-2*Blockly.BlockSvg.CORNER_RADIUS),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+
" 0 0,1 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS),a.push("v",c.bayHeight-3*Blockly.BlockSvg.CORNER_RADIUS-Blockly.BlockSvg.NOTCH_HEIGHT-8),a.push(Blockly.BlockSvg.NOTCH_PATH_DOWN),a.push("v",8),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS),c.statement.connection.moveTo(b.x+2*Blockly.BlockSvg.CORNER_RADIUS+8,b.y+c.height-2*Blockly.BlockSvg.CORNER_RADIUS),c.statement.connection.targetConnection&&
c.statement.connection.tighten_());this.nextConnection?a.push("H",c.width-Blockly.BlockSvg.CORNER_RADIUS):a.push("H",c.width-Blockly.BlockSvg.HAT_CORNER_RADIUS)};
Blockly.BlockSvg.prototype.renderDrawRight_=function(a,b,c){this.nextConnection?a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS):a.push("a",Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.HAT_CORNER_RADIUS+",-"+Blockly.BlockSvg.HAT_CORNER_RADIUS);a.push("v",-8);this.nextConnection?(a.push(Blockly.BlockSvg.NOTCH_PATH_UP),this.nextConnection.moveTo(b.x+
c.width,b.y+c.height-2*Blockly.BlockSvg.CORNER_RADIUS),this.nextConnection.targetConnection&&this.nextConnection.tighten_(),this.height+=4,a.push("V",Blockly.BlockSvg.CORNER_RADIUS)):a.push("V",Blockly.BlockSvg.HAT_CORNER_RADIUS)};
Blockly.BlockSvg.prototype.renderDrawTop_=function(a,b,c){this.nextConnection?a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 -"+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS):a.push("a",Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS+" 0 0,0 -"+Blockly.BlockSvg.HAT_CORNER_RADIUS+",-"+Blockly.BlockSvg.HAT_CORNER_RADIUS);a.push("z")};
")"));a.valueInput&&(b=a.valueInput.getSvgRoot(),b.getBBox(),b.setAttribute("transform","translate("+(Blockly.BlockSvg.NOTCH_WIDTH+(a.bayWidth?8+2*Blockly.BlockSvg.NOTCH_WIDTH:0)+a.bayWidth)+","+(a.height-8)+")"))};
Blockly.BlockSvg.prototype.renderDrawLeft_=function(a,b,c){c.startHat?(a.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START),a.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER),a.push("V",c.height-Blockly.BlockSvg.HAT_CORNER_RADIUS)):this.previousConnection?(a.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START),a.push(Blockly.BlockSvg.TOP_LEFT_CORNER),a.push("V",c.height-Blockly.BlockSvg.CORNER_RADIUS-8-Blockly.BlockSvg.NOTCH_HEIGHT),a.push(Blockly.BlockSvg.NOTCH_PATH_DOWN),this.previousConnection.moveTo(b.x,b.y+
c.height-2*Blockly.BlockSvg.CORNER_RADIUS),a.push("V",c.height-Blockly.BlockSvg.CORNER_RADIUS)):(a.push("m",c.fieldRadius+",0"),a.push("A",c.fieldRadius+","+c.fieldRadius,"0","0,0","0,"+c.fieldRadius),a.push("V",c.height-c.fieldRadius))};
Blockly.BlockSvg.prototype.renderDrawBottom_=function(a,b,c){c.startHat?a.push("a",Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS):this.previousConnection?a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS):a.push("a",c.fieldRadius+","+c.fieldRadius,"0","0,0",c.fieldRadius+","+c.fieldRadius);
c.statement&&(a.push("h",8),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS),a.push("v",-8),a.push(Blockly.BlockSvg.NOTCH_PATH_UP),a.push("v",-c.bayHeight+3*Blockly.BlockSvg.CORNER_RADIUS+Blockly.BlockSvg.NOTCH_HEIGHT+8),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,1 "+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS),a.push("h",c.bayWidth-
2*Blockly.BlockSvg.CORNER_RADIUS),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,1 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS),a.push("v",c.bayHeight-3*Blockly.BlockSvg.CORNER_RADIUS-Blockly.BlockSvg.NOTCH_HEIGHT-8),a.push(Blockly.BlockSvg.NOTCH_PATH_DOWN),a.push("v",8),a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS),c.statement.connection.moveTo(b.x+
2*Blockly.BlockSvg.CORNER_RADIUS+8,b.y+c.height-2*Blockly.BlockSvg.CORNER_RADIUS),c.statement.connection.targetConnection&&c.statement.connection.tighten_());c.endHat?a.push("H",c.width-Blockly.BlockSvg.HAT_CORNER_RADIUS):this.nextConnection?a.push("H",c.width-Blockly.BlockSvg.CORNER_RADIUS):a.push("H",c.width-c.fieldRadius)};
Blockly.BlockSvg.prototype.renderDrawRight_=function(a,b,c){c.endHat?(a.push("a",Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.HAT_CORNER_RADIUS+",-"+Blockly.BlockSvg.HAT_CORNER_RADIUS),a.push("v",-8)):this.nextConnection?(a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS),a.push("v",-8)):(a.push("a",c.fieldRadius+","+c.fieldRadius,"0","0,0",
c.fieldRadius+","+-1*c.fieldRadius),a.push("v",-1*(c.height-2*c.fieldRadius)));c.endHat?a.push("V",Blockly.BlockSvg.HAT_CORNER_RADIUS):this.nextConnection&&(a.push(Blockly.BlockSvg.NOTCH_PATH_UP),this.nextConnection.moveTo(b.x+c.width,b.y+c.height-2*Blockly.BlockSvg.CORNER_RADIUS),this.nextConnection.targetConnection&&this.nextConnection.tighten_(),this.height+=4,a.push("V",Blockly.BlockSvg.CORNER_RADIUS))};
Blockly.BlockSvg.prototype.renderDrawTop_=function(a,b,c){c.endHat?a.push("a",Blockly.BlockSvg.HAT_CORNER_RADIUS+","+Blockly.BlockSvg.HAT_CORNER_RADIUS+" 0 0,0 -"+Blockly.BlockSvg.HAT_CORNER_RADIUS+",-"+Blockly.BlockSvg.HAT_CORNER_RADIUS):this.nextConnection?a.push("a",Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 -"+Blockly.BlockSvg.CORNER_RADIUS+",-"+Blockly.BlockSvg.CORNER_RADIUS):a.push("a",c.fieldRadius+","+c.fieldRadius,"0","0,0","-"+c.fieldRadius+",-"+c.fieldRadius);
a.push("z")};
// Copyright 2016 Google Inc. Apache License 2.0
Blockly.Events={};Blockly.Events.group="";Blockly.Events.disabled_=0;Blockly.Events.CREATE="create";Blockly.Events.DELETE="delete";Blockly.Events.CHANGE="change";Blockly.Events.MOVE="move";Blockly.Events.FIRE_QUEUE_=[];Blockly.Events.fire=function(a){Blockly.Events.isEnabled()&&(0==Blockly.Events.FIRE_QUEUE_.length&&setTimeout(Blockly.Events.fireNow_,0),Blockly.Events.FIRE_QUEUE_.push(a))};
Blockly.Events.fireNow_=function(){for(var a=Blockly.Events.filter_(Blockly.Events.FIRE_QUEUE_),b=Blockly.Events.FIRE_QUEUE_.length=0,c;c=a[b];b++){var d=Blockly.Workspace.getById(c.workspaceId);d&&d.fireChangeListener(c)}};
@ -1157,7 +1162,8 @@ this.spellcheck_);var e=Blockly.FieldTextInput.FONTSIZE*b.scale+"pt";c.style.fon
d.onWorkspaceChangeWrapper_=this.resizeEditor_.bind(this);b.addChangeListener(d.onWorkspaceChangeWrapper_)}};Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_=function(a){var b=Blockly.FieldTextInput.htmlInput_;13==a.keyCode?Blockly.WidgetDiv.hide():27==a.keyCode?(b.value=b.defaultValue,Blockly.WidgetDiv.hide()):9==a.keyCode&&(Blockly.WidgetDiv.hide(),this.sourceBlock_.tab(this,!a.shiftKey),a.preventDefault())};
Blockly.FieldTextInput.prototype.onHtmlInputChange_=function(a){a=Blockly.FieldTextInput.htmlInput_;var b=a.value;b!==a.oldValue_?(a.oldValue_=b,this.setValue(b),this.validate_()):goog.userAgent.WEBKIT&&this.sourceBlock_.render();this.resizeEditor_()};
Blockly.FieldTextInput.prototype.validate_=function(){var a=!0;goog.asserts.assertObject(Blockly.FieldTextInput.htmlInput_);var b=Blockly.FieldTextInput.htmlInput_;this.sourceBlock_&&this.validator_&&(a=this.validator_(b.value));null===a?Blockly.addClass_(b,"blocklyInvalidInput"):Blockly.removeClass_(b,"blocklyInvalidInput")};
Blockly.FieldTextInput.prototype.resizeEditor_=function(){var a=Blockly.WidgetDiv.DIV,b=this.fieldGroup_.getBBox();a.style.width=b.width*this.sourceBlock_.workspace.scale+"px";a.style.height=b.height*this.sourceBlock_.workspace.scale+"px";b=this.getAbsoluteXY_();if(this.sourceBlock_.RTL){var c=this.getScaledBBox_();b.x+=c.width;b.x-=a.offsetWidth}b.y+=1;goog.userAgent.GECKO&&Blockly.WidgetDiv.DIV.style.top&&(--b.x,--b.y);goog.userAgent.WEBKIT&&(b.y-=3);a.style.left=b.x+"px";a.style.top=b.y+"px"};
Blockly.FieldTextInput.prototype.resizeEditor_=function(){var a=Blockly.WidgetDiv.DIV,b=this.fieldGroup_.getBBox(),c=b.height*this.sourceBlock_.workspace.scale;a.style.width=Math.max(b.width,Blockly.BlockSvg.FIELD_WIDTH-Blockly.BlockSvg.SEP_SPACE_X)*this.sourceBlock_.workspace.scale+"px";a.style.height=c+"px";b=this.getAbsoluteXY_();b.x+=Blockly.BlockSvg.SEP_SPACE_X*this.sourceBlock_.workspace.scale;b.y+=Blockly.BlockSvg.FIELD_HEIGHT*this.sourceBlock_.workspace.scale/2-c/2+3;this.sourceBlock_.RTL&&
(c=this.getScaledBBox_(),b.x+=c.width,b.x-=a.offsetWidth);b.y+=1;goog.userAgent.GECKO&&Blockly.WidgetDiv.DIV.style.top&&(--b.x,--b.y);goog.userAgent.WEBKIT&&(b.y-=3);a.style.left=b.x+"px";a.style.top=b.y+"px"};
Blockly.FieldTextInput.prototype.widgetDispose_=function(){var a=this;return function(){var b=Blockly.FieldTextInput.htmlInput_,c=b.value;if(a.sourceBlock_&&a.validator_){var d=a.validator_(c);null===d?c=b.defaultValue:void 0!==d&&(c=d)}a.setValue(c);a.sourceBlock_.rendered&&a.sourceBlock_.render();Blockly.unbindEvent_(b.onKeyDownWrapper_);Blockly.unbindEvent_(b.onKeyUpWrapper_);Blockly.unbindEvent_(b.onKeyPressWrapper_);a.sourceBlock_.workspace.removeChangeListener(b.onWorkspaceChangeWrapper_);Blockly.FieldTextInput.htmlInput_=
null;b=Blockly.WidgetDiv.DIV.style;b.width="auto";b.height="auto";b.fontSize=""}};Blockly.FieldTextInput.numberValidator=function(a){if(null===a)return null;a=String(a);a=a.replace(/O/ig,"0");a=a.replace(/,/g,"");a=parseFloat(a||0);return isNaN(a)?null:String(a)};Blockly.FieldTextInput.nonnegativeIntegerValidator=function(a){(a=Blockly.FieldTextInput.numberValidator(a))&&(a=String(Math.max(0,Math.floor(a))));return a};Blockly.FieldAngle=function(a,b){this.symbol_=Blockly.createSvgElement("tspan",{},null);this.symbol_.appendChild(document.createTextNode("\u00b0"));Blockly.FieldAngle.superClass_.constructor.call(this,a,b)};goog.inherits(Blockly.FieldAngle,Blockly.FieldTextInput);
Blockly.FieldAngle.prototype.setValidator=function(a){Blockly.FieldAngle.superClass_.setValidator.call(this,a?function(b){var c=a.call(this,b);if(null===c)var d=c;else void 0===c&&(c=b),d=Blockly.FieldAngle.angleValidator.call(this,c),void 0===d&&(d=c);return d===b?void 0:d}:Blockly.FieldAngle.angleValidator)};Blockly.FieldAngle.ROUND=15;Blockly.FieldAngle.HALF=50;Blockly.FieldAngle.CLOCKWISE=!1;Blockly.FieldAngle.OFFSET=0;Blockly.FieldAngle.WRAP=360;
@ -1223,9 +1229,12 @@ this.workspace_.scale,viewTop:-this.workspace_.scrollY,viewLeft:-this.workspace_
Blockly.Flyout.prototype.setMetrics_=function(a){var b=this.getMetrics_();b&&(!this.horizontalLayout_&&goog.isNumber(a.y)?this.workspace_.scrollY=-b.contentHeight*a.y-b.contentTop:this.horizontalLayout_&&goog.isNumber(a.x)&&(this.workspace_.scrollX=this.RTL?-b.contentWidth*a.x+b.contentLeft:-b.contentWidth*a.x-b.contentLeft),this.workspace_.translate(this.horizontalLayout_&&this.RTL?b.absoluteLeft+b.viewWidth-this.workspace_.scrollX:this.workspace_.scrollX+b.absoluteLeft,this.workspace_.scrollY+b.absoluteTop))};
Blockly.Flyout.prototype.setVerticalOffset=function(a){this.verticalOffset_=a};
Blockly.Flyout.prototype.position=function(){if(this.isVisible()){var a=this.targetWorkspace_.getMetrics();if(a){var b=this.horizontalLayout_?a.viewWidth:this.width_,b=b-this.CORNER_RADIUS;this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT&&(b*=-1);this.setBackgroundPath_(b,this.horizontalLayout_?this.height_+this.verticalOffset_:a.viewHeight);b=a.absoluteLeft;this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT&&(b+=a.viewWidth,b-=this.width_);var c=a.absoluteTop;this.toolboxPosition_==Blockly.TOOLBOX_AT_BOTTOM&&
(c+=a.viewHeight,c-=this.height_);this.svgGroup_.setAttribute("transform","translate("+b+","+c+")");this.horizontalLayout_?this.width_=a.viewWidth:this.height_=a.viewHeight;this.scrollbar_&&this.scrollbar_.resize();this.svgGroup_.style.opacity=1}}};
Blockly.Flyout.prototype.setBackgroundPath_=function(a,b){var c=this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT,d=["M "+(c?this.width_:0)+",0"];d.push("h",a);d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?-this.CORNER_RADIUS:this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("v",Math.max(0,b-2*this.CORNER_RADIUS));d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?this.CORNER_RADIUS:-this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("h",-a);d.push("z");this.svgBackground_.setAttribute("d",
d.join(" "))};Blockly.Flyout.prototype.scrollToStart=function(){this.scrollbar_.set(this.horizontalLayout_&&this.RTL?1E9:0)};Blockly.Flyout.prototype.wheel_=function(a){if(!this.horizontalLayout_){var b=a.deltaY;if(b){goog.userAgent.GECKO&&(b*=10);var c=this.getMetrics_(),b=c.viewTop+b,b=Math.min(b,c.contentHeight-c.viewHeight),b=Math.max(b,0);this.scrollbar_.set(b);a.preventDefault();a.stopPropagation()}}};Blockly.Flyout.prototype.isVisible=function(){return this.svgGroup_&&"block"==this.svgGroup_.style.display};
(c+=a.viewHeight,c-=this.height_);this.svgGroup_.setAttribute("transform","translate("+b+","+c+")");this.horizontalLayout_?this.width_=a.viewWidth:this.height_=a.viewHeight;this.scrollbar_&&this.scrollbar_.resize();this.svgGroup_.style.opacity=1}}};Blockly.Flyout.prototype.setBackgroundPath_=function(a,b){this.horizontalLayout_?this.setBackgroundPathHorizontal_(a,b):this.setBackgroundPathVertical_(a,b)};
Blockly.Flyout.prototype.setBackgroundPathVertical_=function(a,b){var c=this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT,d=["M "+(c?this.width_:0)+",0"];d.push("h",a);d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?-this.CORNER_RADIUS:this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("v",Math.max(0,b-2*this.CORNER_RADIUS));d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?this.CORNER_RADIUS:-this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("h",-a);d.push("z");this.svgBackground_.setAttribute("d",
d.join(" "))};
Blockly.Flyout.prototype.setBackgroundPathHorizontal_=function(a,b){var c=this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP,d=["M 0,"+(c?0:this.CORNER_RADIUS)];c?(d.push("h",a+this.CORNER_RADIUS),d.push("v",b),d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,-this.CORNER_RADIUS,this.CORNER_RADIUS),d.push("h",-1*(a-this.CORNER_RADIUS)),d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,-this.CORNER_RADIUS,-this.CORNER_RADIUS)):(d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,this.CORNER_RADIUS,
-this.CORNER_RADIUS),d.push("h",a-this.CORNER_RADIUS),d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,this.CORNER_RADIUS,this.CORNER_RADIUS),d.push("v",b-this.CORNER_RADIUS),d.push("h",-a-this.CORNER_RADIUS));d.push("z");this.svgBackground_.setAttribute("d",d.join(" "))};Blockly.Flyout.prototype.scrollToStart=function(){this.scrollbar_.set(this.horizontalLayout_&&this.RTL?1E9:0)};
Blockly.Flyout.prototype.wheel_=function(a){if(!this.horizontalLayout_){var b=a.deltaY;if(b){goog.userAgent.GECKO&&(b*=10);var c=this.getMetrics_(),b=c.viewTop+b,b=Math.min(b,c.contentHeight-c.viewHeight),b=Math.max(b,0);this.scrollbar_.set(b);a.preventDefault();a.stopPropagation()}}};Blockly.Flyout.prototype.isVisible=function(){return this.svgGroup_&&"block"==this.svgGroup_.style.display};
Blockly.Flyout.prototype.hide=function(){if(this.isVisible()){this.svgGroup_.style.display="none";for(var a=0,b;b=this.listeners_[a];a++)Blockly.unbindEvent_(b);this.listeners_.length=0;this.reflowWrapper_&&(this.workspace_.removeChangeListener(this.reflowWrapper_),this.reflowWrapper_=null)}};
Blockly.Flyout.prototype.show=function(a){this.hide();for(var b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)d.workspace==this.workspace_&&d.dispose(!1,!1);for(var c=0,e;e=this.buttons_[c];c++)goog.dom.removeNode(e);this.buttons_.length=0;a==Blockly.Variables.NAME_TYPE?a=Blockly.Variables.flyoutCategory(this.workspace_.targetWorkspace):a==Blockly.Procedures.NAME_TYPE&&(a=Blockly.Procedures.flyoutCategory(this.workspace_.targetWorkspace));for(var f=this.CORNER_RADIUS,b=[],g=[],c=this.permanentlyDisabled_.length=
0;e=a[c];c++)e.tagName&&"BLOCK"==e.tagName.toUpperCase()&&(d=Blockly.Xml.domToBlock(this.workspace_,e),d.disabled&&this.permanentlyDisabled_.push(d),b.push(d),d=parseInt(e.getAttribute("gap"),10),g.push(isNaN(d)?3*f:d));this.svgGroup_.style.opacity=0;this.svgGroup_.style.display="block";a=f/this.workspace_.scale+Blockly.BlockSvg.TAB_WIDTH;for(c=0;d=b[c];c++){e=d.getDescendants();for(var h=0,k;k=e[h];h++)k.isInFlyout=!0;d.render();h=d.getSvgRoot();e=d.getHeightWidth();d.moveBy(this.horizontalLayout_&&
@ -1272,9 +1281,9 @@ Blockly.Css.CONTENT=[".blocklySvg {","background-color: #fff;","outline: none;",
"fill: #aaa;","}",".blocklyResizeSW {","cursor: sw-resize;","fill: #aaa;","}",".blocklyResizeLine {","stroke: #888;","stroke-width: 1;","}",".blocklyHighlightedConnectionPath {","fill: none;","stroke: #fc3;","stroke-width: 4px;","}",".blocklyPath {","stroke-width: 1.5px;","}",".blocklySelected>.blocklyPath {","}",".blocklyDragging>.blocklyPath {","fill-opacity: .8;","stroke-opacity: .8;","}",".blocklyDisabled>.blocklyPath {","fill-opacity: .5;","stroke-opacity: .5;","}",".blocklyText {","cursor: default;",
"fill: #fff;","font-family: sans-serif;","font-size: 11pt;","}",".blocklyNonEditableText>text {","pointer-events: none;","}",".blocklyNonEditableText>rect,",".blocklyEditableText>rect {","fill: #fff;","fill-opacity: .6;","}",".blocklyNonEditableText>text,",".blocklyEditableText>text {","fill: #000;","}",".blocklyEditableText:hover>rect {","stroke: #fff;","stroke-width: 2;","}",".blocklyBubbleText {","fill: #000;","}",".blocklySvg text {","user-select: none;","-moz-user-select: none;","-webkit-user-select: none;",
"cursor: inherit;","}",".blocklyHidden {","display: none;","}",".blocklyFieldDropdown:not(.blocklyHidden) {","display: block;","}",".blocklyIconGroup {","cursor: default;","}",".blocklyIconGroup:not(:hover),",".blocklyIconGroupReadonly {","opacity: .6;","}",".blocklyIconShape {","fill: #00f;","stroke: #fff;","stroke-width: 1px;","}",".blocklyIconSymbol {","fill: #fff;","}",".blocklyMinimalBody {","margin: 0;","padding: 0;","}",".blocklyCommentTextarea {","background-color: #ffc;","border: 0;","margin: 0;",
"padding: 2px;","resize: none;","}",".blocklyHtmlInput {","border: none;","border-radius: 4px;","font-family: sans-serif;","height: 100%;","margin: 0;","outline: none;","padding: 0 1px;","width: 100%","}",".blocklyMainBackground {","stroke-width: 1;","stroke: #c6c6c6;","}",".blocklyMutatorBackground {","fill: #fff;","stroke: #ddd;","stroke-width: 1;","}",".blocklyFlyoutBackground {","fill: #ddd;","fill-opacity: .8;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarKnob {","fill: #ccc;",
"}",".blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyScrollbarKnob:hover {","fill: #bbb;","}",".blocklyZoom>image {","opacity: .4;","}",".blocklyZoom>image:hover {","opacity: .6;","}",".blocklyZoom>image:active {","opacity: .8;","}",".blocklyFlyout .blocklyScrollbarKnob {","fill: #bbb;","}",".blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyFlyout .blocklyScrollbarKnob:hover {","fill: #aaa;","}",".blocklyInvalidInput {","background: #faa;","}",".blocklyAngleCircle {",
"stroke: #444;","stroke-width: 1;","fill: #ddd;","fill-opacity: .8;","}",".blocklyAngleMarks {","stroke: #444;","stroke-width: 1;","}",".blocklyAngleGauge {","fill: #f88;","fill-opacity: .8;","}",".blocklyAngleLine {","stroke: #f00;","stroke-width: 2;","stroke-linecap: round;","}",".blocklyContextMenu {","border-radius: 4px;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {",
"padding: 2px;","resize: none;","}",".blocklyHtmlInput {","border: none;","border-radius: 4px;","font-family: sans-serif;","height: 100%;","margin: 0;","outline: none;","padding: 0 1px;","width: 100%;","text-align: center","}",".blocklyMainBackground {","stroke-width: 1;","stroke: #c6c6c6;","}",".blocklyMutatorBackground {","fill: #fff;","stroke: #ddd;","stroke-width: 1;","}",".blocklyFlyoutBackground {","fill: #ddd;","fill-opacity: .8;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarKnob {",
"fill: #ccc;","}",".blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyScrollbarKnob:hover {","fill: #bbb;","}",".blocklyZoom>image {","opacity: .4;","}",".blocklyZoom>image:hover {","opacity: .6;","}",".blocklyZoom>image:active {","opacity: .8;","}",".blocklyFlyout .blocklyScrollbarKnob {","fill: #bbb;","}",".blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyFlyout .blocklyScrollbarKnob:hover {","fill: #aaa;","}",".blocklyInvalidInput {","background: #faa;",
"}",".blocklyAngleCircle {","stroke: #444;","stroke-width: 1;","fill: #ddd;","fill-opacity: .8;","}",".blocklyAngleMarks {","stroke: #444;","stroke-width: 1;","}",".blocklyAngleGauge {","fill: #f88;","fill-opacity: .8;","}",".blocklyAngleLine {","stroke: #f00;","stroke-width: 2;","stroke-linecap: round;","}",".blocklyContextMenu {","border-radius: 4px;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {",
"background: url(<<<PATH>>>/sprites.png) no-repeat -48px -16px !important;","}",".blocklyToolboxDiv {","background-color: #ddd;","overflow-x: visible;","overflow-y: auto;","position: absolute;","}",".blocklyTreeRoot {","padding: 4px 0;","}",".blocklyTreeRoot:focus {","outline: none;","}",".blocklyTreeRow {","height: 22px;","line-height: 22px;","margin-bottom: 3px;","padding-right: 8px;","white-space: nowrap;","}",".blocklyHorizontalTree {","float: left;","margin: 1px 5px 8px 0px;","}",".blocklyHorizontalTreeRtl {",
"float: right;","margin: 1px 0px 8px 5px;","}",'.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',"margin-left: 8px;","}",".blocklyTreeRow:not(.blocklyTreeSelected):hover {","background-color: #e4e4e4;","}",".blocklyTreeSeparator {","border-bottom: solid #e5e5e5 1px;","height: 0px;","margin: 5px 0;","}",".blocklyTreeSeparatorHorizontal {","border-right: solid #e5e5e5 1px;","width: 0px;","padding: 5px 0;","margin: 0 5px;","}",".blocklyTreeIcon {","background-image: url(<<<PATH>>>/sprites.png);","height: 16px;",
"vertical-align: middle;","width: 16px;","}",".blocklyTreeIconClosedLtr {","background-position: -32px -1px;","}",".blocklyTreeIconClosedRtl {","background-position: 0px -1px;","}",".blocklyTreeIconOpen {","background-position: -16px -1px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedLtr {","background-position: -32px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedRtl {","background-position: 0px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconOpen {","background-position: -16px -17px;",

View file

@ -913,9 +913,10 @@ Blockly.Connection.prototype.hideAll=function(){this.setHidden(!0);if(this.targe
Blockly.Connection.prototype.unhideAll=function(){this.setHidden(!1);var a=[];if(this.type!=Blockly.INPUT_VALUE&&this.type!=Blockly.NEXT_STATEMENT)return a;var b=this.targetBlock();if(b){var c;b.isCollapsed()?(c=[],b.outputConnection&&c.push(b.outputConnection),b.nextConnection&&c.push(b.nextConnection),b.previousConnection&&c.push(b.previousConnection)):c=b.getConnections_(!0);for(var d=0;d<c.length;d++)a.push.apply(a,c[d].unhideAll());0==a.length&&(a[0]=b)}return a};Blockly.ConnectionDB=function(){};
Blockly.ConnectionDB.prototype=[];Blockly.ConnectionDB.constructor=Blockly.ConnectionDB;Blockly.ConnectionDB.prototype.addConnection_=function(a){if(a.inDB_)throw"Connection already in database.";if(!a.sourceBlock_.isInFlyout){for(var b=0,c=this.length;b<c;){var d=Math.floor((b+c)/2);if(this[d].y_<a.y_)b=d+1;else if(this[d].y_>a.y_)c=d;else{b=d;break}}this.splice(b,0,a);a.inDB_=!0}};
Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw"Connection not in database.";a.inDB_=!1;for(var b=0,c=this.length-2,d=c;b<d;)this[d].y_<a.y_?b=d:c=d,d=Math.floor((b+c)/2);for(c=b=d;0<=b&&this[b].y_==a.y_;){if(this[b]==a){this.splice(b,1);return}b--}do{if(this[c]==a){this.splice(c,1);return}c++}while(c<this.length&&this[c].y_==a.y_);throw"Unable to find connection in connectionDB.";};
Blockly.ConnectionDB.init=function(a){var b=[];b[Blockly.INPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.OUTPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.NEXT_STATEMENT]=new Blockly.ConnectionDB;b[Blockly.PREVIOUS_STATEMENT]=new Blockly.ConnectionDB;a.connectionDBList=b};Blockly.Field=function(a,b){this.size_=new goog.math.Size(0,25);this.setValue(a);this.setValidator(b)};Blockly.Field.cacheWidths_=null;Blockly.Field.cacheReference_=0;Blockly.Field.prototype.name=void 0;Blockly.Field.prototype.maxDisplayLength=50;Blockly.Field.prototype.text_="";Blockly.Field.prototype.sourceBlock_=null;Blockly.Field.prototype.visible_=!0;Blockly.Field.prototype.validator_=null;Blockly.Field.NBSP="\u00a0";Blockly.Field.prototype.EDITABLE=!0;
Blockly.Field.prototype.init=function(a){this.sourceBlock_||(this.sourceBlock_=a,this.fieldGroup_=Blockly.createSvgElement("g",{},null),this.visible_||(this.fieldGroup_.style.display="none"),this.borderRect_=Blockly.createSvgElement("rect",{rx:4,ry:4,x:-Blockly.BlockSvg.SEP_SPACE_X/2,y:0,height:16},this.fieldGroup_,this.sourceBlock_.workspace),this.textElement_=Blockly.createSvgElement("text",{"class":"blocklyText",y:this.size_.height-12.5},this.fieldGroup_),this.updateEditable(),a.getSvgRoot().appendChild(this.fieldGroup_),
this.mouseUpWrapper_=Blockly.bindEvent_(this.fieldGroup_,"mouseup",this,this.onMouseUp_),this.updateTextNode_(),Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,"",this.getValue())))};
Blockly.ConnectionDB.init=function(a){var b=[];b[Blockly.INPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.OUTPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.NEXT_STATEMENT]=new Blockly.ConnectionDB;b[Blockly.PREVIOUS_STATEMENT]=new Blockly.ConnectionDB;a.connectionDBList=b};Blockly.Field=function(a,b){this.size_=new goog.math.Size(Blockly.BlockSvg.FIELD_WIDTH,Blockly.BlockSvg.FIELD_HEIGHT);this.setValue(a);this.setValidator(b)};Blockly.Field.cacheWidths_=null;Blockly.Field.cacheReference_=0;Blockly.Field.prototype.name=void 0;Blockly.Field.prototype.maxDisplayLength=5;Blockly.Field.prototype.text_="";Blockly.Field.prototype.sourceBlock_=null;Blockly.Field.prototype.visible_=!0;Blockly.Field.prototype.validator_=null;Blockly.Field.NBSP="\u00a0";
Blockly.Field.prototype.EDITABLE=!0;
Blockly.Field.prototype.init=function(a){this.sourceBlock_||(this.sourceBlock_=a,this.fieldGroup_=Blockly.createSvgElement("g",{},null),this.visible_||(this.fieldGroup_.style.display="none"),this.borderRect_=Blockly.createSvgElement("rect",{rx:4,ry:4,x:-Blockly.BlockSvg.SEP_SPACE_X/2,y:0,height:Blockly.BlockSvg.FIELD_HEIGHT},this.fieldGroup_,this.sourceBlock_.workspace),this.textElement_=Blockly.createSvgElement("text",{"class":"blocklyText",y:this.size_.height/2+6.25,x:Blockly.BlockSvg.FIELD_WIDTH/
2,width:Blockly.BlockSvg.FIELD_WIDTH-Blockly.BlockSvg.SEP_SPACE_X,"text-anchor":"middle"},this.fieldGroup_),this.updateEditable(),a.getSvgRoot().appendChild(this.fieldGroup_),this.mouseUpWrapper_=Blockly.bindEvent_(this.fieldGroup_,"mouseup",this,this.onMouseUp_),this.updateTextNode_(),Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,"",this.getValue())))};
Blockly.Field.prototype.dispose=function(){this.mouseUpWrapper_&&(Blockly.unbindEvent_(this.mouseUpWrapper_),this.mouseUpWrapper_=null);this.sourceBlock_=null;goog.dom.removeNode(this.fieldGroup_);this.validator_=this.borderRect_=this.textElement_=this.fieldGroup_=null};
Blockly.Field.prototype.updateEditable=function(){this.EDITABLE&&this.sourceBlock_&&(this.sourceBlock_.isEditable()?(Blockly.addClass_(this.fieldGroup_,"blocklyEditableText"),Blockly.removeClass_(this.fieldGroup_,"blocklyNoNEditableText"),this.fieldGroup_.style.cursor=this.CURSOR):(Blockly.addClass_(this.fieldGroup_,"blocklyNonEditableText"),Blockly.removeClass_(this.fieldGroup_,"blocklyEditableText"),this.fieldGroup_.style.cursor=""))};Blockly.Field.prototype.isVisible=function(){return this.visible_};
Blockly.Field.prototype.setVisible=function(a){if(this.visible_!=a){this.visible_=a;var b=this.getSvgRoot();b&&(b.style.display=a?"block":"none",this.render_())}};Blockly.Field.prototype.setValidator=function(a){this.validator_=a};Blockly.Field.prototype.getSvgRoot=function(){return this.fieldGroup_};
@ -1094,7 +1095,6 @@ c.oldCoordinate=a.dragStartXY_;c.recordNew();Blockly.Events.fire(c);a.moveConnec
Blockly.BlockSvg.prototype.setParent=function(a){var b=this.getSvgRoot();if(this.parentBlock_&&b){var c=this.getRelativeToSurfaceXY();this.workspace.getCanvas().appendChild(b);b.setAttribute("transform","translate("+c.x+","+c.y+")")}Blockly.Field.startCache();Blockly.BlockSvg.superClass_.setParent.call(this,a);Blockly.Field.stopCache();a&&(c=this.getRelativeToSurfaceXY(),a.getSvgRoot().appendChild(b),a=this.getRelativeToSurfaceXY(),this.moveConnections_(a.x-c.x,a.y-c.y))};
Blockly.BlockSvg.prototype.getRelativeToSurfaceXY=function(){var a=0,b=0,c=this.getSvgRoot();if(c){do var d=Blockly.getRelativeXY_(c),a=a+d.x,b=b+d.y,c=c.parentNode;while(c&&c!=this.workspace.getCanvas())}return new goog.math.Coordinate(a,b)};Blockly.BlockSvg.prototype.moveBy=function(a,b){var c=new Blockly.Events.Move(this),d=this.getRelativeToSurfaceXY();this.getSvgRoot().setAttribute("transform","translate("+(d.x+a)+","+(d.y+b)+")");this.moveConnections_(a,b);c.recordNew();Blockly.Events.fire(c)};
Blockly.BlockSvg.prototype.snapToGrid=function(){if(this.workspace&&0==Blockly.dragMode_&&!this.getParent()&&!this.isInFlyout&&this.workspace.options.gridOptions&&this.workspace.options.gridOptions.snap){var a=this.workspace.options.gridOptions.spacing,b=a/2,c=this.getRelativeToSurfaceXY(),d=Math.round((c.x-b)/a)*a+b-c.x,a=Math.round((c.y-b)/a)*a+b-c.y,d=Math.round(d),a=Math.round(a);0==d&&0==a||this.moveBy(d,a)}};
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.tab=function(a,b){for(var c=[],d=0,e;e=this.inputList[d];d++){for(var f=0,g;g=e.fieldRow[f];f++)g instanceof Blockly.FieldTextInput&&c.push(g);e.connection&&(e=e.connection.targetBlock())&&c.push(e)}d=c.indexOf(a);-1==d&&(d=b?-1:c.length);(c=c[b?d+1:d-1])?c instanceof Blockly.Field?c.showEditor_():c.tab(null,b):(c=this.getParent())&&c.tab(this,b)};
Blockly.BlockSvg.prototype.onMouseDown_=function(a){if(!this.isInFlyout)if(this.workspace.markFocused(),Blockly.svgResize(this.workspace),Blockly.terminateDrag_(),this.select(),Blockly.hideChaff(),this.workspace.recordDeleteAreas(),Blockly.isRightButton(a))this.showContextMenu_(a);else if(this.isMovable()){Blockly.Events.group=Blockly.genUid();Blockly.removeAllRanges();Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);this.dragStartXY_=this.getRelativeToSurfaceXY();this.workspace.startDrag(a,this.dragStartXY_.x,
this.dragStartXY_.y);Blockly.dragMode_=1;Blockly.BlockSvg.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,this.onMouseUp_);Blockly.BlockSvg.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.onMouseMove_);this.draggedBubbles_=[];for(var b=this.getDescendants(),c=0,d;d=b[c];c++){d=d.getIcons();for(var e=0;e<d.length;e++){var f=d[e].getIconLocation();f.bubble=d[e];this.draggedBubbles_.push(f)}}}else return;a.stopPropagation()};
@ -1108,23 +1108,23 @@ Blockly.BlockSvg.prototype.onMouseMove_=function(a){if(!("mousemove"==a.type&&1>
b.x-this.dragStartXY_.x,b=b.y-this.dragStartXY_.y;d.translate_="translate("+c.x+","+c.y+")";d.setAttribute("transform",d.translate_+d.skew_);for(c=0;c<this.draggedBubbles_.length;c++)d=this.draggedBubbles_[c],d.bubble.setIconLocation(d.x+e,d.y+b);for(var d=this.getConnections_(!1),f=null,g=null,h=Blockly.SNAP_RADIUS,c=0;c<d.length;c++){var k=d[c],m=k.closest(h,e,b);m.connection&&(f=m.connection,g=k,h=m.radius)}Blockly.highlightedConnection_&&Blockly.highlightedConnection_!=f&&(Blockly.highlightedConnection_.unhighlight(),
Blockly.highlightedConnection_=null,Blockly.localConnection_=null);f&&f!=Blockly.highlightedConnection_&&(f.highlight(),Blockly.highlightedConnection_=f,Blockly.localConnection_=g);this.isDeletable()&&this.workspace.isDeleteArea(a)}}a.stopPropagation()};Blockly.BlockSvg.prototype.updateMovable=function(){this.isMovable()?Blockly.addClass_(this.svgGroup_,"blocklyDraggable"):Blockly.removeClass_(this.svgGroup_,"blocklyDraggable")};
Blockly.BlockSvg.prototype.setMovable=function(a){Blockly.BlockSvg.superClass_.setMovable.call(this,a);this.updateMovable()};Blockly.BlockSvg.prototype.setEditable=function(a){Blockly.BlockSvg.superClass_.setEditable.call(this,a);if(this.rendered)for(a=0;a<this.icons_.length;a++)this.icons_[a].updateEditable()};Blockly.BlockSvg.prototype.setShadow=function(a){Blockly.BlockSvg.superClass_.setShadow.call(this,a);this.updateColour()};Blockly.BlockSvg.prototype.getSvgRoot=function(){return this.svgGroup_};
Blockly.BlockSvg.SEP_SPACE_X=10;Blockly.BlockSvg.SEP_SPACE_Y=10;Blockly.BlockSvg.INLINE_PADDING_Y=5;Blockly.BlockSvg.MIN_BLOCK_Y=25;Blockly.BlockSvg.TAB_HEIGHT=20;Blockly.BlockSvg.TAB_WIDTH=8;Blockly.BlockSvg.NOTCH_WIDTH=30;Blockly.BlockSvg.CORNER_RADIUS=4;Blockly.BlockSvg.START_HAT=!0;Blockly.BlockSvg.START_HAT_PATH="c 30,-15 70,-15 100,0";Blockly.BlockSvg.NOTCH_PATH_LEFT="l 6,4 3,0 6,-4";Blockly.BlockSvg.NOTCH_PATH_RIGHT="l -6,4 -3,0 -6,-4";
Blockly.BlockSvg.TAB_PATH_DOWN="v 5 c 0,10 -"+Blockly.BlockSvg.TAB_WIDTH+",-8 -"+Blockly.BlockSvg.TAB_WIDTH+",7.5 s "+Blockly.BlockSvg.TAB_WIDTH+",-2.5 "+Blockly.BlockSvg.TAB_WIDTH+",7.5";Blockly.BlockSvg.TOP_LEFT_CORNER_START="m 0,"+Blockly.BlockSvg.CORNER_RADIUS;Blockly.BlockSvg.TOP_LEFT_CORNER="A "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,1 "+Blockly.BlockSvg.CORNER_RADIUS+",0";
Blockly.BlockSvg.INNER_TOP_LEFT_CORNER=Blockly.BlockSvg.NOTCH_PATH_RIGHT+" h -"+(Blockly.BlockSvg.NOTCH_WIDTH-15-Blockly.BlockSvg.CORNER_RADIUS)+" a "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 -"+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS;Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER="a "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS;
Blockly.BlockSvg.prototype.dispose=function(a,b){Blockly.Field.startCache();Blockly.selected==this&&Blockly.terminateDrag_();Blockly.ContextMenu.currentBlock==this&&Blockly.ContextMenu.hide();b&&this.rendered&&(this.unplug(a),this.disposeUiEffect());this.rendered=!1;Blockly.BlockSvg.superClass_.dispose.call(this,a);Blockly.Events.disable();for(var c=this.getIcons(),d=0;d<c.length;d++)c[d].dispose();Blockly.Events.enable();goog.dom.removeNode(this.svgGroup_);this.svgPath_=this.svgGroup_=null;Blockly.Field.stopCache()};
Blockly.BlockSvg.prototype.disposeUiEffect=function(){this.workspace.playAudio("delete");var a=Blockly.getSvgXY_(this.svgGroup_,this.workspace),b=this.svgGroup_.cloneNode(!0);b.translateX_=a.x;b.translateY_=a.y;b.setAttribute("transform","translate("+b.translateX_+","+b.translateY_+")");this.workspace.getParentSvg().appendChild(b);b.bBox_=b.getBBox();Blockly.BlockSvg.disposeUiStep_(b,this.RTL,new Date,this.workspace.scale)};
Blockly.BlockSvg.disposeUiStep_=function(a,b,c,d){var e=(new Date-c)/150;1<e?goog.dom.removeNode(a):(a.setAttribute("transform","translate("+(a.translateX_+(b?-1:1)*a.bBox_.width*d/2*e)+","+(a.translateY_+a.bBox_.height*d*e)+") scale("+(1-e)*d+")"),setTimeout(function(){Blockly.BlockSvg.disposeUiStep_(a,b,c,d)},10))};
Blockly.BlockSvg.prototype.connectionUiEffect=function(){this.workspace.playAudio("click");if(!(1>this.workspace.scale)){var a=Blockly.getSvgXY_(this.svgGroup_,this.workspace);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.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;1<d?goog.dom.removeNode(a):(a.setAttribute("r",25*d*c),a.style.opacity=1-d,Blockly.BlockSvg.disconnectUiStop_.pid_=setTimeout(function(){Blockly.BlockSvg.connectionUiStep_(a,b,c)},10))};
Blockly.BlockSvg.prototype.disconnectUiEffect=function(){this.workspace.playAudio("disconnect");if(!(1>this.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<d?a.skew_="":(a.skew_="skewX("+Math.round(Math.sin(d*Math.PI*3)*(1-d)*b)+")",Blockly.BlockSvg.disconnectUiStop_.group=a,Blockly.BlockSvg.disconnectUiStop_.pid=setTimeout(function(){Blockly.BlockSvg.disconnectUiStep_(a,b,c)},10));a.setAttribute("transform",a.translate_+a.skew_)};
Blockly.BlockSvg.disconnectUiStop_=function(){if(Blockly.BlockSvg.disconnectUiStop_.group){clearTimeout(Blockly.BlockSvg.disconnectUiStop_.pid);var a=Blockly.BlockSvg.disconnectUiStop_.group;a.skew_="";a.setAttribute("transform",a.translate_);Blockly.BlockSvg.disconnectUiStop_.group=null}};Blockly.BlockSvg.disconnectUiStop_.pid=0;Blockly.BlockSvg.disconnectUiStop_.group=null;
Blockly.BlockSvg.prototype.updateColour=function(){var a=this.getColour(),b=goog.color.hexToRgb(a);this.isShadow()&&(b=goog.color.lighten(b,.6),a=goog.color.rgbArrayToHex(b));this.svgPath_.setAttribute("fill",a);a=goog.color.darken(b,.1);a=goog.color.rgbArrayToHex(a);this.svgPath_.setAttribute("stroke",a);a=this.getIcons();for(b=0;b<a.length;b++)a[b].updateColour();for(a=0;b=this.inputList[a];a++)for(var c=0,d;d=b.fieldRow[c];c++)d.setText(null)};Blockly.BlockSvg.prototype.updateDisabled=function(){};
Blockly.BlockSvg.disconnectUiStop_=function(){if(Blockly.BlockSvg.disconnectUiStop_.group){clearTimeout(Blockly.BlockSvg.disconnectUiStop_.pid);var a=Blockly.BlockSvg.disconnectUiStop_.group;a.skew_="";a.setAttribute("transform",a.translate_);Blockly.BlockSvg.disconnectUiStop_.group=null}};Blockly.BlockSvg.disconnectUiStop_.pid=0;Blockly.BlockSvg.disconnectUiStop_.group=null;Blockly.BlockSvg.prototype.updateDisabled=function(){};
Blockly.BlockSvg.prototype.getCommentText=function(){return this.comment?this.comment.getText().replace(/\s+$/,"").replace(/ +\n/g,"\n"):""};Blockly.BlockSvg.prototype.setCommentText=function(a){var b=!1;goog.isString(a)?(this.comment||(this.comment=new Blockly.Comment(this),b=!0),this.comment.setText(a)):this.comment&&(this.comment.dispose(),b=!0);b&&this.rendered&&(this.render(),this.bumpNeighbours_())};
Blockly.BlockSvg.prototype.setWarningText=function(a,b){this.setWarningText.pid_||(this.setWarningText.pid_=Object.create(null));var c=b||"";if(c)this.setWarningText.pid_[c]&&(clearTimeout(this.setWarningText.pid_[c]),delete this.setWarningText.pid_[c]);else for(var d in this.setWarningText.pid_)clearTimeout(this.setWarningText.pid_[d]),delete this.setWarningText.pid_[d];if(2==Blockly.dragMode_){var e=this;this.setWarningText.pid_[c]=setTimeout(function(){e.workspace&&(delete e.setWarningText.pid_[c],
e.setWarningText(a,c))},100)}else{this.isInFlyout&&(a=null);d=!1;if(goog.isString(a))this.warning||(this.warning=new Blockly.Warning(this),d=!0),this.warning.setText(a,c);else if(this.warning&&!c)this.warning.dispose(),d=!0;else if(this.warning){d=this.warning.getText();this.warning.setText("",c);var f=this.warning.getText();f||this.warning.dispose();d=d==f}d&&this.rendered&&(this.render(),this.bumpNeighbours_())}};
Blockly.BlockSvg.prototype.setMutator=function(a){this.mutator&&this.mutator!==a&&this.mutator.dispose();a&&(a.block_=this,this.mutator=a,a.createIcon())};Blockly.BlockSvg.prototype.addSelect=function(){Blockly.addClass_(this.svgGroup_,"blocklySelected");this.svgGroup_.parentNode.appendChild(this.svgGroup_)};Blockly.BlockSvg.prototype.removeSelect=function(){Blockly.removeClass_(this.svgGroup_,"blocklySelected")};Blockly.BlockSvg.prototype.addDragging=function(){Blockly.addClass_(this.svgGroup_,"blocklyDragging")};
Blockly.BlockSvg.prototype.removeDragging=function(){Blockly.removeClass_(this.svgGroup_,"blocklyDragging")};
Blockly.BlockSvg.prototype.removeDragging=function(){Blockly.removeClass_(this.svgGroup_,"blocklyDragging")};Blockly.BlockSvg.render={};Blockly.BlockSvg.SEP_SPACE_X=10;Blockly.BlockSvg.SEP_SPACE_Y=10;Blockly.BlockSvg.INLINE_PADDING_Y=5;Blockly.BlockSvg.MIN_BLOCK_Y=25;Blockly.BlockSvg.TAB_HEIGHT=20;Blockly.BlockSvg.TAB_WIDTH=8;Blockly.BlockSvg.NOTCH_WIDTH=30;Blockly.BlockSvg.CORNER_RADIUS=4;Blockly.BlockSvg.START_HAT=!0;Blockly.BlockSvg.START_HAT_PATH="c 30,-15 70,-15 100,0";Blockly.BlockSvg.NOTCH_PATH_LEFT="l 6,4 3,0 6,-4";Blockly.BlockSvg.NOTCH_PATH_RIGHT="l -6,4 -3,0 -6,-4";
Blockly.BlockSvg.TAB_PATH_DOWN="v 5 c 0,10 -"+Blockly.BlockSvg.TAB_WIDTH+",-8 -"+Blockly.BlockSvg.TAB_WIDTH+",7.5 s "+Blockly.BlockSvg.TAB_WIDTH+",-2.5 "+Blockly.BlockSvg.TAB_WIDTH+",7.5";Blockly.BlockSvg.TOP_LEFT_CORNER_START="m 0,"+Blockly.BlockSvg.CORNER_RADIUS;Blockly.BlockSvg.TOP_LEFT_CORNER="A "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,1 "+Blockly.BlockSvg.CORNER_RADIUS+",0";
Blockly.BlockSvg.INNER_TOP_LEFT_CORNER=Blockly.BlockSvg.NOTCH_PATH_RIGHT+" h -"+(Blockly.BlockSvg.NOTCH_WIDTH-15-Blockly.BlockSvg.CORNER_RADIUS)+" a "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 -"+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS;Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER="a "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS+" 0 0,0 "+Blockly.BlockSvg.CORNER_RADIUS+","+Blockly.BlockSvg.CORNER_RADIUS;
Blockly.BlockSvg.prototype.connectionUiEffect=function(){this.workspace.playAudio("click");if(!(1>this.workspace.scale)){var a=Blockly.getSvgXY_(this.svgGroup_,this.workspace);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.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;1<d?goog.dom.removeNode(a):(a.setAttribute("r",25*d*c),a.style.opacity=1-d,Blockly.BlockSvg.disconnectUiStop_.pid_=setTimeout(function(){Blockly.BlockSvg.connectionUiStep_(a,b,c)},10))};
Blockly.BlockSvg.prototype.updateColour=function(){var a=this.getColour(),b=goog.color.hexToRgb(a);this.isShadow()&&(b=goog.color.lighten(b,.6),a=goog.color.rgbArrayToHex(b));this.svgPath_.setAttribute("fill",a);a=goog.color.darken(b,.1);a=goog.color.rgbArrayToHex(a);this.svgPath_.setAttribute("stroke",a);a=this.getIcons();for(b=0;b<a.length;b++)a[b].updateColour();for(a=0;b=this.inputList[a];a++)for(var c=0,d;d=b.fieldRow[c];c++)d.setText(null)};
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.render=function(a){Blockly.Field.startCache();this.rendered=!0;var b=Blockly.BlockSvg.SEP_SPACE_X;this.RTL&&(b=-b);for(var c=this.getIcons(),d=0;d<c.length;d++)b=c[d].renderIcon(b);b+=this.RTL?Blockly.BlockSvg.SEP_SPACE_X:-Blockly.BlockSvg.SEP_SPACE_X;c=this.renderCompute_(b);this.renderDraw_(b,c);!1!==a&&((a=this.getParent())?a.render(!0):Blockly.fireUiEvent(window,"resize"));Blockly.Field.stopCache()};
Blockly.BlockSvg.prototype.renderFields_=function(a,b,c){c+=Blockly.BlockSvg.INLINE_PADDING_Y;this.RTL&&(b=-b);for(var d=0,e;e=a[d];d++){var f=e.getSvgRoot();f&&(this.RTL?(b-=e.renderSep+e.renderWidth,f.setAttribute("transform","translate("+b+","+c+")"),e.renderWidth&&(b-=Blockly.BlockSvg.SEP_SPACE_X)):(f.setAttribute("transform","translate("+(b+e.renderSep)+","+c+")"),e.renderWidth&&(b+=e.renderSep+e.renderWidth+Blockly.BlockSvg.SEP_SPACE_X)))}return this.RTL?-b:b};
Blockly.BlockSvg.prototype.renderCompute_=function(a){var b=this.inputList,c=[];c.rightEdge=a+2*Blockly.BlockSvg.SEP_SPACE_X;if(this.previousConnection||this.nextConnection)c.rightEdge=Math.max(c.rightEdge,Blockly.BlockSvg.NOTCH_WIDTH+Blockly.BlockSvg.SEP_SPACE_X);for(var d=0,e=0,f=!1,g=!1,h=!1,k=void 0,m=0,n;n=b[m];m++)if(n.isVisible()){var l;k&&k!=Blockly.NEXT_STATEMENT&&n.type!=Blockly.NEXT_STATEMENT?l=c[c.length-1]:(k=n.type,l=[],l.type=n.type!=Blockly.NEXT_STATEMENT?Blockly.BlockSvg.INLINE:n.type,
@ -1158,7 +1158,8 @@ this.spellcheck_);var e=Blockly.FieldTextInput.FONTSIZE*b.scale+"pt";c.style.fon
d.onWorkspaceChangeWrapper_=this.resizeEditor_.bind(this);b.addChangeListener(d.onWorkspaceChangeWrapper_)}};Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_=function(a){var b=Blockly.FieldTextInput.htmlInput_;13==a.keyCode?Blockly.WidgetDiv.hide():27==a.keyCode?(b.value=b.defaultValue,Blockly.WidgetDiv.hide()):9==a.keyCode&&(Blockly.WidgetDiv.hide(),this.sourceBlock_.tab(this,!a.shiftKey),a.preventDefault())};
Blockly.FieldTextInput.prototype.onHtmlInputChange_=function(a){a=Blockly.FieldTextInput.htmlInput_;var b=a.value;b!==a.oldValue_?(a.oldValue_=b,this.setValue(b),this.validate_()):goog.userAgent.WEBKIT&&this.sourceBlock_.render();this.resizeEditor_()};
Blockly.FieldTextInput.prototype.validate_=function(){var a=!0;goog.asserts.assertObject(Blockly.FieldTextInput.htmlInput_);var b=Blockly.FieldTextInput.htmlInput_;this.sourceBlock_&&this.validator_&&(a=this.validator_(b.value));null===a?Blockly.addClass_(b,"blocklyInvalidInput"):Blockly.removeClass_(b,"blocklyInvalidInput")};
Blockly.FieldTextInput.prototype.resizeEditor_=function(){var a=Blockly.WidgetDiv.DIV,b=this.fieldGroup_.getBBox();a.style.width=b.width*this.sourceBlock_.workspace.scale+"px";a.style.height=b.height*this.sourceBlock_.workspace.scale+"px";b=this.getAbsoluteXY_();if(this.sourceBlock_.RTL){var c=this.getScaledBBox_();b.x+=c.width;b.x-=a.offsetWidth}b.y+=1;goog.userAgent.GECKO&&Blockly.WidgetDiv.DIV.style.top&&(--b.x,--b.y);goog.userAgent.WEBKIT&&(b.y-=3);a.style.left=b.x+"px";a.style.top=b.y+"px"};
Blockly.FieldTextInput.prototype.resizeEditor_=function(){var a=Blockly.WidgetDiv.DIV,b=this.fieldGroup_.getBBox(),c=b.height*this.sourceBlock_.workspace.scale;a.style.width=Math.max(b.width,Blockly.BlockSvg.FIELD_WIDTH-Blockly.BlockSvg.SEP_SPACE_X)*this.sourceBlock_.workspace.scale+"px";a.style.height=c+"px";b=this.getAbsoluteXY_();b.x+=Blockly.BlockSvg.SEP_SPACE_X*this.sourceBlock_.workspace.scale;b.y+=Blockly.BlockSvg.FIELD_HEIGHT*this.sourceBlock_.workspace.scale/2-c/2+3;this.sourceBlock_.RTL&&
(c=this.getScaledBBox_(),b.x+=c.width,b.x-=a.offsetWidth);b.y+=1;goog.userAgent.GECKO&&Blockly.WidgetDiv.DIV.style.top&&(--b.x,--b.y);goog.userAgent.WEBKIT&&(b.y-=3);a.style.left=b.x+"px";a.style.top=b.y+"px"};
Blockly.FieldTextInput.prototype.widgetDispose_=function(){var a=this;return function(){var b=Blockly.FieldTextInput.htmlInput_,c=b.value;if(a.sourceBlock_&&a.validator_){var d=a.validator_(c);null===d?c=b.defaultValue:void 0!==d&&(c=d)}a.setValue(c);a.sourceBlock_.rendered&&a.sourceBlock_.render();Blockly.unbindEvent_(b.onKeyDownWrapper_);Blockly.unbindEvent_(b.onKeyUpWrapper_);Blockly.unbindEvent_(b.onKeyPressWrapper_);a.sourceBlock_.workspace.removeChangeListener(b.onWorkspaceChangeWrapper_);Blockly.FieldTextInput.htmlInput_=
null;b=Blockly.WidgetDiv.DIV.style;b.width="auto";b.height="auto";b.fontSize=""}};Blockly.FieldTextInput.numberValidator=function(a){if(null===a)return null;a=String(a);a=a.replace(/O/ig,"0");a=a.replace(/,/g,"");a=parseFloat(a||0);return isNaN(a)?null:String(a)};Blockly.FieldTextInput.nonnegativeIntegerValidator=function(a){(a=Blockly.FieldTextInput.numberValidator(a))&&(a=String(Math.max(0,Math.floor(a))));return a};Blockly.FieldAngle=function(a,b){this.symbol_=Blockly.createSvgElement("tspan",{},null);this.symbol_.appendChild(document.createTextNode("\u00b0"));Blockly.FieldAngle.superClass_.constructor.call(this,a,b)};goog.inherits(Blockly.FieldAngle,Blockly.FieldTextInput);
Blockly.FieldAngle.prototype.setValidator=function(a){Blockly.FieldAngle.superClass_.setValidator.call(this,a?function(b){var c=a.call(this,b);if(null===c)var d=c;else void 0===c&&(c=b),d=Blockly.FieldAngle.angleValidator.call(this,c),void 0===d&&(d=c);return d===b?void 0:d}:Blockly.FieldAngle.angleValidator)};Blockly.FieldAngle.ROUND=15;Blockly.FieldAngle.HALF=50;Blockly.FieldAngle.CLOCKWISE=!1;Blockly.FieldAngle.OFFSET=0;Blockly.FieldAngle.WRAP=360;
@ -1224,9 +1225,12 @@ this.workspace_.scale,viewTop:-this.workspace_.scrollY,viewLeft:-this.workspace_
Blockly.Flyout.prototype.setMetrics_=function(a){var b=this.getMetrics_();b&&(!this.horizontalLayout_&&goog.isNumber(a.y)?this.workspace_.scrollY=-b.contentHeight*a.y-b.contentTop:this.horizontalLayout_&&goog.isNumber(a.x)&&(this.workspace_.scrollX=this.RTL?-b.contentWidth*a.x+b.contentLeft:-b.contentWidth*a.x-b.contentLeft),this.workspace_.translate(this.horizontalLayout_&&this.RTL?b.absoluteLeft+b.viewWidth-this.workspace_.scrollX:this.workspace_.scrollX+b.absoluteLeft,this.workspace_.scrollY+b.absoluteTop))};
Blockly.Flyout.prototype.setVerticalOffset=function(a){this.verticalOffset_=a};
Blockly.Flyout.prototype.position=function(){if(this.isVisible()){var a=this.targetWorkspace_.getMetrics();if(a){var b=this.horizontalLayout_?a.viewWidth:this.width_,b=b-this.CORNER_RADIUS;this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT&&(b*=-1);this.setBackgroundPath_(b,this.horizontalLayout_?this.height_+this.verticalOffset_:a.viewHeight);b=a.absoluteLeft;this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT&&(b+=a.viewWidth,b-=this.width_);var c=a.absoluteTop;this.toolboxPosition_==Blockly.TOOLBOX_AT_BOTTOM&&
(c+=a.viewHeight,c-=this.height_);this.svgGroup_.setAttribute("transform","translate("+b+","+c+")");this.horizontalLayout_?this.width_=a.viewWidth:this.height_=a.viewHeight;this.scrollbar_&&this.scrollbar_.resize();this.svgGroup_.style.opacity=1}}};
Blockly.Flyout.prototype.setBackgroundPath_=function(a,b){var c=this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT,d=["M "+(c?this.width_:0)+",0"];d.push("h",a);d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?-this.CORNER_RADIUS:this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("v",Math.max(0,b-2*this.CORNER_RADIUS));d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?this.CORNER_RADIUS:-this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("h",-a);d.push("z");this.svgBackground_.setAttribute("d",
d.join(" "))};Blockly.Flyout.prototype.scrollToStart=function(){this.scrollbar_.set(this.horizontalLayout_&&this.RTL?1E9:0)};Blockly.Flyout.prototype.wheel_=function(a){if(!this.horizontalLayout_){var b=a.deltaY;if(b){goog.userAgent.GECKO&&(b*=10);var c=this.getMetrics_(),b=c.viewTop+b,b=Math.min(b,c.contentHeight-c.viewHeight),b=Math.max(b,0);this.scrollbar_.set(b);a.preventDefault();a.stopPropagation()}}};Blockly.Flyout.prototype.isVisible=function(){return this.svgGroup_&&"block"==this.svgGroup_.style.display};
(c+=a.viewHeight,c-=this.height_);this.svgGroup_.setAttribute("transform","translate("+b+","+c+")");this.horizontalLayout_?this.width_=a.viewWidth:this.height_=a.viewHeight;this.scrollbar_&&this.scrollbar_.resize();this.svgGroup_.style.opacity=1}}};Blockly.Flyout.prototype.setBackgroundPath_=function(a,b){this.horizontalLayout_?this.setBackgroundPathHorizontal_(a,b):this.setBackgroundPathVertical_(a,b)};
Blockly.Flyout.prototype.setBackgroundPathVertical_=function(a,b){var c=this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT,d=["M "+(c?this.width_:0)+",0"];d.push("h",a);d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?-this.CORNER_RADIUS:this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("v",Math.max(0,b-2*this.CORNER_RADIUS));d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?this.CORNER_RADIUS:-this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("h",-a);d.push("z");this.svgBackground_.setAttribute("d",
d.join(" "))};
Blockly.Flyout.prototype.setBackgroundPathHorizontal_=function(a,b){var c=this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP,d=["M 0,"+(c?0:this.CORNER_RADIUS)];c?(d.push("h",a+this.CORNER_RADIUS),d.push("v",b),d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,-this.CORNER_RADIUS,this.CORNER_RADIUS),d.push("h",-1*(a-this.CORNER_RADIUS)),d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,-this.CORNER_RADIUS,-this.CORNER_RADIUS)):(d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,this.CORNER_RADIUS,
-this.CORNER_RADIUS),d.push("h",a-this.CORNER_RADIUS),d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,1,this.CORNER_RADIUS,this.CORNER_RADIUS),d.push("v",b-this.CORNER_RADIUS),d.push("h",-a-this.CORNER_RADIUS));d.push("z");this.svgBackground_.setAttribute("d",d.join(" "))};Blockly.Flyout.prototype.scrollToStart=function(){this.scrollbar_.set(this.horizontalLayout_&&this.RTL?1E9:0)};
Blockly.Flyout.prototype.wheel_=function(a){if(!this.horizontalLayout_){var b=a.deltaY;if(b){goog.userAgent.GECKO&&(b*=10);var c=this.getMetrics_(),b=c.viewTop+b,b=Math.min(b,c.contentHeight-c.viewHeight),b=Math.max(b,0);this.scrollbar_.set(b);a.preventDefault();a.stopPropagation()}}};Blockly.Flyout.prototype.isVisible=function(){return this.svgGroup_&&"block"==this.svgGroup_.style.display};
Blockly.Flyout.prototype.hide=function(){if(this.isVisible()){this.svgGroup_.style.display="none";for(var a=0,b;b=this.listeners_[a];a++)Blockly.unbindEvent_(b);this.listeners_.length=0;this.reflowWrapper_&&(this.workspace_.removeChangeListener(this.reflowWrapper_),this.reflowWrapper_=null)}};
Blockly.Flyout.prototype.show=function(a){this.hide();for(var b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)d.workspace==this.workspace_&&d.dispose(!1,!1);for(var c=0,e;e=this.buttons_[c];c++)goog.dom.removeNode(e);this.buttons_.length=0;a==Blockly.Variables.NAME_TYPE?a=Blockly.Variables.flyoutCategory(this.workspace_.targetWorkspace):a==Blockly.Procedures.NAME_TYPE&&(a=Blockly.Procedures.flyoutCategory(this.workspace_.targetWorkspace));for(var f=this.CORNER_RADIUS,b=[],g=[],c=this.permanentlyDisabled_.length=
0;e=a[c];c++)e.tagName&&"BLOCK"==e.tagName.toUpperCase()&&(d=Blockly.Xml.domToBlock(this.workspace_,e),d.disabled&&this.permanentlyDisabled_.push(d),b.push(d),d=parseInt(e.getAttribute("gap"),10),g.push(isNaN(d)?3*f:d));this.svgGroup_.style.opacity=0;this.svgGroup_.style.display="block";a=f/this.workspace_.scale+Blockly.BlockSvg.TAB_WIDTH;for(c=0;d=b[c];c++){e=d.getDescendants();for(var h=0,k;k=e[h];h++)k.isInFlyout=!0;d.render();h=d.getSvgRoot();e=d.getHeightWidth();d.moveBy(this.horizontalLayout_&&
@ -1273,9 +1277,9 @@ Blockly.Css.CONTENT=[".blocklySvg {","background-color: #fff;","outline: none;",
"fill: #aaa;","}",".blocklyResizeSW {","cursor: sw-resize;","fill: #aaa;","}",".blocklyResizeLine {","stroke: #888;","stroke-width: 1;","}",".blocklyHighlightedConnectionPath {","fill: none;","stroke: #fc3;","stroke-width: 4px;","}",".blocklyPath {","stroke-width: 1.5px;","}",".blocklySelected>.blocklyPath {","}",".blocklyDragging>.blocklyPath {","fill-opacity: .8;","stroke-opacity: .8;","}",".blocklyDisabled>.blocklyPath {","fill-opacity: .5;","stroke-opacity: .5;","}",".blocklyText {","cursor: default;",
"fill: #fff;","font-family: sans-serif;","font-size: 11pt;","}",".blocklyNonEditableText>text {","pointer-events: none;","}",".blocklyNonEditableText>rect,",".blocklyEditableText>rect {","fill: #fff;","fill-opacity: .6;","}",".blocklyNonEditableText>text,",".blocklyEditableText>text {","fill: #000;","}",".blocklyEditableText:hover>rect {","stroke: #fff;","stroke-width: 2;","}",".blocklyBubbleText {","fill: #000;","}",".blocklySvg text {","user-select: none;","-moz-user-select: none;","-webkit-user-select: none;",
"cursor: inherit;","}",".blocklyHidden {","display: none;","}",".blocklyFieldDropdown:not(.blocklyHidden) {","display: block;","}",".blocklyIconGroup {","cursor: default;","}",".blocklyIconGroup:not(:hover),",".blocklyIconGroupReadonly {","opacity: .6;","}",".blocklyIconShape {","fill: #00f;","stroke: #fff;","stroke-width: 1px;","}",".blocklyIconSymbol {","fill: #fff;","}",".blocklyMinimalBody {","margin: 0;","padding: 0;","}",".blocklyCommentTextarea {","background-color: #ffc;","border: 0;","margin: 0;",
"padding: 2px;","resize: none;","}",".blocklyHtmlInput {","border: none;","border-radius: 4px;","font-family: sans-serif;","height: 100%;","margin: 0;","outline: none;","padding: 0 1px;","width: 100%","}",".blocklyMainBackground {","stroke-width: 1;","stroke: #c6c6c6;","}",".blocklyMutatorBackground {","fill: #fff;","stroke: #ddd;","stroke-width: 1;","}",".blocklyFlyoutBackground {","fill: #ddd;","fill-opacity: .8;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarKnob {","fill: #ccc;",
"}",".blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyScrollbarKnob:hover {","fill: #bbb;","}",".blocklyZoom>image {","opacity: .4;","}",".blocklyZoom>image:hover {","opacity: .6;","}",".blocklyZoom>image:active {","opacity: .8;","}",".blocklyFlyout .blocklyScrollbarKnob {","fill: #bbb;","}",".blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyFlyout .blocklyScrollbarKnob:hover {","fill: #aaa;","}",".blocklyInvalidInput {","background: #faa;","}",".blocklyAngleCircle {",
"stroke: #444;","stroke-width: 1;","fill: #ddd;","fill-opacity: .8;","}",".blocklyAngleMarks {","stroke: #444;","stroke-width: 1;","}",".blocklyAngleGauge {","fill: #f88;","fill-opacity: .8;","}",".blocklyAngleLine {","stroke: #f00;","stroke-width: 2;","stroke-linecap: round;","}",".blocklyContextMenu {","border-radius: 4px;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {",
"padding: 2px;","resize: none;","}",".blocklyHtmlInput {","border: none;","border-radius: 4px;","font-family: sans-serif;","height: 100%;","margin: 0;","outline: none;","padding: 0 1px;","width: 100%;","text-align: center","}",".blocklyMainBackground {","stroke-width: 1;","stroke: #c6c6c6;","}",".blocklyMutatorBackground {","fill: #fff;","stroke: #ddd;","stroke-width: 1;","}",".blocklyFlyoutBackground {","fill: #ddd;","fill-opacity: .8;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarKnob {",
"fill: #ccc;","}",".blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyScrollbarKnob:hover {","fill: #bbb;","}",".blocklyZoom>image {","opacity: .4;","}",".blocklyZoom>image:hover {","opacity: .6;","}",".blocklyZoom>image:active {","opacity: .8;","}",".blocklyFlyout .blocklyScrollbarKnob {","fill: #bbb;","}",".blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarKnob,",".blocklyFlyout .blocklyScrollbarKnob:hover {","fill: #aaa;","}",".blocklyInvalidInput {","background: #faa;",
"}",".blocklyAngleCircle {","stroke: #444;","stroke-width: 1;","fill: #ddd;","fill-opacity: .8;","}",".blocklyAngleMarks {","stroke: #444;","stroke-width: 1;","}",".blocklyAngleGauge {","fill: #f88;","fill-opacity: .8;","}",".blocklyAngleLine {","stroke: #f00;","stroke-width: 2;","stroke-linecap: round;","}",".blocklyContextMenu {","border-radius: 4px;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {",
"background: url(<<<PATH>>>/sprites.png) no-repeat -48px -16px !important;","}",".blocklyToolboxDiv {","background-color: #ddd;","overflow-x: visible;","overflow-y: auto;","position: absolute;","}",".blocklyTreeRoot {","padding: 4px 0;","}",".blocklyTreeRoot:focus {","outline: none;","}",".blocklyTreeRow {","height: 22px;","line-height: 22px;","margin-bottom: 3px;","padding-right: 8px;","white-space: nowrap;","}",".blocklyHorizontalTree {","float: left;","margin: 1px 5px 8px 0px;","}",".blocklyHorizontalTreeRtl {",
"float: right;","margin: 1px 0px 8px 5px;","}",'.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',"margin-left: 8px;","}",".blocklyTreeRow:not(.blocklyTreeSelected):hover {","background-color: #e4e4e4;","}",".blocklyTreeSeparator {","border-bottom: solid #e5e5e5 1px;","height: 0px;","margin: 5px 0;","}",".blocklyTreeSeparatorHorizontal {","border-right: solid #e5e5e5 1px;","width: 0px;","padding: 5px 0;","margin: 0 5px;","}",".blocklyTreeIcon {","background-image: url(<<<PATH>>>/sprites.png);","height: 16px;",
"vertical-align: middle;","width: 16px;","}",".blocklyTreeIconClosedLtr {","background-position: -32px -1px;","}",".blocklyTreeIconClosedRtl {","background-position: 0px -1px;","}",".blocklyTreeIconOpen {","background-position: -16px -1px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedLtr {","background-position: -32px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedRtl {","background-position: 0px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconOpen {","background-position: -16px -17px;",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -28,11 +28,13 @@ goog.provide('Blockly.Blocks.math');
goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
/**
* Common HSV hue for all blocks in this category.
*/
Blockly.Blocks.math.HUE = 230;
Blockly.Blocks.math.HUE = Blockly.Colours.textField;
Blockly.Blocks['math_number'] = {
/**

View file

@ -28,11 +28,13 @@ goog.provide('Blockly.Blocks.texts');
goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
/**
* Common HSV hue for all blocks in this category.
*/
Blockly.Blocks.texts.HUE = 160;
Blockly.Blocks.texts.HUE = Blockly.Colours.textField;
Blockly.Blocks['text'] = {
/**

View file

@ -28,6 +28,8 @@ goog.provide('Blockly.Blocks.control');
goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
Blockly.Blocks['control_repeat'] = {
/**
* Block for repeat n times (external number).
@ -41,11 +43,11 @@ Blockly.Blocks['control_repeat'] = {
"args0": [
{
"type": "input_statement",
"name": "DO"
"name": "SUBSTACK"
},
{
"type": "field_image",
"src": "../media/icons/control_forever.svg",
"src": Blockly.mainWorkspace.options.pathToMedia + "icons/control_forever.svg",
"width": 40,
"height": 40,
"alt": "*"
@ -59,7 +61,9 @@ Blockly.Blocks['control_repeat'] = {
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"colour": '#F2B827',
"colour": Blockly.Colours.control.primary,
"colourSecondary": Blockly.Colours.control.secondary,
"colourTertiary": Blockly.Colours.control.tertiary,
"tooltip": "",
"helpUrl": "http://www.example.com/"
});
@ -83,11 +87,11 @@ Blockly.Blocks['control_forever'] = {
"args0": [
{
"type": "input_statement",
"name": "NAME"
"name": "SUBSTACK"
},
{
"type": "field_image",
"src": "../media/icons/control_forever.svg",
"src": Blockly.mainWorkspace.options.pathToMedia + "icons/control_forever.svg",
"width": 40,
"height": 40,
"alt": "*"
@ -95,7 +99,9 @@ Blockly.Blocks['control_forever'] = {
],
"inputsInline": true,
"previousStatement": null,
"colour": '#F2B827',
"colour": Blockly.Colours.control.primary,
"colourSecondary": Blockly.Colours.control.secondary,
"colourTertiary": Blockly.Colours.control.tertiary,
"tooltip": ""
});

View file

@ -28,6 +28,8 @@ goog.provide('Blockly.Blocks.event');
goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
Blockly.Blocks['event_whenflagclicked'] = {
/**
* Block for repeat n times (external number).
@ -40,7 +42,7 @@ Blockly.Blocks['event_whenflagclicked'] = {
"args0": [
{
"type": "field_image",
"src": "../media/icons/event_whenflagclicked.svg",
"src": Blockly.mainWorkspace.options.pathToMedia + "icons/event_whenflagclicked.svg",
"width": 40,
"height": 40,
"alt": "flag"
@ -48,7 +50,9 @@ Blockly.Blocks['event_whenflagclicked'] = {
],
"inputsInline": true,
"nextStatement": null,
"colour": '#F2EC27',
"colour": Blockly.Colours.event.primary,
"colourSecondary": Blockly.Colours.event.secondary,
"colourTertiary": Blockly.Colours.event.tertiary,
"tooltip": "Do stuff!"
});

View file

@ -0,0 +1,69 @@
/**
* @license
* Visual Blocks Editor
*
* Copyright 2016 MIT
* https://github.com/lkjashdflkjahsdf
*
* 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 Looks blocks for Scratch (Horizontal)
* @author rschamp@media.mit.edu <Ray Schamp>
*/
'use strict';
goog.provide('Blockly.Blocks.looks');
goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
Blockly.Blocks['looks_say'] = {
/**
* Block to say something.
* @this Blockly.Block
*/
init: function() {
this.jsonInit({
"id": "looks_say",
"message0": "%1 %2",
"args0": [
{
"type": "field_image",
"src": Blockly.mainWorkspace.options.pathToMedia + "icons/looks_say.svg",
"width": 40,
"height": 40,
"alt": "say"
},
{
"type": "input_value",
"name": "MESSAGE",
"check": "String"
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"colour": Blockly.Colours.looks.primary,
"colourSecondary": Blockly.Colours.looks.secondary,
"colourTertiary": Blockly.Colours.looks.tertiary,
"tooltip": ""
});
this.setHelpUrl(function () {
return 'halp me plz. k thx bye.';
});
}
};

View file

@ -28,6 +28,8 @@ goog.provide('Blockly.Blocks.motion');
goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
Blockly.Blocks['motion_moveright'] = {
/**
* Block for move right (external number)
@ -41,7 +43,7 @@ Blockly.Blocks['motion_moveright'] = {
"args0": [
{
"type": "field_image",
"src": "../media/icons/motion_moveright.svg",
"src": Blockly.mainWorkspace.options.pathToMedia + "icons/motion_moveright.svg",
"width": 40,
"height": 40,
"alt": "*"
@ -49,7 +51,9 @@ Blockly.Blocks['motion_moveright'] = {
],
"previousStatement": null,
"nextStatement": null,
"colour": '#25AFF4',
"colour": Blockly.Colours.motion.primary,
"colourSecondary": Blockly.Colours.motion.secondary,
"colourTertiary": Blockly.Colours.motion.tertiary,
"tooltip": ""
});

View file

@ -439,10 +439,10 @@ class Gen_langfiles(threading.Thread):
print("FAILED to create " + f)
def exclude_vertical(item):
return not item.endswith("block_vertical_scratch.js")
return not item.endswith("block_render_svg_vertical.js")
def exclude_horizontal(item):
return not item.endswith("block_svg.js")
return not item.endswith("block_render_svg_horizontal.js")
if __name__ == "__main__":
try:

View file

@ -164,6 +164,20 @@ Blockly.Block.prototype.data = null;
*/
Blockly.Block.prototype.colour_ = '#000000';
/**
* Secondary colour of the block in '#RRGGBB' format.
* @type {string}
* @private
*/
Blockly.Block.prototype.colourSecondary_ = '#000000';
/**
* Tertiary colour of the block in '#RRGGBB' format.
* @type {string}
* @private
*/
Blockly.Block.prototype.colourTertiary_ = '#000000';
/**
* Dispose of this block.
* @param {boolean} healStack If true, then try to heal any gap by connecting
@ -638,18 +652,57 @@ Blockly.Block.prototype.getColour = function() {
};
/**
* Change the colour of a block.
* @param {number|string} colour HSV hue value, or #RRGGBB string.
* Get the secondary colour of a block.
* @return {string} #RRGGBB string.
*/
Blockly.Block.prototype.setColour = function(colour) {
Blockly.Block.prototype.getColourSecondary = function() {
return this.colourSecondary_;
};
/**
* Get the tertiary colour of a block.
* @return {string} #RRGGBB string.
*/
Blockly.Block.prototype.getColourTertiary = function() {
return this.colourTertiary_;
};
/**
* Create an #RRGGBB string colour from a colour HSV hue value or #RRGGBB string.
* @param {number|string} colour HSV hue value, or #RRGGBB string.
* @return {string} #RRGGBB string.
* @private
*/
Blockly.Block.prototype.makeColour_ = function(colour) {
var hue = parseFloat(colour);
if (!isNaN(hue)) {
this.colour_ = Blockly.hueToRgb(hue);
return Blockly.hueToRgb(hue);
} else if (goog.isString(colour) && colour.match(/^#[0-9a-fA-F]{6}$/)) {
this.colour_ = colour;
return colour;
} else {
throw 'Invalid colour: ' + colour;
}
}
/**
* Change the colour of a block, and optional secondary/teriarty colours.
* @param {number|string} colour HSV hue value, or #RRGGBB string.
* @param {number|string} colourSecondary HSV hue value, or #RRGGBB string.
* @param {number|string} colourTertiary HSV hue value, or #RRGGBB string.
*/
Blockly.Block.prototype.setColour = function(colour, colourSecondary, colourTertiary) {
this.colour_ = this.makeColour_(colour);
if (colourSecondary !== undefined) {
this.colourSecondary_ = this.makeColour_(colourSecondary);
} else {
this.colourSecondary_ = goog.color.darken(colour, 0.1);
}
if (colourTertiary !== undefined) {
this.colourTertiary_ = this.makeColour_(colourTertiary);
} else {
this.colourTertiary_ = goog.color.darken(colour, 0.2);
}
if (this.rendered) {
this.updateColour();
}
@ -973,7 +1026,7 @@ Blockly.Block.prototype.jsonInit = function(json) {
// Set basic properties of block.
if (json['colour'] !== undefined) {
this.setColour(json['colour']);
this.setColour(json['colour'], json['colourSecondary'], json['colourTertiary']);
}
// Interpolate the message blocks.

View file

@ -0,0 +1,611 @@
/**
* @license
* Visual Blocks Editor
*
* Copyright 2012 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 for graphically rendering a block as SVG.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.BlockSvg.render');
goog.require('Blockly.BlockSvg');
// UI constants for rendering blocks.
/**
* Grid unit to pixels conversion
* @const
*/
Blockly.BlockSvg.GRID_UNIT = 4;
/**
* Horizontal space between elements.
* @const
*/
Blockly.BlockSvg.SEP_SPACE_X = 3 * Blockly.BlockSvg.GRID_UNIT;
/**
* Vertical space between elements.
* @const
*/
Blockly.BlockSvg.SEP_SPACE_Y = 3 * Blockly.BlockSvg.GRID_UNIT;
/**
* Vertical space above blocks with statements.
* @const
*/
Blockly.BlockSvg.STATEMENT_BLOCK_SPACE = 4 * Blockly.BlockSvg.GRID_UNIT;
/**
* Height of user inputs
* @const
*/
Blockly.BlockSvg.FIELD_HEIGHT = 8 * Blockly.BlockSvg.GRID_UNIT;
/**
* Width of user inputs
* @const
*/
Blockly.BlockSvg.FIELD_WIDTH = 12 * Blockly.BlockSvg.GRID_UNIT;
/**
* Corner radius of number inputs
* @const
*/
Blockly.BlockSvg.NUMBER_FIELD_CORNER_RADIUS = 4 * Blockly.BlockSvg.GRID_UNIT;
/**
* Corner radius of text inputs
* @const
*/
Blockly.BlockSvg.TEXT_FIELD_CORNER_RADIUS = 1 * Blockly.BlockSvg.GRID_UNIT;
/**
* Minimum width of a block.
* @const
*/
Blockly.BlockSvg.MIN_BLOCK_X = 16 * Blockly.BlockSvg.GRID_UNIT;
/**
* Minimum height of a block.
* @const
*/
Blockly.BlockSvg.MIN_BLOCK_Y = 16 * Blockly.BlockSvg.GRID_UNIT;
/**
* Width of horizontal puzzle tab.
* @const
*/
Blockly.BlockSvg.TAB_WIDTH = 2 * Blockly.BlockSvg.GRID_UNIT;
/**
* Rounded corner radius.
* @const
*/
Blockly.BlockSvg.CORNER_RADIUS = 1 * Blockly.BlockSvg.GRID_UNIT;
/**
* Rounded corner radius.
* @const
*/
Blockly.BlockSvg.HAT_CORNER_RADIUS = 8 * Blockly.BlockSvg.GRID_UNIT;
/**
* Full height of connector notch including rounded corner.
* @const
*/
Blockly.BlockSvg.NOTCH_HEIGHT = 8 * Blockly.BlockSvg.GRID_UNIT + 2;
/**
* Width of connector notch
* @const
*/
Blockly.BlockSvg.NOTCH_WIDTH = 2 * Blockly.BlockSvg.GRID_UNIT;
/**
* SVG path for drawing next/previous notch from top to bottom.
* Drawn in pixel units since Bezier control points are off the grid.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_DOWN =
'c 0,2 1,3 2,4 ' +
'l 4,4 ' +
'c 1,1 2,2 2,4 ' +
'v 12 ' +
'c 0,2 -1,3 -2,4 ' +
'l -4,4 ' +
'c -1,1 -2,2 -2,4';
/**
* SVG path for drawing next/previous notch from bottom to top.
* Drawn in pixel units since Bezier control points are off the grid.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_UP =
'c 0,-2 1,-3 2,-4 '+
'l 4,-4 ' +
'c 1,-1 2,-2 2,-4 ' +
'v -12 ' +
'c 0,-2 -1,-3 -2,-4 ' +
'l -4,-4 ' +
'c -1,-1 -2,-2 -2,-4';
/**
* Width of rendered icons in px
* @const
*/
Blockly.BlockSvg.ICON_WIDTH = 10 * Blockly.BlockSvg.GRID_UNIT;
/**
* Height of rendered icons in px
* @const
*/
Blockly.BlockSvg.ICON_HEIGHT = 10 * Blockly.BlockSvg.GRID_UNIT;
/**
* SVG start point for drawing the top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER_START =
'm ' + Blockly.BlockSvg.CORNER_RADIUS + ',0';
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER =
'A ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
'0,' + Blockly.BlockSvg.CORNER_RADIUS;
/**
* SVG start point for drawing the top-left corner.
* @const
*/
Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START =
'm ' + Blockly.BlockSvg.HAT_CORNER_RADIUS + ',0';
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
Blockly.BlockSvg.HAT_TOP_LEFT_CORNER =
'A ' + Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' +
'0,' + Blockly.BlockSvg.HAT_CORNER_RADIUS;
/**
* Play some UI effects (sound) after a connection has been established.
*/
Blockly.BlockSvg.prototype.connectionUiEffect = function() {
this.workspace.playAudio('click');
};
/**
* Change the colour of a block.
*/
Blockly.BlockSvg.prototype.updateColour = function() {
var strokeColour = this.getColourTertiary();
if (this.isShadow() && this.parentBlock_) {
// Pull shadow block stroke colour from parent block's tertiary if possible.
strokeColour = this.parentBlock_.getColourTertiary();
}
// Render block stroke
this.svgPath_.setAttribute('stroke', strokeColour);
// Render block fill
this.svgPath_.setAttribute('fill', this.getColour());
// Bump every dropdown to change its colour.
for (var x = 0, input; input = this.inputList[x]; x++) {
for (var y = 0, field; field = input.fieldRow[y]; y++) {
field.setText(null);
}
}
};
/**
* Returns a bounding box describing the dimensions of this block
* and any blocks stacked below it.
* @return {!{height: number, width: number}} Object with height and width properties.
*/
Blockly.BlockSvg.prototype.getHeightWidth = function() {
var height = this.height;
var width = this.width;
// Recursively add size of subsequent blocks.
var nextBlock = this.getNextBlock();
if (nextBlock) {
var nextHeightWidth = nextBlock.getHeightWidth();
width += nextHeightWidth.width;
height = Math.max(height, nextHeightWidth.height);
}
return {height: height, width: width};
};
/**
* Render the block.
* Lays out and reflows a block based on its contents and settings.
* @param {boolean=} opt_bubble If false, just render this block.
* If true, also render block's parent, grandparent, etc. Defaults to true.
*/
Blockly.BlockSvg.prototype.render = function(opt_bubble) {
Blockly.Field.startCache();
this.rendered = true;
var metrics = this.renderCompute_();
this.height = metrics.height;
this.width = metrics.width;
this.renderDraw_(metrics);
if (opt_bubble !== false) {
// Render all blocks above this one (propagate a reflow).
var parentBlock = this.getParent();
if (parentBlock) {
parentBlock.render(true);
} else {
// Top-most block. Fire an event to allow scrollbars to resize.
Blockly.fireUiEvent(window, 'resize');
}
}
Blockly.Field.stopCache();
};
/**
* Computes the height and widths for each row and field.
* @return {!Array.<!Array.<!Object>>} 2D array of objects, each containing
* position information.
* @private
*/
Blockly.BlockSvg.prototype.renderCompute_ = function() {
var metrics = {
statement: null,
valueInput: null,
icon: null,
width: 0,
height: 0,
bayHeight: 0,
bayWidth: 0,
fieldWidth: 0,
fieldHeight: 0,
fieldRadius: 0,
startHat: false,
endHat: false
};
if (this.nextConnection && !this.previousConnection) {
metrics.startHat = true;
}
if (this.previousConnection && !this.nextConnection) {
metrics.endHat = true;
}
// Does block have a statement?
for (var i = 0, input; input = this.inputList[i]; i++) {
if (input.type == Blockly.NEXT_STATEMENT) {
metrics.statement = input;
// Compute minimum input size.
metrics.bayHeight = Blockly.BlockSvg.MIN_BLOCK_Y;
metrics.bayWidth = Blockly.BlockSvg.MIN_BLOCK_X;
// Expand input size if there is a connection.
if (input.connection && input.connection.targetConnection) {
var linkedBlock = input.connection.targetBlock();
var bBox = linkedBlock.getHeightWidth();
metrics.bayHeight = Math.max(metrics.bayHeight, bBox.height);
metrics.bayWidth = Math.max(metrics.bayWidth, bBox.width);
}
}
// Find icon, input fields
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (field instanceof Blockly.FieldImage) {
metrics.icon = field;
}
if (field instanceof Blockly.FieldTextInput) {
var fieldBBox = field.textElement_.getBBox();
metrics.fieldWidth = fieldBBox.width + Blockly.BlockSvg.SEP_SPACE_X;
metrics.fieldHeight = fieldBBox.height;
if (field.sourceBlock_.type === 'math_number') {
metrics.fieldRadius = Blockly.BlockSvg.NUMBER_FIELD_CORNER_RADIUS;
} else {
metrics.fieldRadius = Blockly.BlockSvg.TEXT_FIELD_CORNER_RADIUS;
}
}
}
}
for (var i = 0, child; child = this.childBlocks_[i]; i++) {
if (child.isShadow()) {
metrics.valueInput = child;
}
}
// Always render icon at 40x40 px
// Normal block sizing
metrics.width = Blockly.BlockSvg.SEP_SPACE_X * 2 + Blockly.BlockSvg.ICON_WIDTH;
metrics.height = Blockly.BlockSvg.SEP_SPACE_Y * 2 + Blockly.BlockSvg.ICON_HEIGHT;
if (this.outputConnection) {
// Field shadow block
metrics.height = Blockly.BlockSvg.FIELD_HEIGHT;
metrics.width = Blockly.BlockSvg.FIELD_WIDTH;
}
if (metrics.statement) {
// Block with statement (e.g., repeat, forever)
metrics.width += metrics.bayWidth + 4 * Blockly.BlockSvg.CORNER_RADIUS + 2 * Blockly.BlockSvg.GRID_UNIT;
metrics.height = metrics.bayHeight + Blockly.BlockSvg.STATEMENT_BLOCK_SPACE;
}
if (metrics.startHat || metrics.endHat) {
// Start and end hats are 1 unit wider to account for optical effect of curve
metrics.width += 1 * Blockly.BlockSvg.GRID_UNIT;
}
return metrics;
};
/**
* Draw the path of the block.
* Move the fields to the correct locations.
* @param {number} iconWidth Offset of first row due to icons.
* @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
* containing position information.
* @private
*/
Blockly.BlockSvg.prototype.renderDraw_ = function(metrics) {
// Fetch the block's coordinates on the surface for use in anchoring
// the connections.
var connectionsXY = this.getRelativeToSurfaceXY();
// Assemble the block's path.
var steps = [];
this.renderDrawLeft_(steps, connectionsXY, metrics);
this.renderDrawBottom_(steps, connectionsXY, metrics);
this.renderDrawRight_(steps, connectionsXY, metrics);
this.renderDrawTop_(steps, connectionsXY, metrics);
var pathString = steps.join(' ');
this.svgPath_.setAttribute('d', pathString);
if (this.RTL) {
// Mirror the block's path.
// This is awesome.
this.svgPath_.setAttribute('transform', 'scale(-1 1)');
}
// Position icon
if (metrics.icon) {
var icon = metrics.icon.getSvgRoot();
var iconSize = metrics.icon.getSize();
var iconX = metrics.width - iconSize.width - Blockly.BlockSvg.SEP_SPACE_X / 1.5;
if (metrics.endHat) {
// Icon positioning bumped to the left
iconX -= 1 * Blockly.BlockSvg.GRID_UNIT;
}
icon.setAttribute('transform',
'translate(' + (iconX) + ',' +
(metrics.height - iconSize.height - Blockly.BlockSvg.SEP_SPACE_Y) + ')');
// @todo RTL
}
// Position value input
if (metrics.valueInput) {
var input = metrics.valueInput.getSvgRoot();
var inputBBox = input.getBBox();
var transformation = 'translate(' +
(Blockly.BlockSvg.NOTCH_WIDTH +
(metrics.bayWidth ? 2 * Blockly.BlockSvg.GRID_UNIT +
Blockly.BlockSvg.NOTCH_WIDTH*2 : 0) + metrics.bayWidth) + ',' +
(metrics.height - 2 * Blockly.BlockSvg.GRID_UNIT) + ')';
input.setAttribute('transform', transformation);
}
};
/**
* Render the left edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} rightEdge Minimum width of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawLeft_ =
function(steps, connectionsXY, metrics) {
// Top edge.
if (metrics.startHat) {
// Hat block
// Position the cursor at the top-left starting point.
steps.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START);
// Top-left rounded corner.
steps.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER);
} else if (this.previousConnection) {
// Regular block
// Position the cursor at the top-left starting point.
steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START);
// Top-left rounded corner.
steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER);
var cursorY = metrics.height - Blockly.BlockSvg.CORNER_RADIUS - Blockly.BlockSvg.SEP_SPACE_Y - Blockly.BlockSvg.NOTCH_HEIGHT;
steps.push('V', cursorY);
steps.push(Blockly.BlockSvg.NOTCH_PATH_DOWN);
// Create previous block connection.
var connectionX = connectionsXY.x;
var connectionY = connectionsXY.y + metrics.height - Blockly.BlockSvg.CORNER_RADIUS * 2;
this.previousConnection.moveTo(connectionX, connectionY);
// This connection will be tightened when the parent renders.
steps.push('V', metrics.height - Blockly.BlockSvg.CORNER_RADIUS);
} else {
// Input
// Position the cursor at the top-left starting point.
steps.push('m', metrics.fieldRadius + ',0');
// Top-left rounded corner.
steps.push(
'A', metrics.fieldRadius + ',' + metrics.fieldRadius,
'0', '0,0', '0,' + metrics.fieldRadius);
steps.push(
'V', metrics.height - metrics.fieldRadius);
}
};
/**
* Render the bottom edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
* containing position information.
* @param {number} iconWidth Offset of first row due to icons.
* @return {number} Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps,
connectionsXY, metrics) {
if (metrics.startHat) {
steps.push('a', Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS);
} else if (this.previousConnection) {
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS);
} else {
// Input
steps.push(
'a', metrics.fieldRadius + ',' + metrics.fieldRadius,
'0', '0,0', metrics.fieldRadius + ',' + metrics.fieldRadius);
}
// Has statement
if (metrics.statement) {
steps.push('h', 4 * Blockly.BlockSvg.GRID_UNIT);
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
steps.push('v', -2.5 * Blockly.BlockSvg.GRID_UNIT);
steps.push(Blockly.BlockSvg.NOTCH_PATH_UP);
// @todo Why 3?
steps.push('v', -metrics.bayHeight + (Blockly.BlockSvg.CORNER_RADIUS * 3) +
Blockly.BlockSvg.NOTCH_HEIGHT + 2 * Blockly.BlockSvg.GRID_UNIT);
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
steps.push('h', metrics.bayWidth - (Blockly.BlockSvg.CORNER_RADIUS * 2));
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS);
steps.push('v', metrics.bayHeight - (Blockly.BlockSvg.CORNER_RADIUS * 3) -
Blockly.BlockSvg.NOTCH_HEIGHT - 2 * Blockly.BlockSvg.GRID_UNIT);
steps.push(Blockly.BlockSvg.NOTCH_PATH_DOWN);
steps.push('v', 2.5 * Blockly.BlockSvg.GRID_UNIT);
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS);
// Create statement connection.
// @todo RTL
// var connectionX = connectionsXY.x + (this.RTL ? -cursorX : cursorX + 1);
var connectionX = connectionsXY.x + Blockly.BlockSvg.CORNER_RADIUS * 2 + 4 * Blockly.BlockSvg.GRID_UNIT;
var connectionY = connectionsXY.y + metrics.height - Blockly.BlockSvg.CORNER_RADIUS * 2;
metrics.statement.connection.moveTo(connectionX, connectionY);
if (metrics.statement.connection.targetConnection) {
metrics.statement.connection.tighten_();
}
}
if (metrics.endHat) {
steps.push('H', metrics.width - Blockly.BlockSvg.HAT_CORNER_RADIUS);
} else if (this.nextConnection) {
steps.push('H', metrics.width - Blockly.BlockSvg.CORNER_RADIUS);
} else {
// input
steps.push('H', metrics.width - metrics.fieldRadius);
}
};
/**
* Render the right edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} cursorY Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawRight_ =
function(steps, connectionsXY, metrics) {
if (metrics.endHat) {
steps.push('a', Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ',-' +
Blockly.BlockSvg.HAT_CORNER_RADIUS);
steps.push('v', -2 * Blockly.BlockSvg.GRID_UNIT);
} else if (this.nextConnection) {
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
steps.push('v', -2.5 * Blockly.BlockSvg.GRID_UNIT);
} else {
// Input
steps.push(
'a', metrics.fieldRadius + ',' + metrics.fieldRadius,
'0', '0,0', metrics.fieldRadius + ',' + -1*metrics.fieldRadius);
steps.push('v', -1*(metrics.height - metrics.fieldRadius*2));
}
if (metrics.endHat) {
steps.push('V', Blockly.BlockSvg.HAT_CORNER_RADIUS);
} else if (this.nextConnection) {
steps.push(Blockly.BlockSvg.NOTCH_PATH_UP);
// Create next block connection.
var connectionX;
if (this.RTL) {
connectionX = connectionsXY.x + metrics.width;
} else {
connectionX = connectionsXY.x + metrics.width;
}
var connectionY = connectionsXY.y + metrics.height - Blockly.BlockSvg.CORNER_RADIUS * 2;
this.nextConnection.moveTo(connectionX, connectionY);
if (this.nextConnection.targetConnection) {
this.nextConnection.tighten_();
}
steps.push('V', Blockly.BlockSvg.CORNER_RADIUS);
}
};
/**
* Render the top edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} cursorY Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawTop_ =
function(steps, connectionsXY, metrics) {
if (metrics.endHat) {
steps.push('a', Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 -' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ',-' +
Blockly.BlockSvg.HAT_CORNER_RADIUS);
} else if (this.nextConnection) {
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 -' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
} else {
steps.push(
'a', metrics.fieldRadius + ',' + metrics.fieldRadius,
'0', '0,0', '-' + metrics.fieldRadius + ',-' + metrics.fieldRadius);
}
steps.push('z');
};

View file

@ -0,0 +1,751 @@
/**
* @license
* Visual Blocks Editor
*
* Copyright 2012 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 for graphically rendering a block as SVG.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.BlockSvg.render');
goog.require('Blockly.BlockSvg');
// UI constants for rendering blocks.
/**
* Horizontal space between elements.
* @const
*/
Blockly.BlockSvg.SEP_SPACE_X = 10;
/**
* Vertical space between elements.
* @const
*/
Blockly.BlockSvg.SEP_SPACE_Y = 10;
/**
* Vertical padding around inline elements.
* @const
*/
Blockly.BlockSvg.INLINE_PADDING_Y = 5;
/**
* Minimum height of a block.
* @const
*/
Blockly.BlockSvg.MIN_BLOCK_Y = 25;
/**
* Height of horizontal puzzle tab.
* @const
*/
Blockly.BlockSvg.TAB_HEIGHT = 20;
/**
* Width of horizontal puzzle tab.
* @const
*/
Blockly.BlockSvg.TAB_WIDTH = 8;
/**
* Width of vertical tab (inc left margin).
* @const
*/
Blockly.BlockSvg.NOTCH_WIDTH = 30;
/**
* Rounded corner radius.
* @const
*/
Blockly.BlockSvg.CORNER_RADIUS = 4;
/**
* Do blocks with no previous or output connections have a 'hat' on top?
* @const
*/
Blockly.BlockSvg.START_HAT = true;
/**
* Path of the top hat's curve.
* @const
*/
Blockly.BlockSvg.START_HAT_PATH = 'c 30,-15 70,-15 100,0';
/**
* SVG path for drawing next/previous notch from left to right.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_LEFT = 'l 6,4 3,0 6,-4';
/**
* SVG path for drawing next/previous notch from right to left.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_RIGHT = 'l -6,4 -3,0 -6,-4';
/**
* SVG path for drawing a horizontal puzzle tab from top to bottom.
* @const
*/
Blockly.BlockSvg.TAB_PATH_DOWN = 'v 5 c 0,10 -' + Blockly.BlockSvg.TAB_WIDTH +
',-8 -' + Blockly.BlockSvg.TAB_WIDTH + ',7.5 s ' +
Blockly.BlockSvg.TAB_WIDTH + ',-2.5 ' + Blockly.BlockSvg.TAB_WIDTH + ',7.5';
/**
* SVG start point for drawing the top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER_START =
'm 0,' + Blockly.BlockSvg.CORNER_RADIUS;
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER =
'A ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',0';
/**
* SVG path for drawing the top-left corner of a statement input.
* Includes the top notch, a horizontal space, and the rounded inside corner.
* @const
*/
Blockly.BlockSvg.INNER_TOP_LEFT_CORNER =
Blockly.BlockSvg.NOTCH_PATH_RIGHT + ' h -' +
(Blockly.BlockSvg.NOTCH_WIDTH - 15 - Blockly.BlockSvg.CORNER_RADIUS) +
' a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 -' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS;
/**
* SVG path for drawing the bottom-left corner of a statement input.
* Includes the rounded inside corner.
* @const
*/
Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER =
'a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS;
/**
* Play some UI effects (sound, ripple) after a connection has been established.
*/
Blockly.BlockSvg.prototype.connectionUiEffect = function() {
this.workspace.playAudio('click');
if (this.workspace.scale < 1) {
return; // Too small to care about visual effects.
}
// Determine the absolute coordinates of the inferior block.
var xy = Blockly.getSvgXY_(/** @type {!Element} */ (this.svgGroup_),
this.workspace);
// 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.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;
var closure = function() {
Blockly.BlockSvg.connectionUiStep_(ripple, start, workspaceScale);
};
Blockly.BlockSvg.disconnectUiStop_.pid_ = setTimeout(closure, 10);
}
};
/**
* Change the colour of a block.
*/
Blockly.BlockSvg.prototype.updateColour = function() {
// Render block fill
var hexColour = this.getColour();
var rgb = goog.color.hexToRgb(hexColour);
if (this.isShadow()) {
rgb = goog.color.lighten(rgb, 0.6);
hexColour = goog.color.rgbArrayToHex(rgb);
}
this.svgPath_.setAttribute('fill', hexColour);
// Render block stroke
var colorShift = goog.color.darken(rgb, 0.1);
var strokeColor = goog.color.rgbArrayToHex(colorShift);
this.svgPath_.setAttribute('stroke', strokeColor);
// Render icon(s) if applicable
var icons = this.getIcons();
for (var i = 0; i < icons.length; i++) {
icons[i].updateColour();
}
// Bump every dropdown to change its colour.
for (var x = 0, input; input = this.inputList[x]; x++) {
for (var y = 0, field; field = input.fieldRow[y]; y++) {
field.setText(null);
}
}
};
/**
* Returns a bounding box describing the dimensions of this block
* and any blocks stacked below it.
* @return {!{height: number, width: number}} Object with height and width properties.
*/
Blockly.BlockSvg.prototype.getHeightWidth = function() {
var height = this.height;
var width = this.width;
// Recursively add size of subsequent blocks.
var nextBlock = this.getNextBlock();
if (nextBlock) {
var nextHeightWidth = nextBlock.getHeightWidth();
height += nextHeightWidth.height - 4; // Height of tab.
width = Math.max(width, nextHeightWidth.width);
} else if (!this.nextConnection && !this.outputConnection) {
// Add a bit of margin under blocks with no bottom tab.
height += 2;
}
return {height: height, width: width};
};
/**
* Render the block.
* Lays out and reflows a block based on its contents and settings.
* @param {boolean=} opt_bubble If false, just render this block.
* If true, also render block's parent, grandparent, etc. Defaults to true.
*/
Blockly.BlockSvg.prototype.render = function(opt_bubble) {
Blockly.Field.startCache();
this.rendered = true;
var cursorX = Blockly.BlockSvg.SEP_SPACE_X;
if (this.RTL) {
cursorX = -cursorX;
}
// Move the icons into position.
var icons = this.getIcons();
for (var i = 0; i < icons.length; i++) {
cursorX = icons[i].renderIcon(cursorX);
}
cursorX += this.RTL ?
Blockly.BlockSvg.SEP_SPACE_X : -Blockly.BlockSvg.SEP_SPACE_X;
// If there are no icons, cursorX will be 0, otherwise it will be the
// width that the first label needs to move over by.
var inputRows = this.renderCompute_(cursorX);
this.renderDraw_(cursorX, inputRows);
if (opt_bubble !== false) {
// Render all blocks above this one (propagate a reflow).
var parentBlock = this.getParent();
if (parentBlock) {
parentBlock.render(true);
} else {
// Top-most block. Fire an event to allow scrollbars to resize.
Blockly.fireUiEvent(window, 'resize');
}
}
Blockly.Field.stopCache();
};
/**
* Render a list of fields starting at the specified location.
* @param {!Array.<!Blockly.Field>} fieldList List of fields.
* @param {number} cursorX X-coordinate to start the fields.
* @param {number} cursorY Y-coordinate to start the fields.
* @return {number} X-coordinate of the end of the field row (plus a gap).
* @private
*/
Blockly.BlockSvg.prototype.renderFields_ =
function(fieldList, cursorX, cursorY) {
cursorY += Blockly.BlockSvg.INLINE_PADDING_Y;
if (this.RTL) {
cursorX = -cursorX;
}
for (var t = 0, field; field = fieldList[t]; t++) {
var root = field.getSvgRoot();
if (!root) {
continue;
}
if (this.RTL) {
cursorX -= field.renderSep + field.renderWidth;
root.setAttribute('transform',
'translate(' + cursorX + ',' + cursorY + ')');
if (field.renderWidth) {
cursorX -= Blockly.BlockSvg.SEP_SPACE_X;
}
} else {
root.setAttribute('transform',
'translate(' + (cursorX + field.renderSep) + ',' + cursorY + ')');
if (field.renderWidth) {
cursorX += field.renderSep + field.renderWidth +
Blockly.BlockSvg.SEP_SPACE_X;
}
}
}
return this.RTL ? -cursorX : cursorX;
};
/**
* Computes the height and widths for each row and field.
* @param {number} iconWidth Offset of first row due to icons.
* @return {!Array.<!Array.<!Object>>} 2D array of objects, each containing
* position information.
* @private
*/
Blockly.BlockSvg.prototype.renderCompute_ = function(iconWidth) {
var inputList = this.inputList;
var inputRows = [];
inputRows.rightEdge = iconWidth + Blockly.BlockSvg.SEP_SPACE_X * 2;
if (this.previousConnection || this.nextConnection) {
inputRows.rightEdge = Math.max(inputRows.rightEdge,
Blockly.BlockSvg.NOTCH_WIDTH + Blockly.BlockSvg.SEP_SPACE_X);
}
var fieldValueWidth = 0; // Width of longest external value field.
var fieldStatementWidth = 0; // Width of longest statement field.
var hasValue = false;
var hasStatement = false;
var hasDummy = false;
var lastType = undefined;
for (var i = 0, input; input = inputList[i]; i++) {
if (!input.isVisible()) {
continue;
}
var row;
if (!lastType ||
lastType == Blockly.NEXT_STATEMENT ||
input.type == Blockly.NEXT_STATEMENT) {
// Create new row.
lastType = input.type;
row = [];
if (input.type != Blockly.NEXT_STATEMENT) {
row.type = Blockly.BlockSvg.INLINE;
} else {
row.type = input.type;
}
row.height = 0;
inputRows.push(row);
} else {
row = inputRows[inputRows.length - 1];
}
row.push(input);
// Compute minimum input size.
input.renderHeight = Blockly.BlockSvg.MIN_BLOCK_Y;
// The width is currently only needed for inline value inputs.
if (input.type == Blockly.INPUT_VALUE) {
input.renderWidth = Blockly.BlockSvg.TAB_WIDTH +
Blockly.BlockSvg.SEP_SPACE_X * 1.25;
} else {
input.renderWidth = 0;
}
// Expand input size if there is a connection.
if (input.connection && input.connection.targetConnection) {
var linkedBlock = input.connection.targetBlock();
var bBox = linkedBlock.getHeightWidth();
input.renderHeight = Math.max(input.renderHeight, bBox.height);
input.renderWidth = Math.max(input.renderWidth, bBox.width);
}
// Blocks have a one pixel shadow that should sometimes overhang.
if (i == inputList.length - 1) {
// Last value input should overhang.
input.renderHeight--;
} else if (input.type == Blockly.INPUT_VALUE &&
inputList[i + 1] && inputList[i + 1].type == Blockly.NEXT_STATEMENT) {
// Value input above statement input should overhang.
input.renderHeight--;
}
row.height = Math.max(row.height, input.renderHeight);
input.fieldWidth = 0;
if (inputRows.length == 1) {
// The first row gets shifted to accommodate any icons.
input.fieldWidth += this.RTL ? -iconWidth : iconWidth;
}
var previousFieldEditable = false;
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (j != 0) {
input.fieldWidth += Blockly.BlockSvg.SEP_SPACE_X;
}
// Get the dimensions of the field.
var fieldSize = field.getSize();
field.renderWidth = fieldSize.width;
field.renderSep = (previousFieldEditable && field.EDITABLE) ?
Blockly.BlockSvg.SEP_SPACE_X : 0;
input.fieldWidth += field.renderWidth + field.renderSep;
row.height = Math.max(row.height, fieldSize.height);
previousFieldEditable = field.EDITABLE;
}
if (row.type != Blockly.BlockSvg.INLINE) {
if (row.type == Blockly.NEXT_STATEMENT) {
hasStatement = true;
fieldStatementWidth = Math.max(fieldStatementWidth, input.fieldWidth);
} else {
if (row.type == Blockly.INPUT_VALUE) {
hasValue = true;
} else if (row.type == Blockly.DUMMY_INPUT) {
hasDummy = true;
}
fieldValueWidth = Math.max(fieldValueWidth, input.fieldWidth);
}
}
}
// Make inline rows a bit thicker in order to enclose the values.
for (var y = 0, row; row = inputRows[y]; y++) {
row.thicker = false;
if (row.type == Blockly.BlockSvg.INLINE) {
for (var z = 0, input; input = row[z]; z++) {
if (input.type == Blockly.INPUT_VALUE) {
row.height += 2 * Blockly.BlockSvg.INLINE_PADDING_Y;
row.thicker = true;
break;
}
}
}
}
// Compute the statement edge.
// This is the width of a block where statements are nested.
inputRows.statementEdge = 2 * Blockly.BlockSvg.SEP_SPACE_X +
fieldStatementWidth;
// Compute the preferred right edge. Inline blocks may extend beyond.
// This is the width of the block where external inputs connect.
if (hasStatement) {
inputRows.rightEdge = Math.max(inputRows.rightEdge,
inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH);
}
if (hasValue) {
inputRows.rightEdge = Math.max(inputRows.rightEdge, fieldValueWidth +
Blockly.BlockSvg.SEP_SPACE_X * 2 + Blockly.BlockSvg.TAB_WIDTH);
} else if (hasDummy) {
inputRows.rightEdge = Math.max(inputRows.rightEdge, fieldValueWidth +
Blockly.BlockSvg.SEP_SPACE_X * 2);
}
inputRows.hasValue = hasValue;
inputRows.hasStatement = hasStatement;
inputRows.hasDummy = hasDummy;
return inputRows;
};
/**
* Draw the path of the block.
* Move the fields to the correct locations.
* @param {number} iconWidth Offset of first row due to icons.
* @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
* containing position information.
* @private
*/
Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) {
this.startHat_ = false;
// Should the top and bottom left corners be rounded or square?
if (this.outputConnection) {
this.squareTopLeftCorner_ = true;
this.squareBottomLeftCorner_ = true;
} else {
this.squareTopLeftCorner_ = false;
this.squareBottomLeftCorner_ = false;
// If this block is in the middle of a stack, square the corners.
if (this.previousConnection) {
var prevBlock = this.previousConnection.targetBlock();
if (prevBlock && prevBlock.getNextBlock() == this) {
this.squareTopLeftCorner_ = true;
}
} else if (Blockly.BlockSvg.START_HAT) {
// No output or previous connection.
this.squareTopLeftCorner_ = true;
this.startHat_ = true;
inputRows.rightEdge = Math.max(inputRows.rightEdge, 100);
}
var nextBlock = this.getNextBlock();
if (nextBlock) {
this.squareBottomLeftCorner_ = true;
}
}
// Fetch the block's coordinates on the surface for use in anchoring
// the connections.
var connectionsXY = this.getRelativeToSurfaceXY();
// Assemble the block's path.
var steps = [];
this.renderDrawTop_(steps, connectionsXY,
inputRows.rightEdge);
var cursorY = this.renderDrawRight_(steps,
connectionsXY, inputRows, iconWidth);
this.renderDrawBottom_(steps, connectionsXY, cursorY);
this.renderDrawLeft_(steps, connectionsXY, cursorY);
var pathString = steps.join(' ');
this.svgPath_.setAttribute('d', pathString);
if (this.RTL) {
// Mirror the block's path.
// This is awesome.
this.svgPath_.setAttribute('transform', 'scale(-1 1)');
}
};
/**
* Render the top edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} rightEdge Minimum width of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawTop_ =
function(steps, connectionsXY, rightEdge) {
// Position the cursor at the top-left starting point.
if (this.squareTopLeftCorner_) {
steps.push('m 0,0');
if (this.startHat_) {
steps.push(Blockly.BlockSvg.START_HAT_PATH);
}
} else {
steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START);
// Top-left rounded corner.
steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER);
}
// Top edge.
if (this.previousConnection) {
steps.push('H', Blockly.BlockSvg.NOTCH_WIDTH - 15);
steps.push(Blockly.BlockSvg.NOTCH_PATH_LEFT);
// Create previous block connection.
var connectionX = connectionsXY.x + (this.RTL ?
-Blockly.BlockSvg.NOTCH_WIDTH : Blockly.BlockSvg.NOTCH_WIDTH);
var connectionY = connectionsXY.y;
this.previousConnection.moveTo(connectionX, connectionY);
// This connection will be tightened when the parent renders.
}
steps.push('H', rightEdge);
this.width = rightEdge;
};
/**
* Render the right edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
* containing position information.
* @param {number} iconWidth Offset of first row due to icons.
* @return {number} Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps,
connectionsXY, inputRows, iconWidth) {
var cursorX;
var cursorY = 0;
var connectionX, connectionY;
for (var y = 0, row; row = inputRows[y]; y++) {
cursorX = Blockly.BlockSvg.SEP_SPACE_X;
if (y == 0) {
cursorX += this.RTL ? -iconWidth : iconWidth;
}
if (row.type == Blockly.BlockSvg.INLINE) {
// Inline inputs.
for (var x = 0, input; input = row[x]; x++) {
var fieldX = cursorX;
var fieldY = cursorY;
if (row.thicker) {
// Lower the field slightly.
fieldY += Blockly.BlockSvg.INLINE_PADDING_Y;
}
// TODO: Align inline field rows (left/right/centre).
cursorX = this.renderFields_(input.fieldRow, fieldX, fieldY);
if (input.type != Blockly.DUMMY_INPUT) {
cursorX += input.renderWidth + Blockly.BlockSvg.SEP_SPACE_X;
}
if (input.type == Blockly.INPUT_VALUE) {
// Create inline input connection.
if (this.RTL) {
connectionX = connectionsXY.x - cursorX -
Blockly.BlockSvg.TAB_WIDTH + Blockly.BlockSvg.SEP_SPACE_X +
input.renderWidth + 1;
} else {
connectionX = connectionsXY.x + cursorX +
Blockly.BlockSvg.TAB_WIDTH - Blockly.BlockSvg.SEP_SPACE_X -
input.renderWidth - 1;
}
connectionY = connectionsXY.y + cursorY +
Blockly.BlockSvg.INLINE_PADDING_Y + 1;
input.connection.moveTo(connectionX, connectionY);
if (input.connection.targetConnection) {
input.connection.tighten_();
}
}
}
cursorX = Math.max(cursorX, inputRows.rightEdge);
this.width = Math.max(this.width, cursorX);
steps.push('H', cursorX);
steps.push('v', row.height);
} else if (row.type == Blockly.DUMMY_INPUT) {
// External naked field.
var input = row[0];
var fieldX = cursorX;
var fieldY = cursorY;
if (input.align != Blockly.ALIGN_LEFT) {
var fieldRightX = inputRows.rightEdge - input.fieldWidth -
2 * Blockly.BlockSvg.SEP_SPACE_X;
if (inputRows.hasValue) {
fieldRightX -= Blockly.BlockSvg.TAB_WIDTH;
}
if (input.align == Blockly.ALIGN_RIGHT) {
fieldX += fieldRightX;
} else if (input.align == Blockly.ALIGN_CENTRE) {
fieldX += fieldRightX / 2;
}
}
this.renderFields_(input.fieldRow, fieldX, fieldY);
steps.push('v', row.height);
} else if (row.type == Blockly.NEXT_STATEMENT) {
// Nested statement.
var input = row[0];
if (y == 0) {
// If the first input is a statement stack, add a small row on top.
steps.push('v', Blockly.BlockSvg.SEP_SPACE_Y);
cursorY += Blockly.BlockSvg.SEP_SPACE_Y;
}
var fieldX = cursorX;
var fieldY = cursorY;
if (input.align != Blockly.ALIGN_LEFT) {
var fieldRightX = inputRows.statementEdge - input.fieldWidth -
2 * Blockly.BlockSvg.SEP_SPACE_X;
if (input.align == Blockly.ALIGN_RIGHT) {
fieldX += fieldRightX;
} else if (input.align == Blockly.ALIGN_CENTRE) {
fieldX += fieldRightX / 2;
}
}
this.renderFields_(input.fieldRow, fieldX, fieldY);
cursorX = inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH;
steps.push('H', cursorX);
steps.push(Blockly.BlockSvg.INNER_TOP_LEFT_CORNER);
steps.push('v', row.height - 2 * Blockly.BlockSvg.CORNER_RADIUS);
steps.push(Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER);
steps.push('H', inputRows.rightEdge);
// Create statement connection.
connectionX = connectionsXY.x + (this.RTL ? -cursorX : cursorX + 1);
connectionY = connectionsXY.y + cursorY + 1;
input.connection.moveTo(connectionX, connectionY);
if (input.connection.targetConnection) {
input.connection.tighten_();
this.width = Math.max(this.width, inputRows.statementEdge +
input.connection.targetBlock().getHeightWidth().width);
}
if (y == inputRows.length - 1 ||
inputRows[y + 1].type == Blockly.NEXT_STATEMENT) {
// If the final input is a statement stack, add a small row underneath.
// Consecutive statement stacks are also separated by a small divider.
steps.push('v', Blockly.BlockSvg.SEP_SPACE_Y);
cursorY += Blockly.BlockSvg.SEP_SPACE_Y;
}
}
cursorY += row.height;
}
if (!inputRows.length) {
cursorY = Blockly.BlockSvg.MIN_BLOCK_Y;
steps.push('V', cursorY);
}
return cursorY;
};
/**
* Render the bottom edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} cursorY Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawBottom_ =
function(steps, connectionsXY, cursorY) {
this.height = cursorY + 1; // Add one for the shadow.
if (this.nextConnection) {
steps.push('H', (Blockly.BlockSvg.NOTCH_WIDTH + (this.RTL ? 0.5 : - 0.5)) +
' ' + Blockly.BlockSvg.NOTCH_PATH_RIGHT);
// Create next block connection.
var connectionX;
if (this.RTL) {
connectionX = connectionsXY.x - Blockly.BlockSvg.NOTCH_WIDTH;
} else {
connectionX = connectionsXY.x + Blockly.BlockSvg.NOTCH_WIDTH;
}
var connectionY = connectionsXY.y + cursorY + 1;
this.nextConnection.moveTo(connectionX, connectionY);
if (this.nextConnection.targetConnection) {
this.nextConnection.tighten_();
}
this.height += 4; // Height of tab.
}
// Should the bottom-left corner be rounded or square?
if (this.squareBottomLeftCorner_) {
steps.push('H 0');
} else {
steps.push('H', Blockly.BlockSvg.CORNER_RADIUS);
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 -' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
}
};
/**
* Render the left edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} cursorY Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawLeft_ =
function(steps, connectionsXY, cursorY) {
if (this.outputConnection) {
// Create output connection.
this.outputConnection.moveTo(connectionsXY.x, connectionsXY.y);
// This connection will be tightened when the parent renders.
if (this.outputConnection.getOutputShape() === Blockly.Connection.NUMBER) {
steps.push('V', Blockly.BlockSvg.TAB_HEIGHT);
steps.push('c 0,-10 -' + Blockly.BlockSvg.TAB_WIDTH + ',8 -' +
Blockly.BlockSvg.TAB_WIDTH + ',-7.5 s ' + Blockly.BlockSvg.TAB_WIDTH +
',2.5 ' + Blockly.BlockSvg.TAB_WIDTH + ',-7.5');
this.width += Blockly.BlockSvg.TAB_WIDTH;
}
}
steps.push('z');
};

View file

@ -189,7 +189,6 @@ Blockly.BlockSvg.onMouseMoveWrapper_ = null;
* @private
*/
Blockly.BlockSvg.terminateDrag_ = function() {
Blockly.BlockSvg.disconnectUiStop_();
if (Blockly.BlockSvg.onMouseUpWrapper_) {
Blockly.unbindEvent_(Blockly.BlockSvg.onMouseUpWrapper_);
Blockly.BlockSvg.onMouseUpWrapper_ = null;
@ -325,27 +324,6 @@ Blockly.BlockSvg.prototype.snapToGrid = function() {
}
};
/**
* Returns a bounding box describing the dimensions of this block
* and any blocks stacked below it.
* @return {!{height: number, width: number}} Object with height and width properties.
*/
Blockly.BlockSvg.prototype.getHeightWidth = function() {
var height = this.height;
var width = this.width;
// Recursively add size of subsequent blocks.
var nextBlock = this.getNextBlock();
if (nextBlock) {
var nextHeightWidth = nextBlock.getHeightWidth();
width += nextHeightWidth.width;
height = Math.max(height, nextHeightWidth.height);
} else if (!this.nextConnection && !this.outputConnection) {
// Add a bit of margin under blocks with no bottom tab.
height += 2;
}
return {height: height, width: width};
};
/**
* Open the next (or previous) FieldTextInput.
* @param {Blockly.Field|Blockly.Block} start Current location.
@ -451,6 +429,11 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
* @private
*/
Blockly.BlockSvg.prototype.onMouseUp_ = function(e) {
if (Blockly.dragMode_ == 1 && Blockly.selected) {
// 1 - Still inside the sticky DRAG_RADIUS.
// Trigger a tap
this.workspace.fireTapListener(Blockly.selected.id, Blockly.selected.getRootBlock().id);
}
Blockly.terminateDrag_();
if (Blockly.selected && Blockly.highlightedConnection_) {
// Connect two blocks together.
@ -802,149 +785,6 @@ Blockly.BlockSvg.prototype.getSvgRoot = function() {
return this.svgGroup_;
};
// UI constants for rendering blocks.
/**
* Horizontal space between elements.
* @const
*/
Blockly.BlockSvg.SEP_SPACE_X = 8;
/**
* Vertical space between elements.
* @const
*/
Blockly.BlockSvg.SEP_SPACE_Y = 8;
/**
* Vertical padding around inline elements.
* @const
*/
Blockly.BlockSvg.INLINE_PADDING_Y = 5;
/**
* Minimum height of a block.
* @const
*/
Blockly.BlockSvg.MIN_BLOCK_Y = 25;
/**
* Width of horizontal puzzle tab.
* @const
*/
Blockly.BlockSvg.TAB_WIDTH = 8;
/**
* Rounded corner radius.
* @const
*/
Blockly.BlockSvg.CORNER_RADIUS = 4;
/**
* Rounded corner radius.
* @const
*/
Blockly.BlockSvg.HAT_CORNER_RADIUS = 16;
/**
* Rounded notch radius.
* @const
*/
Blockly.BlockSvg.NOTCH_RADIUS = 2.5;
/**
* Height of connector notch, not including rounded corner at top and bottom.
* @const
*/
Blockly.BlockSvg.NOTCH_BASE_HEIGHT = 32;
/**
* Full height of connector notch including rounded corner.
* @const
*/
Blockly.BlockSvg.NOTCH_HEIGHT = Blockly.BlockSvg.NOTCH_BASE_HEIGHT + Blockly.BlockSvg.NOTCH_RADIUS;
/**
* Width of connector notch
* @const
*/
Blockly.BlockSvg.NOTCH_WIDTH = Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4;
/**
* SVG path for drawing next/previous notch from top to bottom.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_DOWN =
'a ' + -Blockly.BlockSvg.NOTCH_RADIUS + ',' + Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 0 ' + Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'l ' + (Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 - Blockly.BlockSvg.NOTCH_RADIUS) + ',' +
(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 - Blockly.BlockSvg.NOTCH_RADIUS) + ' ' +
'a ' + -Blockly.BlockSvg.NOTCH_RADIUS + ',' + Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 1 ' + Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'v ' + (Blockly.BlockSvg.NOTCH_BASE_HEIGHT/2 - Blockly.BlockSvg.NOTCH_RADIUS) + ' ' +
'a ' + -Blockly.BlockSvg.NOTCH_RADIUS + ',' + Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 1 ' + -Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'l ' + (-Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 + Blockly.BlockSvg.NOTCH_RADIUS) + ',' +
(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 - Blockly.BlockSvg.NOTCH_RADIUS) + ' ' +
'a ' + -Blockly.BlockSvg.NOTCH_RADIUS + ',' + Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 0 ' + -Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + Blockly.BlockSvg.NOTCH_RADIUS;
/**
* SVG path for drawing next/previous notch from bottom to top.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_UP =
'a ' + Blockly.BlockSvg.NOTCH_RADIUS + ',' + -Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 1 ' + Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + -Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'l ' + (Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 - Blockly.BlockSvg.NOTCH_RADIUS) + ',' +
-1*(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 - Blockly.BlockSvg.NOTCH_RADIUS) + ' ' +
'a ' + Blockly.BlockSvg.NOTCH_RADIUS + ',' + -Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 0 ' + Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + -Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'v ' + -1*(Blockly.BlockSvg.NOTCH_BASE_HEIGHT/2 - Blockly.BlockSvg.NOTCH_RADIUS) + ' ' +
'a ' + Blockly.BlockSvg.NOTCH_RADIUS + ',' + -Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 0 ' + -Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + -Blockly.BlockSvg.NOTCH_RADIUS +
'l ' + (-Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 + Blockly.BlockSvg.NOTCH_RADIUS) + ',' +
(-Blockly.BlockSvg.NOTCH_BASE_HEIGHT/4 + Blockly.BlockSvg.NOTCH_RADIUS) + ' ' +
'a ' + Blockly.BlockSvg.NOTCH_RADIUS + ',' + -Blockly.BlockSvg.NOTCH_RADIUS + ' ' +
'0 0 1 ' + -Blockly.BlockSvg.NOTCH_RADIUS/2 + ',' + -Blockly.BlockSvg.NOTCH_RADIUS;
/**
* SVG start point for drawing the top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER_START =
'm ' + Blockly.BlockSvg.CORNER_RADIUS + ',0';
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER =
'A ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
'0,' + Blockly.BlockSvg.CORNER_RADIUS;
/**
* SVG start point for drawing the top-left corner.
* @const
*/
Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START =
'm ' + Blockly.BlockSvg.HAT_CORNER_RADIUS + ',0';
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
Blockly.BlockSvg.HAT_TOP_LEFT_CORNER =
'A ' + Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' +
'0,' + Blockly.BlockSvg.HAT_CORNER_RADIUS;
/**
* SVG path for drawing the top-left corner of a statement input.
* Includes the top notch, a horizontal space, and the rounded inside corner.
* @const
*/
Blockly.BlockSvg.INNER_TOP_LEFT_CORNER =
Blockly.BlockSvg.NOTCH_PATH_UP + ' h -' +
(Blockly.BlockSvg.NOTCH_HEIGHT - 15 - Blockly.BlockSvg.CORNER_RADIUS) +
' h -0.5 a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 -' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS;
/**
* SVG path for drawing the bottom-left corner of a statement input.
* Includes the rounded inside corner.
* @const
*/
Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER =
'a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS;
/**
* Dispose of this block.
* @param {boolean} healStack If true, then try to heal any gap by connecting
@ -1036,150 +876,10 @@ Blockly.BlockSvg.disposeUiStep_ = function(clone, rtl, start, workspaceScale) {
};
/**
* Play some UI effects (sound, ripple) after a connection has been established.
*/
Blockly.BlockSvg.prototype.connectionUiEffect = function() {
this.workspace.playAudio('click');
if (this.workspace.scale < 1) {
return; // Too small to care about visual effects.
}
// Determine the absolute coordinates of the inferior block.
var xy = Blockly.getSvgXY_(/** @type {!Element} */ (this.svgGroup_),
this.workspace);
// Offset the coordinates based on the two connection types, fix scale.
xy.x += 8 * this.workspace.scale;
xy.y += this.height - (Blockly.BlockSvg.CORNER_RADIUS * 2 + Blockly.BlockSvg.NOTCH_HEIGHT / 2) - 8 * this.workspace.scale;
var ripple = Blockly.createSvgElement('circle',
{'cx': xy.x, 'cy': xy.y, 'r': 0, 'fill': 'none',
'stroke': '#EEE', 'stroke-width': 8},
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 = 0.8 - percent;
var closure = function() {
Blockly.BlockSvg.connectionUiStep_(ripple, start, workspaceScale);
};
Blockly.BlockSvg.disconnectUiStop_.pid_ = setTimeout(closure, 10);
}
};
/**
* Play some UI effects (sound, animation) when disconnecting a block.
* Play some UI effects (sound) when disconnecting a block.
*/
Blockly.BlockSvg.prototype.disconnectUiEffect = function() {
this.workspace.playAudio('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 + ')';
var closure = function() {
Blockly.BlockSvg.disconnectUiStep_(group, magnitude, start);
};
Blockly.BlockSvg.disconnectUiStop_.group = group;
Blockly.BlockSvg.disconnectUiStop_.pid = setTimeout(closure, 10);
}
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.
*/
Blockly.BlockSvg.prototype.updateColour = function() {
// Render block fill
var hexColour = this.getColour();
var rgb = goog.color.hexToRgb(hexColour);
if (this.isShadow()) {
rgb = goog.color.lighten(rgb, 0.6);
hexColour = goog.color.rgbArrayToHex(rgb);
}
this.svgPath_.setAttribute('fill', hexColour);
// Render block stroke
var colorShift = goog.color.darken(rgb, 0.1);
var strokeColor = goog.color.rgbArrayToHex(colorShift);
this.svgPath_.setAttribute('stroke', strokeColor);
// Bump every dropdown to change its colour.
for (var x = 0, input; input = this.inputList[x]; x++) {
for (var y = 0, field; field = input.fieldRow[y]; y++) {
field.setText(null);
}
}
};
/**
@ -1345,303 +1045,3 @@ Blockly.BlockSvg.prototype.removeDragging = function() {
Blockly.removeClass_(/** @type {!Element} */ (this.svgGroup_),
'blocklyDragging');
};
/**
* Render the block.
* Lays out and reflows a block based on its contents and settings.
* @param {boolean=} opt_bubble If false, just render this block.
* If true, also render block's parent, grandparent, etc. Defaults to true.
*/
Blockly.BlockSvg.prototype.render = function(opt_bubble) {
Blockly.Field.startCache();
this.rendered = true;
var metrics = this.renderCompute_();
this.height = metrics.height;
this.width = metrics.width;
this.renderDraw_(metrics);
if (opt_bubble !== false) {
// Render all blocks above this one (propagate a reflow).
var parentBlock = this.getParent();
if (parentBlock) {
parentBlock.render(true);
} else {
// Top-most block. Fire an event to allow scrollbars to resize.
Blockly.fireUiEvent(window, 'resize');
}
}
Blockly.Field.stopCache();
};
/**
* Computes the height and widths for each row and field.
* @param {number} iconWidth Offset of first row due to icons.
* @return {!Array.<!Array.<!Object>>} 2D array of objects, each containing
* position information.
* @private
*/
Blockly.BlockSvg.prototype.renderCompute_ = function() {
var metrics = {
statement: null,
icon: null,
width: 0,
height: 0,
bayHeight: 0,
bayWidth: 0
};
// Does block have a statement?
for (var i = 0, input; input = this.inputList[i]; i++) {
if (input.type == Blockly.NEXT_STATEMENT) {
metrics.statement = input;
// Compute minimum input size.
// @todo Why 3?
metrics.bayHeight = Blockly.BlockSvg.NOTCH_HEIGHT + 16 +
Blockly.BlockSvg.CORNER_RADIUS * 3;
metrics.bayWidth = Blockly.BlockSvg.NOTCH_WIDTH * 2 +
Blockly.BlockSvg.SEP_SPACE_X;
// Expand input size if there is a connection.
if (input.connection && input.connection.targetConnection) {
var linkedBlock = input.connection.targetBlock();
var bBox = linkedBlock.getHeightWidth();
metrics.bayHeight = Math.max(metrics.bayHeight, bBox.height);
metrics.bayWidth = Math.max(metrics.bayWidth, bBox.width);
}
}
// Find icon
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (field instanceof Blockly.FieldImage) {
metrics.icon = field;
}
}
}
var iconSize = (metrics.icon) ? metrics.icon.getSize() : new goog.math.Size(0,0);
metrics.width =
Blockly.BlockSvg.SEP_SPACE_X * 2 + iconSize.width + metrics.bayWidth;
if (metrics.statement) {
metrics.width += 2 * Blockly.BlockSvg.CORNER_RADIUS + 8;
}
metrics.height = Math.max(
Blockly.BlockSvg.SEP_SPACE_Y * 2 + iconSize.height,
Blockly.BlockSvg.NOTCH_HEIGHT + 16 + Blockly.BlockSvg.CORNER_RADIUS * 2,
metrics.bayHeight + Blockly.BlockSvg.SEP_SPACE_Y
);
return metrics;
};
/**
* Draw the path of the block.
* Move the fields to the correct locations.
* @param {number} iconWidth Offset of first row due to icons.
* @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
* containing position information.
* @private
*/
Blockly.BlockSvg.prototype.renderDraw_ = function(metrics) {
// Fetch the block's coordinates on the surface for use in anchoring
// the connections.
var connectionsXY = this.getRelativeToSurfaceXY();
// Assemble the block's path.
var steps = [];
this.renderDrawLeft_(steps, connectionsXY, metrics);
this.renderDrawBottom_(steps, connectionsXY, metrics);
this.renderDrawRight_(steps, connectionsXY, metrics);
this.renderDrawTop_(steps, connectionsXY, metrics);
var pathString = steps.join(' ');
this.svgPath_.setAttribute('d', pathString);
if (this.RTL) {
// Mirror the block's path.
// This is awesome.
this.svgPath_.setAttribute('transform', 'scale(-1 1)');
}
// Position icon
if (!this.isGhost_ && metrics.icon && metrics.icon.getSvgRoot()) {
var icon = metrics.icon.getSvgRoot();
var iconSize = metrics.icon.getSize();
icon.setAttribute('transform',
'translate(' + (metrics.width - iconSize.width - Blockly.BlockSvg.SEP_SPACE_X / 2) + ',' +
(metrics.height - iconSize.height - Blockly.BlockSvg.SEP_SPACE_Y) + ')');
// @todo RTL
}
};
/**
* Render the left edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} rightEdge Minimum width of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawLeft_ =
function(steps, connectionsXY, metrics) {
// Top edge.
if (this.previousConnection) {
// Position the cursor at the top-left starting point.
steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START);
// Top-left rounded corner.
steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER);
var cursorY = metrics.height - Blockly.BlockSvg.CORNER_RADIUS - 8 - Blockly.BlockSvg.NOTCH_HEIGHT;
steps.push('V', cursorY);
steps.push(Blockly.BlockSvg.NOTCH_PATH_DOWN);
// Create previous block connection.
var connectionX = connectionsXY.x;
var connectionY = connectionsXY.y + metrics.height - Blockly.BlockSvg.CORNER_RADIUS * 2;
this.previousConnection.moveTo(connectionX, connectionY);
// This connection will be tightened when the parent renders.
steps.push('V', metrics.height - Blockly.BlockSvg.CORNER_RADIUS);
} else {
// Position the cursor at the top-left starting point.
steps.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START);
// Top-left rounded corner.
steps.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER);
steps.push('V', metrics.height - Blockly.BlockSvg.HAT_CORNER_RADIUS);
}
};
/**
* Render the bottom edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
* containing position information.
* @param {number} iconWidth Offset of first row due to icons.
* @return {number} Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps,
connectionsXY, metrics) {
if (this.previousConnection) {
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS);
} else {
steps.push('a', Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS);
}
// Has statement
if (metrics.statement) {
steps.push('h', 8);
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
steps.push('v', -8);
steps.push(Blockly.BlockSvg.NOTCH_PATH_UP);
// @todo Why 3?
steps.push('v', -metrics.bayHeight + (Blockly.BlockSvg.CORNER_RADIUS * 3) + Blockly.BlockSvg.NOTCH_HEIGHT + 8);
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
steps.push('h', metrics.bayWidth - (Blockly.BlockSvg.CORNER_RADIUS * 2));
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS);
steps.push('v', metrics.bayHeight - (Blockly.BlockSvg.CORNER_RADIUS * 3) - Blockly.BlockSvg.NOTCH_HEIGHT - 8);
steps.push(Blockly.BlockSvg.NOTCH_PATH_DOWN);
steps.push('v', 8);
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS);
// Create statement connection.
// @todo RTL
// var connectionX = connectionsXY.x + (this.RTL ? -cursorX : cursorX + 1);
var connectionX = connectionsXY.x + Blockly.BlockSvg.CORNER_RADIUS * 2 + 8;
var connectionY = connectionsXY.y + metrics.height - Blockly.BlockSvg.CORNER_RADIUS * 2;
metrics.statement.connection.moveTo(connectionX, connectionY);
if (metrics.statement.connection.targetConnection) {
metrics.statement.connection.tighten_();
}
}
if (this.nextConnection) {
steps.push('H', metrics.width - Blockly.BlockSvg.CORNER_RADIUS);
} else {
steps.push('H', metrics.width - Blockly.BlockSvg.HAT_CORNER_RADIUS);
}
};
/**
* Render the right edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} cursorY Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawRight_ =
function(steps, connectionsXY, metrics) {
if (this.nextConnection) {
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
} else {
steps.push('a', Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ',-' +
Blockly.BlockSvg.HAT_CORNER_RADIUS);
}
steps.push('v', -8);
if (this.nextConnection) {
steps.push(Blockly.BlockSvg.NOTCH_PATH_UP);
// Create next block connection.
var connectionX;
if (this.RTL) {
connectionX = connectionsXY.x + metrics.width;
} else {
connectionX = connectionsXY.x + metrics.width;
}
var connectionY = connectionsXY.y + metrics.height - Blockly.BlockSvg.CORNER_RADIUS * 2;
this.nextConnection.moveTo(connectionX, connectionY);
if (this.nextConnection.targetConnection) {
this.nextConnection.tighten_();
}
this.height += 4; // Height of tab.
steps.push('V', Blockly.BlockSvg.CORNER_RADIUS);
} else {
steps.push('V', Blockly.BlockSvg.HAT_CORNER_RADIUS);
}
};
/**
* Render the top edge of the block.
* @param {!Array.<string>} steps Path of block outline.
* @param {!Object} connectionsXY Location of block.
* @param {number} cursorY Height of block.
* @private
*/
Blockly.BlockSvg.prototype.renderDrawTop_ =
function(steps, connectionsXY, metrics) {
if (this.nextConnection) {
steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 -' +
Blockly.BlockSvg.CORNER_RADIUS + ',-' +
Blockly.BlockSvg.CORNER_RADIUS);
} else {
steps.push('a', Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 -' +
Blockly.BlockSvg.HAT_CORNER_RADIUS + ',-' +
Blockly.BlockSvg.HAT_CORNER_RADIUS);
}
steps.push('z');
};

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,7 @@
// Top level object for Blockly.
goog.provide('Blockly');
goog.require('Blockly.BlockSvg');
goog.require('Blockly.BlockSvg.render');
goog.require('Blockly.Events');
goog.require('Blockly.FieldAngle');
goog.require('Blockly.FieldCheckbox');

34
core/colours.js Normal file
View file

@ -0,0 +1,34 @@
'use strict';
goog.provide('Blockly.Colours');
Blockly.Colours = {
"motion": {
"primary": "#4C97FF",
"secondary": "#4280D7",
"tertiary": "#3373CC"
},
"looks": {
"primary": "#9966FF",
"secondary": "#855CD6",
"tertiary": "#774DCB"
},
"sounds": {
"primary": "#D65CD6",
"secondary": "#BF40BF",
"tertiary": "#A63FA6"
},
"control": {
"primary": "#FFAB19",
"secondary": "#EC9C13",
"tertiary": "#CF8B17"
},
"event": {
"primary": "#FFD500",
"secondary": "#DBC200",
"tertiary": "#CCAA00"
},
"text": "#575E75",
"workspace": "#F5F8FF",
"textField": "#FFFFFF"
};

View file

@ -68,6 +68,17 @@ Blockly.Connection.STRING = 2;
*/
Blockly.Connection.NUMBER = 3;
/**
* Constants for checking whether two connections are compatible.
*/
Blockly.Connection.CAN_CONNECT = 0;
Blockly.Connection.REASON_SELF_CONNECTION = 1;
Blockly.Connection.REASON_WRONG_TYPE = 2;
Blockly.Connection.REASON_MUST_DISCONNECT = 3;
Blockly.Connection.REASON_TARGET_NULL = 4;
Blockly.Connection.REASON_CHECKS_FAILED = 5;
Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 6;
/**
* Connection this connection connects to. Null if not connected.
* @type {Blockly.Connection}
@ -160,26 +171,73 @@ Blockly.Connection.prototype.isSuperior = function() {
this.type == Blockly.NEXT_STATEMENT;
};
/**
* Checks whether the current connection can connect with the target
* connection.
* @param {Blockly.Connection} target Connection to check compatibility with.
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
* an error code otherwise.
* @private
*/
Blockly.Connection.prototype.canConnectWithReason_ = function(target) {
if (!target) {
return Blockly.Connection.REASON_TARGET_NULL;
} else if (this.sourceBlock_ && target.sourceBlock_ == this.sourceBlock_) {
return Blockly.Connection.REASON_SELF_CONNECTION;
} else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) {
return Blockly.Connection.REASON_WRONG_TYPE;
} else if (this.targetConnection) {
return Blockly.Connection.REASON_MUST_DISCONNECT;
} else if (this.sourceBlock_ && target.sourceBlock_ &&
this.sourceBlock_.workspace !== target.sourceBlock_.workspace) {
return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
} else if (!this.checkType_(target)) {
return Blockly.Connection.REASON_CHECKS_FAILED;
}
return Blockly.Connection.CAN_CONNECT;
};
/**
* Checks whether the current connection and target connection are compatible
* and throws an exception if they are not.
* @param {Blockly.Connection} target The connection to check compatibility
* with.
* @private
*/
Blockly.Connection.prototype.checkConnection_ = function(target) {
switch (this.canConnectWithReason_(target)) {
case Blockly.Connection.CAN_CONNECT:
break;
case Blockly.Connection.REASON_SELF_CONNECTION:
throw 'Attempted to connect a block to itself.';
case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:
throw 'Blocks are on different workspaces.';
case Blockly.Connection.REASON_WRONG_TYPE:
throw 'Attempt to connect incompatible types.';
case Blockly.Connection.REASON_MUST_DISCONNECT:
throw 'Source connection already connected.';
case Blockly.Connection.REASON_TARGET_NULL:
throw 'Target connection is null.';
case Blockly.Connection.REASON_CHECKS_FAILED:
throw 'Connection checks failed.';
default:
throw 'Unknown connection failure: this should never happen!';
}
};
/**
* Connect this connection to another connection.
* @param {!Blockly.Connection} otherConnection Connection to connect to.
*/
Blockly.Connection.prototype.connect = function(otherConnection) {
if (this.sourceBlock_ == otherConnection.sourceBlock_) {
throw 'Attempted to connect a block to itself.';
}
if (this.sourceBlock_.workspace !== otherConnection.sourceBlock_.workspace) {
throw 'Blocks are on different workspaces.';
}
if (Blockly.OPPOSITE_TYPE[this.type] != otherConnection.type) {
throw 'Attempt to connect incompatible types.';
}
if (this.targetConnection) {
throw 'Source connection already connected.';
}
this.checkConnection_(otherConnection);
// If the previous statement failed it would have thrown an exception.
if (otherConnection.targetConnection) {
// Other connection is already connected to something.
// Disconnect it and reattach it or bump it as needed.
var orphanBlock = otherConnection.targetBlock();
// Displaced shadow blocks dissolve rather than reattaching or bumping.
if (orphanBlock.isShadow()) {
orphanBlock.dispose();
orphanBlock = null;
@ -192,18 +250,13 @@ Blockly.Connection.prototype.connect = function(otherConnection) {
throw 'Orphan block does not have an output connection.';
}
// Attempt to reattach the orphan at the end of the newly inserted
// block. Since this block may be a row, walk down to the end.
var newBlock = this.sourceBlock_;
var connection;
while (connection = Blockly.Connection.singleConnection_(
/** @type {!Blockly.Block} */ (newBlock), orphanBlock)) {
// '=' is intentional in line above.
newBlock = connection.targetBlock();
if (!newBlock || newBlock.isShadow()) {
orphanBlock.outputConnection.connect(connection);
orphanBlock = null;
break;
}
// block. Since this block may be a row, walk down to the end
// or to the first (and only) shadow block.
var connection = Blockly.Connection.lastConnectionInRow_(
this.sourceBlock_, orphanBlock);
if (connection != null) {
orphanBlock.outputConnection.connect(connection);
orphanBlock = null;
}
} else if (this.type == Blockly.PREVIOUS_STATEMENT) {
// Statement connections.
@ -237,7 +290,11 @@ Blockly.Connection.prototype.connect = function(otherConnection) {
setTimeout(function() {
// Verify orphan hasn't been deleted or reconnected (user on meth).
if (orphanBlock.workspace && !orphanBlock.getParent()) {
orphanBlock.outputConnection.bumpAwayFrom_(otherConnection);
if (orphanBlock.outputConnection) {
orphanBlock.outputConnection.bumpAwayFrom_(otherConnection);
} else if (orphanBlock.previousConnection) {
orphanBlock.previousConnection.bumpAwayFrom_(otherConnection);
}
}
}, Blockly.BUMP_DELAY);
}
@ -256,8 +313,7 @@ Blockly.Connection.prototype.connect = function(otherConnection) {
}
// Establish the connections.
this.targetConnection = otherConnection;
otherConnection.targetConnection = this;
Blockly.Connection.connectReciprocally(this, otherConnection);
// Demote the inferior block so that one is a child of the superior one.
childBlock.setParent(parentBlock);
@ -282,6 +338,19 @@ Blockly.Connection.prototype.connect = function(otherConnection) {
}
};
/**
* Update two connections to target each other.
* @param {Blockly.Connection} first The first connection to update.
* @param {Blockly.Connection} second The second conneciton to update.
*/
Blockly.Connection.connectReciprocally = function(first, second) {
if (!first || !second) {
throw 'Cannot connect null connections.';
}
first.targetConnection = second;
second.targetConnection = first;
};
/**
* Does the given block have one and only one connection point that will accept
* an orphaned block?
@ -306,6 +375,32 @@ Blockly.Connection.singleConnection_ = function(block, orphanBlock) {
return connection;
};
/**
* Walks down a row a blocks, at each stage checking if there are any
* connections that will accept the orphaned block. If at any point there
* are zero or multiple eligible connections, returns null. Otherwise
* returns the only input on the last block in the chain.
* Terminates early for shadow blocks.
* @param {!Blockly.Block} startBlack The block on which to start the search.
* @param {!Blockly.Block} orphanBlock The block that is looking for a home.
* @return {Blockly.Connection} The suitable connection point on the chain
* of blocks, or null.
* @private
*/
Blockly.Connection.lastConnectionInRow_ = function(startBlock, orphanBlock) {
var newBlock = startBlock;
var connection;
while (connection = Blockly.Connection.singleConnection_(
/** @type {!Blockly.Block} */ (newBlock), orphanBlock)) {
// '=' is intentional in line above.
newBlock = connection.targetBlock();
if (!newBlock || newBlock.isShadow()) {
return connection;
}
}
return null;
};
/**
* Disconnect this connection.
*/

View file

@ -26,6 +26,8 @@
goog.provide('Blockly.Css');
goog.require('Blockly.Colours');
/**
* List of cursors.
@ -132,7 +134,7 @@ Blockly.Css.setCursor = function(cursor) {
*/
Blockly.Css.CONTENT = [
'.blocklySvg {',
'background-color: #fff;',
'background-color: ' + Blockly.Colours.workspace + ';',
'outline: none;',
'overflow: hidden;', /* IE overflows by default. */
'}',
@ -179,8 +181,7 @@ Blockly.Css.CONTENT = [
'}',
'.blocklyPath {',
// 'stroke: #f00;',
'stroke-width: 1.5px;',
'stroke-width: 1px;',
'}',
'.blocklySelected>.blocklyPath {',
@ -189,8 +190,6 @@ Blockly.Css.CONTENT = [
'}',
'.blocklyDragging>.blocklyPath {',
'fill-opacity: .8;',
'stroke-opacity: .8;',
'}',
'.blocklyDisabled>.blocklyPath {',
@ -217,7 +216,7 @@ Blockly.Css.CONTENT = [
'.blocklyNonEditableText>text,',
'.blocklyEditableText>text {',
'fill: #000;',
'fill: ' + Blockly.Colours.text + ';',
'}',
'.blocklyEditableText:hover>rect {',
@ -226,7 +225,7 @@ Blockly.Css.CONTENT = [
'}',
'.blocklyBubbleText {',
'fill: #000;',
'fill:' + Blockly.Colours.text +';',
'}',
/*
@ -288,7 +287,8 @@ Blockly.Css.CONTENT = [
'margin: 0;',
'outline: none;',
'padding: 0 1px;',
'width: 100%',
'width: 100%;',
'text-align: center',
'}',
'.blocklyMainBackground {',

View file

@ -45,7 +45,9 @@ goog.require('goog.userAgent');
* @constructor
*/
Blockly.Field = function(text, opt_validator) {
this.size_ = new goog.math.Size(0, 25);
this.size_ = new goog.math.Size(
Blockly.BlockSvg.FIELD_WIDTH,
Blockly.BlockSvg.FIELD_HEIGHT);
this.setValue(text);
this.setValidator(opt_validator);
};
@ -76,7 +78,7 @@ Blockly.Field.prototype.name = undefined;
* Maximum characters of text to display before adding an ellipsis.
* @type {number}
*/
Blockly.Field.prototype.maxDisplayLength = 50;
Blockly.Field.prototype.maxDisplayLength = 5;
/**
* Visible text to display.
@ -137,10 +139,14 @@ Blockly.Field.prototype.init = function(block) {
'ry': 4,
'x': -Blockly.BlockSvg.SEP_SPACE_X / 2,
'y': 0,
'height': 16}, this.fieldGroup_, this.sourceBlock_.workspace);
'height': Blockly.BlockSvg.FIELD_HEIGHT}, this.fieldGroup_, this.sourceBlock_.workspace);
/** @type {!Element} */
this.textElement_ = Blockly.createSvgElement('text',
{'class': 'blocklyText', 'y': this.size_.height - 12.5},
{'class': 'blocklyText',
'y': this.size_.height/2 + 6.25,
'x': Blockly.BlockSvg.FIELD_WIDTH / 2,
'width': Blockly.BlockSvg.FIELD_WIDTH - Blockly.BlockSvg.SEP_SPACE_X,
'text-anchor': 'middle'},
this.fieldGroup_);
this.updateEditable();

View file

@ -26,6 +26,7 @@
goog.provide('Blockly.FieldTextInput');
goog.require('Blockly.BlockSvg.render');
goog.require('Blockly.Field');
goog.require('Blockly.Msg');
goog.require('goog.asserts');
@ -225,9 +226,16 @@ Blockly.FieldTextInput.prototype.validate_ = function() {
Blockly.FieldTextInput.prototype.resizeEditor_ = function() {
var div = Blockly.WidgetDiv.DIV;
var bBox = this.fieldGroup_.getBBox();
div.style.width = bBox.width * this.sourceBlock_.workspace.scale + 'px';
div.style.height = bBox.height * this.sourceBlock_.workspace.scale + 'px';
var height = bBox.height * this.sourceBlock_.workspace.scale;
var width = Math.max(
bBox.width, Blockly.BlockSvg.FIELD_WIDTH-Blockly.BlockSvg.SEP_SPACE_X) *
this.sourceBlock_.workspace.scale
div.style.width = width + 'px';
div.style.height = height + 'px';
var xy = this.getAbsoluteXY_();
xy.x += Blockly.BlockSvg.SEP_SPACE_X * this.sourceBlock_.workspace.scale;
// @todo Why 3?
xy.y += (Blockly.BlockSvg.FIELD_HEIGHT * this.sourceBlock_.workspace.scale)/2 - height/2 + 3;
// In RTL mode block fields and LTR input fields the left edge moves,
// whereas the right edge is fixed. Reposition the editor.
if (this.sourceBlock_.RTL) {

View file

@ -366,7 +366,23 @@ Blockly.Flyout.prototype.position = function() {
* @private
*/
Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) {
var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT;
if (this.horizontalLayout_) {
this.setBackgroundPathHorizontal_(width, height);
} else {
this.setBackgroundPathVertical_(width, height);
}
};
/**
* Create and set the path for the visible boundaries of the toolbox in vertical mode.
* @param {number} width The width of the toolbox, not including the
* rounded corners.
* @param {number} height The height of the toolbox, not including
* rounded corners.
* @private
*/
Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) {
var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT;
// Decide whether to start on the left or right.
var path = ['M ' + (atRight ? this.width_ : 0) + ',0'];
// Top.
@ -389,6 +405,50 @@ Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) {
this.svgBackground_.setAttribute('d', path.join(' '));
};
/**
* Create and set the path for the visible boundaries of the toolbox in horizontal mode.
* @param {number} width The width of the toolbox, not including the
* rounded corners.
* @param {number} height The height of the toolbox, not including
* rounded corners.
* @private
*/
Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width, height) {
var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP;
// start at top left.
var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)];
if (atTop) {
// top
path.push('h', width + this.CORNER_RADIUS);
// right
path.push('v', height);
// bottom
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
-this.CORNER_RADIUS, this.CORNER_RADIUS);
path.push('h', -1 * (width - this.CORNER_RADIUS));
// left
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
-this.CORNER_RADIUS, -this.CORNER_RADIUS);
path.push('z');
} else {
// top
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
this.CORNER_RADIUS, -this.CORNER_RADIUS);
path.push('h', width - this.CORNER_RADIUS);
// right
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
this.CORNER_RADIUS, this.CORNER_RADIUS);
path.push('v', height - this.CORNER_RADIUS);
// bottom
path.push('h', -width - this.CORNER_RADIUS);
// left
path.push('z');
}
this.svgBackground_.setAttribute('d', path.join(' '));
};
/**
* Scroll the flyout to the top.
*/
@ -783,7 +843,7 @@ Blockly.Flyout.prototype.filterForCapacity_ = function() {
};
/**
* Return the deletion rectangle for this flyout.
* Return the deletion rectangle for this flyout in viewport coordinates.
* @return {goog.math.Rect} Rectangle in which to delete.
*/
Blockly.Flyout.prototype.getClientRect = function() {
@ -792,23 +852,22 @@ Blockly.Flyout.prototype.getClientRect = function() {
// area are still deleted. Must be larger than the largest screen size,
// but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
var BIG_NUM = 1000000000;
var mainWorkspace = Blockly.mainWorkspace;
var x = flyoutRect.left;
var y = flyoutRect.top;
var width = flyoutRect.width;
var height = flyoutRect.height;
// Fix scale if nested in zoomed workspace.
var scale = this.targetWorkspace_ == mainWorkspace ? 1 : mainWorkspace.scale;
var x = Blockly.getSvgXY_(this.svgGroup_, mainWorkspace).x;
var y = Blockly.getSvgXY_(this.svgGroup_, mainWorkspace).y;
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
return new goog.math.Rect(-BIG_NUM, y - BIG_NUM, BIG_NUM * 2,
BIG_NUM + this.height_ * scale);
BIG_NUM + height);
} else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
return new goog.math.Rect(-BIG_NUM, y + this.verticalOffset_, BIG_NUM * 2,
BIG_NUM + this.height_ * scale);
BIG_NUM + height);
} else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + this.width_ * scale,
return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width,
BIG_NUM * 2);
} else { // Right
return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + this.width_ * scale,
return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width,
BIG_NUM * 2);
}
};

View file

@ -249,16 +249,6 @@ Blockly.createDom_ = function(container, options) {
Blockly.Css.inject(options.hasCss, options.pathToMedia);
// Build the SVG DOM.
/*
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
class="blocklySvg">
...
</svg>
*/
var svg = Blockly.createSvgElement('svg', {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:html': 'http://www.w3.org/1999/xhtml',
@ -266,27 +256,9 @@ Blockly.createDom_ = function(container, options) {
'version': '1.1',
'class': 'blocklySvg'
}, container);
/*
<defs>
... filters go here ...
</defs>
*/
var defs = Blockly.createSvgElement('defs', {}, svg);
var rnd = String(Math.random()).substring(2);
/*
<filter id="blocklyEmbossFilter837493">
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"/>
<feSpecularLighting in="blur" surfaceScale="1" specularConstant="0.5"
specularExponent="10" lighting-color="white"
result="specOut">
<fePointLight x="-5000" y="-10000" z="20000"/>
</feSpecularLighting>
<feComposite in="specOut" in2="SourceAlpha" operator="in"
result="specOut"/>
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic"
k1="0" k2="1" k3="1" k4="0"/>
</filter>
*/
var embossFilter = Blockly.createSvgElement('filter',
{'id': 'blocklyEmbossFilter' + rnd}, defs);
Blockly.createSvgElement('feGaussianBlur',
@ -304,13 +276,7 @@ Blockly.createDom_ = function(container, options) {
{'in': 'SourceGraphic', 'in2': 'specOut', 'operator': 'arithmetic',
'k1': 0, 'k2': 1, 'k3': 1, 'k4': 0}, embossFilter);
options.embossFilterId = embossFilter.id;
/*
<pattern id="blocklyDisabledPattern837493" patternUnits="userSpaceOnUse"
width="10" height="10">
<rect width="10" height="10" fill="#aaa" />
<path d="M 0 0 L 10 10 M 10 0 L 0 10" stroke="#cc0" />
</pattern>
*/
var disabledPattern = Blockly.createSvgElement('pattern',
{'id': 'blocklyDisabledPattern' + rnd,
'patternUnits': 'userSpaceOnUse',
@ -320,12 +286,7 @@ Blockly.createDom_ = function(container, options) {
Blockly.createSvgElement('path',
{'d': 'M 0 0 L 10 10 M 10 0 L 0 10', 'stroke': '#cc0'}, disabledPattern);
options.disabledPatternId = disabledPattern.id;
/*
<pattern id="blocklyGridPattern837493" patternUnits="userSpaceOnUse">
<rect stroke="#888" />
<rect stroke="#888" />
</pattern>
*/
var gridPattern = Blockly.createSvgElement('pattern',
{'id': 'blocklyGridPattern' + rnd,
'patternUnits': 'userSpaceOnUse'}, defs);
@ -481,36 +442,6 @@ Blockly.init_ = function(mainWorkspace) {
mainWorkspace.scrollbar = new Blockly.ScrollbarPair(mainWorkspace);
mainWorkspace.scrollbar.resize();
}
// Load the sounds.
if (options.hasSounds) {
mainWorkspace.loadAudio_(
[options.pathToMedia + 'click.mp3',
options.pathToMedia + 'click.wav',
options.pathToMedia + 'click.ogg'], 'click');
mainWorkspace.loadAudio_(
[options.pathToMedia + 'disconnect.wav',
options.pathToMedia + 'disconnect.mp3',
options.pathToMedia + 'disconnect.ogg'], 'disconnect');
mainWorkspace.loadAudio_(
[options.pathToMedia + 'delete.mp3',
options.pathToMedia + 'delete.ogg',
options.pathToMedia + 'delete.wav'], 'delete');
// Bind temporary hooks that preload the sounds.
var soundBinds = [];
var unbindSounds = function() {
while (soundBinds.length) {
Blockly.unbindEvent_(soundBinds.pop());
}
mainWorkspace.preloadAudio_();
};
// Android ignores any sound not loaded as a result of a user action.
soundBinds.push(
Blockly.bindEvent_(document, 'mousemove', null, unbindSounds));
soundBinds.push(
Blockly.bindEvent_(document, 'touchstart', null, unbindSounds));
}
};
/**

View file

@ -78,15 +78,43 @@ Blockly.Toolbox = function(workspace) {
*/
this.toolboxPosition = workspace.options.toolboxPosition;
/**
* Configuration constants for Closure's tree UI.
* @type {Object.<string,*>}
* @private
*/
this.config_ = {
indentWidth: 19,
cssRoot: 'blocklyTreeRoot',
cssHideRoot: 'blocklyHidden',
cssItem: '',
cssTreeRow: 'blocklyTreeRow',
cssItemLabel: 'blocklyTreeLabel',
cssTreeIcon: 'blocklyTreeIcon',
cssExpandedFolderIcon: 'blocklyTreeIconOpen',
cssFileIcon: 'blocklyTreeIconNone',
cssSelectedRow: 'blocklyTreeSelected'
};
/**
* Configuration constants for tree separator.
* @type {Object.<string,*>}
* @private
*/
this.treeSeparatorConfig_ = {
cssTreeRow: 'blocklyTreeSeparator'
};
if (this.horizontalLayout_) {
this.CONFIG_['cssTreeRow'] =
this.CONFIG_['cssTreeRow'] +
this.config_['cssTreeRow'] =
this.config_['cssTreeRow'] +
(workspace.RTL ? ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree');
Blockly.Toolbox.TreeSeparator.CONFIG_['cssTreeRow'] =
this.treeSeparatorConfig_['cssTreeRow'] =
'blocklyTreeSeparatorHorizontal' +
(workspace.RTL ? ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree');
this.CONFIG_['cssTreeIcon'] = '';
this.config_['cssTreeIcon'] = '';
}
};
@ -116,25 +144,6 @@ Blockly.Toolbox.prototype.selectedOption_ = null;
*/
Blockly.Toolbox.prototype.lastCategory_ = null;
/**
* Configuration constants for Closure's tree UI.
* @type {Object.<string,*>}
* @const
* @private
*/
Blockly.Toolbox.prototype.CONFIG_ = {
indentWidth: 19,
cssRoot: 'blocklyTreeRoot',
cssHideRoot: 'blocklyHidden',
cssItem: '',
cssTreeRow: 'blocklyTreeRow',
cssItemLabel: 'blocklyTreeLabel',
cssTreeIcon: 'blocklyTreeIcon',
cssExpandedFolderIcon: 'blocklyTreeIconOpen',
cssFileIcon: 'blocklyTreeIconNone',
cssSelectedRow: 'blocklyTreeSelected'
};
/**
* Initializes the toolbox.
*/
@ -174,10 +183,10 @@ Blockly.Toolbox.prototype.init = function() {
this.flyout_.init(workspace);
this.flyout_.hide();
this.CONFIG_['cleardotPath'] = workspace.options.pathToMedia + '1x1.gif';
this.CONFIG_['cssCollapsedFolderIcon'] =
this.config_['cleardotPath'] = workspace.options.pathToMedia + '1x1.gif';
this.config_['cssCollapsedFolderIcon'] =
'blocklyTreeIconClosed' + (this.RTL ? 'Rtl' : 'Ltr');
var tree = new Blockly.Toolbox.TreeControl(this, this.CONFIG_);
var tree = new Blockly.Toolbox.TreeControl(this, this.config_);
this.tree_ = tree;
tree.setShowRootNode(false);
tree.setShowLines(false);
@ -309,9 +318,10 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) {
// Separator between two categories.
// <sep></sep>
if (that.horizontalLayout_) {
treeOut.add(new Blockly.Toolbox.TreeSeparator());
treeOut.add(new Blockly.Toolbox.TreeSeparator(that.treeSeparatorConfig_));
} else {
treeOut.addChildAt(new Blockly.Toolbox.TreeSeparator(), 0);
treeOut.addChildAt(new Blockly.Toolbox.TreeSeparator(that.treeSeparatorConfig_),
0);
}
} else {
// Change the gap between two blocks.
@ -382,7 +392,7 @@ Blockly.Toolbox.prototype.clearSelection = function() {
};
/**
* Return the deletion rectangle for this toolbar.
* Return the deletion rectangle for this toolbar in viewport coordinates.
* @return {goog.math.Rect} Rectangle in which to delete.
*/
Blockly.Toolbox.prototype.getClientRect = function() {
@ -390,19 +400,23 @@ Blockly.Toolbox.prototype.getClientRect = function() {
// area are still deleted. Must be smaller than Infinity, but larger than
// the largest screen size.
var BIG_NUM = 10000000;
var svgSize = Blockly.svgSize(this.workspace_.getParentSvg());
var x = svgSize.width - this.width;
var toolboxRect = this.HtmlDiv.getBoundingClientRect();
var x = toolboxRect.left;
var y = toolboxRect.top;
var width = toolboxRect.width;
var height = toolboxRect.height;
// Assumes that the toolbox is on the SVG edge. If this changes
// (e.g. toolboxes in mutators) then this code will need to be more complex.
if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) {
return new goog.math.Rect(-BIG_NUM, -BIG_NUM, BIG_NUM + this.width, 2 * BIG_NUM);
return new goog.math.Rect(-BIG_NUM, -BIG_NUM, BIG_NUM + width, 2 * BIG_NUM);
} else if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) {
return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + this.width, 2 * BIG_NUM);
return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, 2 * BIG_NUM);
} else if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) {
return new goog.math.Rect(-BIG_NUM, -BIG_NUM, 2 * BIG_NUM, BIG_NUM + this.height);
return new goog.math.Rect(-BIG_NUM, -BIG_NUM, 2 * BIG_NUM, BIG_NUM + height);
} else { // Bottom
return new goog.math.Rect(0, svgSize.height, 2 * BIG_NUM, BIG_NUM + this.height);
return new goog.math.Rect(0, y, 2 * BIG_NUM, BIG_NUM + width);
}
};
@ -576,18 +590,7 @@ Blockly.Toolbox.TreeNode.prototype.onDoubleClick_ = function(e) {
* @constructor
* @extends {Blockly.Toolbox.TreeNode}
*/
Blockly.Toolbox.TreeSeparator = function() {
Blockly.Toolbox.TreeNode.call(this, null, '',
Blockly.Toolbox.TreeSeparator.CONFIG_);
Blockly.Toolbox.TreeSeparator = function(config) {
Blockly.Toolbox.TreeNode.call(this, null, '', config);
};
goog.inherits(Blockly.Toolbox.TreeSeparator, Blockly.Toolbox.TreeNode);
/**
* Configuration constants for tree separator.
* @type {Object.<string,*>}
* @const
* @private
*/
Blockly.Toolbox.TreeSeparator.CONFIG_ = {
cssTreeRow: 'blocklyTreeSeparator'
};

View file

@ -49,6 +49,8 @@ Blockly.Workspace = function(opt_options) {
this.topBlocks_ = [];
/** @type {!Array.<!Function>} */
this.listeners_ = [];
/** @type {!Array.<!Function>} */
this.tapListeners_ = [];
};
/**
@ -228,6 +230,40 @@ Blockly.Workspace.prototype.fireChangeListener = function(event) {
}
};
/**
* When a block in the workspace is tapped, call a function with the
* blockId and root blockId.
* @param {!Function} func Function to call.
* @return {!Function} Function that can be passed to
* removeTapListener.
*/
Blockly.Workspace.prototype.addTapListener = function(func) {
this.tapListeners_.push(func);
return func;
};
/**
* Stop listening for this workspace's taps.
* @param {Function} func Function to stop calling.
*/
Blockly.Workspace.prototype.removeTapListener = function(func) {
var i = this.tapListeners_.indexOf(func);
if (i != -1) {
this.tapListeners_.splice(i, 1);
}
};
/**
* Fire a tap event.
* @param {string} ID of block that was tapped
* @param {string} ID of root block in tree that was tapped
*/
Blockly.Workspace.prototype.fireTapListener = function(blockId, rootBlockId) {
for (var i = 0, func; func = this.tapListeners_[i]; i++) {
func(blockId, rootBlockId);
}
};
/**
* Database of all workspaces.
* @private

View file

@ -51,14 +51,10 @@ Blockly.WorkspaceSvg = function(options) {
this.getMetrics = options.getMetrics;
this.setMetrics = options.setMetrics;
Blockly.ConnectionDB.init(this);
/** @type {!Function} */
this.audioCallback_ = null;
/**
* Database of pre-loaded sounds.
* @private
* @const
*/
this.SOUNDS_ = Object.create(null);
Blockly.ConnectionDB.init(this);
};
goog.inherits(Blockly.WorkspaceSvg, Blockly.Workspace);
@ -793,82 +789,21 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) {
};
/**
* Load an audio file. Cache it, ready for instantaneous playing.
* @param {!Array.<string>} filenames List of file types in decreasing order of
* preference (i.e. increasing size). E.g. ['media/go.mp3', 'media/go.wav']
* Filenames include path from Blockly's root. File extensions matter.
* @param {string} name Name of sound.
* @private
* Set the callback for playing audio. The application should provide
* a function that plays the called audio sound.
* @param {!Function} func Function to call.
*/
Blockly.WorkspaceSvg.prototype.loadAudio_ = function(filenames, name) {
if (!filenames.length) {
return;
}
try {
var audioTest = new window['Audio']();
} catch(e) {
// No browser support for Audio.
// IE can throw an error even if the Audio object exists.
return;
}
var sound;
for (var i = 0; i < filenames.length; i++) {
var filename = filenames[i];
var ext = filename.match(/\.(\w+)$/);
if (ext && audioTest.canPlayType('audio/' + ext[1])) {
// Found an audio format we can play.
sound = new window['Audio'](filename);
break;
}
}
if (sound && sound.play) {
this.SOUNDS_[name] = sound;
}
Blockly.Workspace.prototype.setAudioCallback = function(func) {
this.audioCallback_ = func;
};
/**
* Preload all the audio files so that they play quickly when asked for.
* @private
*/
Blockly.WorkspaceSvg.prototype.preloadAudio_ = function() {
for (var name in this.SOUNDS_) {
var sound = this.SOUNDS_[name];
sound.volume = .01;
sound.play();
sound.pause();
// iOS can only process one sound at a time. Trying to load more than one
// corrupts the earlier ones. Just load one and leave the others uncached.
if (goog.userAgent.IPAD || goog.userAgent.IPHONE) {
break;
}
}
};
/**
* Play an audio file at specified value. If volume is not specified,
* use full volume (1).
* Play an audio file at specified value.
* @param {string} name Name of sound.
* @param {number=} opt_volume Volume of sound (0-1).
*/
Blockly.WorkspaceSvg.prototype.playAudio = function(name, opt_volume) {
var sound = this.SOUNDS_[name];
if (sound) {
var mySound;
var ie9 = goog.userAgent.DOCUMENT_MODE &&
goog.userAgent.DOCUMENT_MODE === 9;
if (ie9 || goog.userAgent.IPAD || goog.userAgent.ANDROID) {
// Creating a new audio node causes lag in IE9, Android and iPad. Android
// and IE9 refetch the file from the server, iPad uses a singleton audio
// node which must be deleted and recreated for each new audio tag.
mySound = sound;
} else {
mySound = sound.cloneNode();
}
mySound.volume = (opt_volume === undefined ? 1 : opt_volume);
mySound.play();
} else if (this.options.parentWorkspace) {
// Maybe a workspace on a lower level knows about this sound.
this.options.parentWorkspace.playAudio(name, opt_volume);
Blockly.WorkspaceSvg.prototype.playAudio = function(name) {
if (this.audioCallback_) {
this.audioCallback_(name);
}
};

BIN
demos/audio/icon.png Normal file

Binary file not shown.

After

(image error) Size: 2.1 KiB

146
demos/audio/index.html Normal file
View file

@ -0,0 +1,146 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Blockly Demo: Fixed Blockly</title>
<script src="../../blockly_compressed_horizontal.js"></script>
<script src="../../blocks_compressed.js"></script>
<script src="../../msg/js/en.js"></script>
<style>
body {
background-color: #fff;
font-family: sans-serif;
}
h1 {
font-weight: normal;
font-size: 140%;
}
</style>
</head>
<body>
<h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
<a href="../index.html">Demos</a> &gt; Audio</h1>
<p>This demo of using the setAudioCallback workspace method with a simple web-based Audio method to register sounds.
Upstream Blockly embeds the audio-playing code directly into the Workspace. We have split out the functionality to allow
for integration with other audio systems.
</p>
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
<xml id="toolbox" style="display: none">
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="controls_repeat_ext"></block>
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="text"></block>
<block type="text_print"></block>
</xml>
<script>
// Sound system originally from Blockly, combined with new hooks
// Database of sounds
var SOUNDS_ = Object.create(null);
/**
* Load an audio file. Cache it, ready for instantaneous playing.
* @param {!Array.<string>} filenames List of file types in decreasing order of
* preference (i.e. increasing size). E.g. ['media/go.mp3', 'media/go.wav']
* Filenames include path from Blockly's root. File extensions matter.
* @param {string} name Name of sound.
*/
var loadAudio_ = function(filenames, name) {
if (!filenames.length) {
return;
}
try {
var audioTest = new window['Audio']();
} catch(e) {
// No browser support for Audio.
// IE can throw an error even if the Audio object exists.
return;
}
var sound;
for (var i = 0; i < filenames.length; i++) {
var filename = filenames[i];
var ext = filename.match(/\.(\w+)$/);
if (ext && audioTest.canPlayType('audio/' + ext[1])) {
// Found an audio format we can play.
sound = new window['Audio'](filename);
break;
}
}
if (sound && sound.play) {
SOUNDS_[name] = sound;
}
};
/**
* Preload all the audio files so that they play quickly when asked for.
*/
var preloadAudio_ = function() {
for (var name in SOUNDS_) {
var sound = SOUNDS_[name];
sound.volume = .01;
sound.play();
sound.pause();
// iOS can only process one sound at a time. Trying to load more than one
// corrupts the earlier ones. Just load one and leave the others uncached.
if (goog.userAgent.IPAD || goog.userAgent.IPHONE) {
break;
}
}
};
/**
* Play an audio file at specified value. If volume is not specified,
* use full volume (1).
* @param {string} name Name of sound.
* @param {number=} opt_volume Volume of sound (0-1).
*/
var playAudio = function(name, opt_volume) {
var sound = SOUNDS_[name];
if (sound) {
var mySound;
var ie9 = goog.userAgent.DOCUMENT_MODE &&
goog.userAgent.DOCUMENT_MODE === 9;
if (ie9 || goog.userAgent.IPAD || goog.userAgent.ANDROID) {
// Creating a new audio node causes lag in IE9, Android and iPad. Android
// and IE9 refetch the file from the server, iPad uses a singleton audio
// node which must be deleted and recreated for each new audio tag.
mySound = sound;
} else {
mySound = sound.cloneNode();
}
mySound.volume = (opt_volume === undefined ? 1 : opt_volume);
mySound.play();
} else if (this.options.parentWorkspace) {
// Maybe a workspace on a lower level knows about this sound.
this.options.parentWorkspace.playAudio(name, opt_volume);
}
};
var pathToMedia = '../../media/';
loadAudio_(
[pathToMedia + 'click.mp3',
pathToMedia + 'click.wav',
pathToMedia + 'click.ogg'], 'click');
loadAudio_(
[pathToMedia + 'disconnect.wav',
pathToMedia + 'disconnect.mp3',
pathToMedia + 'disconnect.ogg'], 'disconnect');
loadAudio_(
[pathToMedia + 'delete.mp3',
pathToMedia + 'delete.ogg',
pathToMedia + 'delete.wav'], 'delete');
preloadAudio_();
var workspace = Blockly.inject('blocklyDiv',
{media: pathToMedia,
toolbox: document.getElementById('toolbox')});
workspace.setAudioCallback(playAudio);
</script>
</body>
</html>

View file

@ -183,6 +183,17 @@
<div>Build custom blocks using Blockly.</div>
</td>
</tr>
<tr>
<td>
<a href="audio/index.html">
<img src="audio/icon.png" height=80 width=100>
</a>
</td>
<td>
<div><a href="audio/index.html">Audio</a></div>
<div>Audio hooks for Blockly.</div>
</td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,41 @@
/**
* @license
* Visual Blocks Language
*
* Copyright 2012 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 Generating JavaScript for looks blocks.
* @author rschamp@media.mit.edu (Ray Schamp)
*/
'use strict';
goog.provide('Blockly.JavaScript.looks');
goog.require('Blockly.JavaScript');
Blockly.JavaScript['looks_say'] = function(block) {
// Say something.
var message = Blockly.JavaScript.valueToCode(block, 'MESSAGE',
Blockly.JavaScript.ORDER_ASSIGNMENT) || '0';
var code = '';
var loopVar = Blockly.JavaScript.variableDB_.getDistinctName(
'message', Blockly.Variables.NAME_TYPE);
code += 'console.log(' + message + ');\n';
return code;
};

View file

@ -35,7 +35,7 @@ Blockly.JavaScript.controls_if=function(a){for(var b=0,c=Blockly.JavaScript.valu
Blockly.JavaScript.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.JavaScript.ORDER_EQUALITY:Blockly.JavaScript.ORDER_RELATIONAL,d=Blockly.JavaScript.valueToCode(a,"A",c)||"0";a=Blockly.JavaScript.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]};
Blockly.JavaScript.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"&&":"||",c="&&"==b?Blockly.JavaScript.ORDER_LOGICAL_AND:Blockly.JavaScript.ORDER_LOGICAL_OR,d=Blockly.JavaScript.valueToCode(a,"A",c);a=Blockly.JavaScript.valueToCode(a,"B",c);if(d||a){var e="&&"==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+" "+b+" "+a,c]};
Blockly.JavaScript.logic_negate=function(a){var b=Blockly.JavaScript.ORDER_LOGICAL_NOT;return["!"+(Blockly.JavaScript.valueToCode(a,"BOOL",b)||"true"),b]};Blockly.JavaScript.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"true":"false",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.logic_null=function(a){return["null",Blockly.JavaScript.ORDER_ATOMIC]};
Blockly.JavaScript.logic_ternary=function(a){var b=Blockly.JavaScript.valueToCode(a,"IF",Blockly.JavaScript.ORDER_CONDITIONAL)||"false",c=Blockly.JavaScript.valueToCode(a,"THEN",Blockly.JavaScript.ORDER_CONDITIONAL)||"null";a=Blockly.JavaScript.valueToCode(a,"ELSE",Blockly.JavaScript.ORDER_CONDITIONAL)||"null";return[b+" ? "+c+" : "+a,Blockly.JavaScript.ORDER_CONDITIONAL]};Blockly.JavaScript.loops={};
Blockly.JavaScript.logic_ternary=function(a){var b=Blockly.JavaScript.valueToCode(a,"IF",Blockly.JavaScript.ORDER_CONDITIONAL)||"false",c=Blockly.JavaScript.valueToCode(a,"THEN",Blockly.JavaScript.ORDER_CONDITIONAL)||"null";a=Blockly.JavaScript.valueToCode(a,"ELSE",Blockly.JavaScript.ORDER_CONDITIONAL)||"null";return[b+" ? "+c+" : "+a,Blockly.JavaScript.ORDER_CONDITIONAL]};Blockly.JavaScript.looks={};Blockly.JavaScript.looks_say=function(a){a=Blockly.JavaScript.valueToCode(a,"MESSAGE",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0";var b="";Blockly.JavaScript.variableDB_.getDistinctName("message",Blockly.Variables.NAME_TYPE);return b+("console.log("+a+");\n")};Blockly.JavaScript.loops={};
Blockly.JavaScript.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(Number(a.getFieldValue("TIMES"))):Blockly.JavaScript.valueToCode(a,"TIMES",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0",c=Blockly.JavaScript.statementToCode(a,"DO"),c=Blockly.JavaScript.addLoopTrap(c,a.id);a="";var d=Blockly.JavaScript.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE),e=b;b.match(/^\w+$/)||Blockly.isNumber(b)||(e=Blockly.JavaScript.variableDB_.getDistinctName("repeat_end",Blockly.Variables.NAME_TYPE),
a+="var "+e+" = "+b+";\n");return a+("for (var "+d+" = 0; "+d+" < "+e+"; "+d+"++) {\n"+c+"}\n")};Blockly.JavaScript.controls_repeat=Blockly.JavaScript.controls_repeat_ext;
Blockly.JavaScript.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.JavaScript.valueToCode(a,"BOOL",b?Blockly.JavaScript.ORDER_LOGICAL_NOT:Blockly.JavaScript.ORDER_NONE)||"false",d=Blockly.JavaScript.statementToCode(a,"DO"),d=Blockly.JavaScript.addLoopTrap(d,a.id);b&&(c="!"+c);return"while ("+c+") {\n"+d+"}\n"};

11
media/icons/looks_say.svg Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#545DAA;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
</style>
<path class="st0" d="M37.4,18.3c0,7.4-7.6,13.3-17,13.5c-1.7,0-3.3,0.2-4.9,0.5c-1,0.2-1.9,0.4-2.8,0.6c-2.3,0.7-4.4,1.5-6.1,2.3
c-1,0.5-1.9-0.6-1.4-1.5c0.8-1.3,1.5-2.9,1.6-4.2c0.2-1.1,0-2.2-0.8-3c0,0-0.1-0.1-0.1-0.1c-1.8-1.9-3-4.3-3.3-6.9
c0-0.4-0.1-0.7-0.1-1.1c0-0.2,0-0.3,0-0.5c0.3-7.3,8-13.1,17.4-13.1C29.6,4.7,37.4,10.8,37.4,18.3z"/>
</svg>

After

(image error) Size: 849 B

View file

@ -6,13 +6,16 @@
<script src="../blockly_uncompressed_horizontal.js"></script>
<script src="../generators/javascript.js"></script>
<script src="../generators/javascript/control.js"></script>
<script src="../generators/javascript/looks.js"></script>
<script src="../generators/javascript/math.js"></script>
<script src="../generators/javascript/motion.js"></script>
<script src="../generators/javascript/event.js"></script>
<script src="../msg/messages.js"></script>
<script src="../blocks/math.js"></script>
<script src="../blocks_horizontal/event.js"></script>
<script src="../blocks/text.js"></script>
<script src="../blocks_horizontal/control.js"></script>
<script src="../blocks_horizontal/event.js"></script>
<script src="../blocks_horizontal/looks.js"></script>
<script src="../blocks_horizontal/motion.js"></script>
<script>
'use strict';
@ -149,6 +152,15 @@ h1 {
<div id="blocklyDiv"></div>
<xml id="toolbox" style="display: none">
<category name="Looks">
<block type="looks_say">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">Hey!</field>
</shadow>
</value>
</block>
</category>
<category name="Events">
<block type="event_whenflagclicked"></block>
</category>

View file

@ -0,0 +1,95 @@
/**
* @license
* Blockly Tests
*
* Copyright 2015 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';
function verify_DB_(msg, expected, db) {
var equal = (expected.length == db.length);
if (equal) {
for (var x = 0; x < expected.length; x++) {
if (expected[x] != db[x]) {
equal = false;
break;
}
}
}
if (equal) {
assertTrue(msg, true);
} else {
assertEquals(msg, expected, db);
}
}
function test_DB_addConnection() {
var db = new Blockly.ConnectionDB();
var o2 = {y_: 2, sourceBlock_: {}};
db.addConnection_(o2);
verify_DB_('Adding connection #2', [o2], db);
var o4 = {y_: 4, sourceBlock_: {}};
db.addConnection_(o4);
verify_DB_('Adding connection #4', [o2, o4], db);
var o1 = {y_: 1, sourceBlock_: {}};
db.addConnection_(o1);
verify_DB_('Adding connection #1', [o1, o2, o4], db);
var o3a = {y_: 3, sourceBlock_: {}};
db.addConnection_(o3a);
verify_DB_('Adding connection #3a', [o1, o2, o3a, o4], db);
var o3b = {y_: 3, sourceBlock_: {}};
db.addConnection_(o3b);
verify_DB_('Adding connection #3b', [o1, o2, o3b, o3a, o4], db);
}
function test_DB_removeConnection() {
var db = new Blockly.ConnectionDB();
var o1 = {y_: 1, sourceBlock_: {}};
var o2 = {y_: 2, sourceBlock_: {}};
var o3a = {y_: 3, sourceBlock_: {}};
var o3b = {y_: 3, sourceBlock_: {}};
var o3c = {y_: 3, sourceBlock_: {}};
var o4 = {y_: 4, sourceBlock_: {}};
db.addConnection_(o1);
db.addConnection_(o2);
db.addConnection_(o3c);
db.addConnection_(o3b);
db.addConnection_(o3a);
db.addConnection_(o4);
verify_DB_('Adding connections 1-4', [o1, o2, o3a, o3b, o3c, o4], db);
db.removeConnection_(o2);
verify_DB_('Removing connection #2', [o1, o3a, o3b, o3c, o4], db);
db.removeConnection_(o4);
verify_DB_('Removing connection #4', [o1, o3a, o3b, o3c], db);
db.removeConnection_(o1);
verify_DB_('Removing connection #1', [o3a, o3b, o3c], db);
db.removeConnection_(o3a);
verify_DB_('Removing connection #3a', [o3b, o3c], db);
db.removeConnection_(o3c);
verify_DB_('Removing connection #3c', [o3b], db);
db.removeConnection_(o3b);
verify_DB_('Removing connection #3b', [], db);
}

View file

@ -2,7 +2,7 @@
* @license
* Blockly Tests
*
* Copyright 2015 Google Inc.
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,79 +17,217 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Tests for connection logic.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
function verify_DB_(msg, expected, db) {
var equal = (expected.length == db.length);
if (equal) {
for (var x = 0; x < expected.length; x++) {
if (expected[x] != db[x]) {
equal = false;
break;
}
}
}
if (equal) {
assertTrue(msg, true);
} else {
assertEquals(msg, expected, db);
}
var input;
var output;
var previous;
var next;
var dummyWorkspace;
function connectionTest_setUp() {
dummyWorkspace = {};
input = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.INPUT_VALUE);
output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
previous = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.PREVIOUS_STATEMENT);
next = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.NEXT_STATEMENT);
}
function test_DB_addConnection() {
var db = new Blockly.ConnectionDB();
var o2 = {y_: 2, sourceBlock_: {}};
db.addConnection_(o2);
verify_DB_('Adding connection #2', [o2], db);
var o4 = {y_: 4, sourceBlock_: {}};
db.addConnection_(o4);
verify_DB_('Adding connection #4', [o2, o4], db);
var o1 = {y_: 1, sourceBlock_: {}};
db.addConnection_(o1);
verify_DB_('Adding connection #1', [o1, o2, o4], db);
var o3a = {y_: 3, sourceBlock_: {}};
db.addConnection_(o3a);
verify_DB_('Adding connection #3a', [o1, o2, o3a, o4], db);
var o3b = {y_: 3, sourceBlock_: {}};
db.addConnection_(o3b);
verify_DB_('Adding connection #3b', [o1, o2, o3b, o3a, o4], db);
function connectionTest_tearDown() {
input = null;
output = null;
previous = null;
next = null;
dummyWorkspace = null;
}
function test_DB_removeConnection() {
var db = new Blockly.ConnectionDB();
var o1 = {y_: 1, sourceBlock_: {}};
var o2 = {y_: 2, sourceBlock_: {}};
var o3a = {y_: 3, sourceBlock_: {}};
var o3b = {y_: 3, sourceBlock_: {}};
var o3c = {y_: 3, sourceBlock_: {}};
var o4 = {y_: 4, sourceBlock_: {}};
db.addConnection_(o1);
db.addConnection_(o2);
db.addConnection_(o3c);
db.addConnection_(o3b);
db.addConnection_(o3a);
db.addConnection_(o4);
verify_DB_('Adding connections 1-4', [o1, o2, o3a, o3b, o3c, o4], db);
/**
* These tests check that the reasons for failures to connect are consistent (internal view of
* error states).
*/
function testCanConnectWithReason_TargetNull() {
connectionTest_setUp();
db.removeConnection_(o2);
verify_DB_('Removing connection #2', [o1, o3a, o3b, o3c, o4], db);
assertEquals(Blockly.Connection.REASON_TARGET_NULL, input.canConnectWithReason_(null));
db.removeConnection_(o4);
verify_DB_('Removing connection #4', [o1, o3a, o3b, o3c], db);
db.removeConnection_(o1);
verify_DB_('Removing connection #1', [o3a, o3b, o3c], db);
db.removeConnection_(o3a);
verify_DB_('Removing connection #3a', [o3b, o3c], db);
db.removeConnection_(o3c);
verify_DB_('Removing connection #3c', [o3b], db);
db.removeConnection_(o3b);
verify_DB_('Removing connection #3b', [], db);
connectionTest_tearDown();
}
function testCanConnectWithReason_Disconnect() {
connectionTest_setUp();
var tempConnection = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
Blockly.Connection.connectReciprocally(input, tempConnection);
assertEquals(Blockly.Connection.REASON_MUST_DISCONNECT, input.canConnectWithReason_(output));
connectionTest_tearDown();
}
function testCanConnectWithReason_DifferentWorkspaces() {
connectionTest_setUp();
input = new Blockly.Connection({workspace: {}}, Blockly.INPUT_VALUE);
output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
assertEquals(Blockly.Connection.REASON_DIFFERENT_WORKSPACES, input.canConnectWithReason_(output));
connectionTest_tearDown();
}
function testCanConnectWithReason_Self() {
connectionTest_setUp();
var block = {type_: "test block"};
input.sourceBlock_ = block;
assertEquals(Blockly.Connection.REASON_SELF_CONNECTION, input.canConnectWithReason_(input));
connectionTest_tearDown();
}
function testCanConnectWithReason_Type() {
connectionTest_setUp();
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(previous));
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(next));
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(previous));
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(next));
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(input));
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(output));
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(input));
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(output));
connectionTest_tearDown();
}
function testCanConnectWithReason_CanConnect() {
connectionTest_setUp();
assertEquals(Blockly.Connection.CAN_CONNECT, previous.canConnectWithReason_(next));
assertEquals(Blockly.Connection.CAN_CONNECT, next.canConnectWithReason_(previous));
assertEquals(Blockly.Connection.CAN_CONNECT, input.canConnectWithReason_(output));
assertEquals(Blockly.Connection.CAN_CONNECT, output.canConnectWithReason_(input));
connectionTest_tearDown();
}
/**
* The next set of tests checks that exceptions are being thrown at the correct times (external
* view of errors).
*/
function testCheckConnection_Self() {
connectionTest_setUp();
var block = {type_: "test block"};
input.sourceBlock_ = block;
try {
input.checkConnection_(input);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_TypeInputPrev() {
connectionTest_setUp();
try {
input.checkConnection_(previous);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_TypeInputNext() {
connectionTest_setUp();
try {
input.checkConnection_(next);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_TypeOutputPrev() {
connectionTest_setUp();
try {
output.checkConnection_(previous);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_TypePrevInput() {
connectionTest_setUp();
try {
previous.checkConnection_(input);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_TypePrevOutput() {
connectionTest_setUp();
try {
previous.checkConnection_(output);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_TypeNextInput() {
connectionTest_setUp();
try {
next.checkConnection_(input);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_TypeNextOutput() {
connectionTest_setUp();
try {
next.checkConnection_(output);
fail();
} catch (e) {
// expected
}
connectionTest_tearDown();
}
function testCheckConnection_Okay() {
connectionTest_setUp();
previous.checkConnection_(next);
next.checkConnection_(previous);
input.checkConnection_(output);
output.checkConnection_(input);
connectionTest_tearDown();
}

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Unit Tests for Horizontal Scratch-Blockly</title>
<script src="../../blockly_uncompressed_horizontal.js"></script>
<script>goog.require('goog.testing.jsunit');</script>
</head>
<body>
<script src="blockly_test.js"></script>
<script src="block_test.js"></script>
<script src="connection_test.js"></script>
<script src="connection_db_test.js"></script>
<script src="generator_test.js"></script>
<script src="names_test.js"></script>
<script src="workspace_test.js"></script>
<script src="xml_test.js"></script>
</body>
</html>

View file

@ -2,17 +2,9 @@
<html>
<head>
<meta charset="utf-8">
<title>Unit Tests for Blockly</title>
<script src="../../blockly_uncompressed.js"></script>
<script>goog.require('goog.testing.jsunit');</script>
</head>
<body>
<script src="blockly_test.js"></script>
<script src="block_test.js"></script>
<script src="connection_test.js"></script>
<script src="generator_test.js"></script>
<script src="names_test.js"></script>
<script src="workspace_test.js"></script>
<script src="xml_test.js"></script>
<a href="./vertical_tests.html">Vertical tests</a>
<a href="./horizontal_tests.html">Horizontal tests</a>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Unit Tests for Vertical Scratch-Blockly</title>
<script src="../../blockly_uncompressed_vertical.js"></script>
<script>goog.require('goog.testing.jsunit');</script>
</head>
<body>
<script src="blockly_test.js"></script>
<script src="block_test.js"></script>
<script src="connection_test.js"></script>
<script src="generator_test.js"></script>
<script src="connection_db_test.js"></script>
<script src="names_test.js"></script>
<script src="workspace_test.js"></script>
<script src="xml_test.js"></script>
</body>
</html>

207
tests/multi_playground.html Normal file
View file

@ -0,0 +1,207 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Multi-toolbox Playground</title>
<script src="../blockly_uncompressed_horizontal.js"></script>
<script src="../msg/messages.js"></script>
<script src="../blocks/math.js"></script>
<script src="../blocks_horizontal/event.js"></script>
<script src="../blocks_horizontal/control.js"></script>
<script src="../blocks_horizontal/motion.js"></script>
<script>
'use strict';
function start() {
startBlocklyInstance('VertStartLTR', false, false, 'start');
startBlocklyInstance('VertStartRTL', true, false, 'start');
startBlocklyInstance('VertEndLTR', false, false, 'end');
startBlocklyInstance('VertEndRTL', true, false, 'end');
startBlocklyInstance('HorizontalStartLTR', false, true, 'start');
startBlocklyInstance('HorizontalStartRTL', true, true, 'start');
startBlocklyInstance('HorizontalEndLTR', false, true, 'end');
startBlocklyInstance('HorizontalEndRTL', true, true, 'end');
}
function startBlocklyInstance(suffix, rtl, horizontalLayout, position) {
var toolbox = document.getElementById('toolbox_categories');
var options = {
comments: false,
disable: false,
collapse: false,
maxBlocks: Infinity,
media: '../media/',
readOnly: false,
rtl: rtl,
scrollbars: true,
toolbox: toolbox,
trashcan: true,
horizontalLayout: horizontalLayout,
toolboxPosition: position,
zoom: {
controls: true,
wheel: false,
startScale: 1.0,
maxScale: 4,
minScale: 0.25,
scaleSpeed: 1.1
},
};
Blockly.inject('blocklyDiv' + suffix, options);
}
</script>
<style>
html, body {
height: 100%;
}
body {
background-color: #fff;
font-family: sans-serif;
}
h1 {
font-weight: normal;
font-size: 140%;
}
#blocklyDiv {
float: right;
height: 95%;
width: 70%;
}
#collaborators {
float: right;
width: 30px;
margin-left: 10px;
}
#collaborators > img {
margin-right: 5px;
height: 30px;
padding-bottom: 5px;
width: 30px;
border-radius: 3px;
}
#importExport {
font-family: monospace;
}
</style>
</head>
<body onload="start()">
<div id="collaborators"></div>
<table>
<tr>
<td/>
<td>LTR</td>
<td>RTL</td>
</tr>
<tr>
<td>Vertical layout; toolbox at start</td>
<td>
<div id="blocklyDivVertStartLTR" style="height: 480px; width: 600px;"></div>
</td>
<td>
<div id="blocklyDivVertStartRTL" style="height: 480px; width: 600px;"></div>
</td>
</tr>
<tr>
<td>Vertical layout; toolbox at end</td>
<td>
<div id="blocklyDivVertEndLTR" style="height: 480px; width: 600px;"></div>
</td>
<td>
<div id="blocklyDivVertEndRTL" style="height: 480px; width: 600px;"></div>
</td>
</tr>
<tr>
<td>Horizontal layout; toolbox at start</td>
<td>
<div id="blocklyDivHorizontalStartLTR" style="height: 480px; width: 600px;"></div>
</td>
<td>
<div id="blocklyDivHorizontalStartRTL" style="height: 480px; width: 600px;"></div>
</td>
</tr>
<tr>
<td>Horizontal layout; toolbox at end</td>
<td>
<div id="blocklyDivHorizontalEndLTR" style="height: 480px; width: 600px;"></div>
</td>
<td>
<div id="blocklyDivHorizontalEndRTL" style="height: 480px; width: 600px;"></div>
</td>
</tr>
</table>
<xml id="toolbox_alwaysOpen" style="display: none">
<block type="event_whenflagclicked"></block>
<block type="motion_moveright"></block>
<!-- <block type="control_repeat"></block> -->
<block type="control_forever"></block>
<block type="control_repeat">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
</xml>
<xml id="toolbox_categoriesScroll" style="display: none">
<category name="Events">
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
<block type="event_whenflagclicked"></block>
</category>
<sep></sep>
<category name="Motion">
<block type="motion_moveright"></block>
</category>
<!-- <block type="control_repeat"></block> -->
<category name="Pants">
<block type="control_forever"></block>
<block type="control_repeat">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
</category>
</xml>
<xml id="toolbox_categories" style="display: none">
<category name="Events">
<block type="event_whenflagclicked"></block>
</category>
<category name="Motion">
<block type="motion_moveright"></block>
</category>
<!-- <block type="control_repeat"></block> -->
<category name="Pants">
<block type="control_forever"></block>
<block type="control_repeat">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
</category>
</xml>
</body>
</html>

BIN
tests/spec.png Normal file

Binary file not shown.

After

(image error) Size: 74 KiB

128
tests/spectool.html Normal file
View file

@ -0,0 +1,128 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Spec tool</title>
<script src="../blockly_uncompressed_horizontal.js"></script>
<script src="../generators/javascript.js"></script>
<script src="../generators/javascript/control.js"></script>
<script src="../generators/javascript/looks.js"></script>
<script src="../generators/javascript/math.js"></script>
<script src="../generators/javascript/motion.js"></script>
<script src="../generators/javascript/event.js"></script>
<script src="../msg/messages.js"></script>
<script src="../blocks/math.js"></script>
<script src="../blocks/text.js"></script>
<script src="../blocks_horizontal/control.js"></script>
<script src="../blocks_horizontal/event.js"></script>
<script src="../blocks_horizontal/looks.js"></script>
<script src="../blocks_horizontal/motion.js"></script>
<script>
'use strict';
// Depending on the URL argument, render as LTR or RTL.
var rtl = (document.location.search == '?rtl');
var workspace = null;
function start() {
var toolbox = document.getElementById('toolbox');
workspace = Blockly.inject('blocklyDiv', {
comments: false,
disable: false,
collapse: false,
maxBlocks: Infinity,
media: '../media/',
readOnly: false,
rtl: rtl,
scrollbars: true,
toolbox: toolbox,
trashcan: true,
horizontalLayout: true,
toolboxPosition: 'start',
zoom: {
controls: true,
wheel: false,
startScale: 8.0,
maxScale: 10,
minScale: 0.25,
scaleSpeed: 1.1
},
grid:
{spacing: 4,
length: 50,
colour: '#ccc',
snap: false},
});
}
</script>
<style>
html, body {
height: 100%;
}
body {
background-color: #fff;
font-family: sans-serif;
overflow: hidden;
}
h1 {
font-weight: normal;
font-size: 140%;
}
#blocklyDiv {
float: right;
height: 95%;
width: 90%;
}
.blocklySvg {
background-image: url(./spec.png) !important;
background-repeat: no-repeat;
}
.blocklyWorkspace {
opacity: .8;
}
</style>
</head>
<body onload="start()">
<div id="blocklyDiv"></div>
<xml id="toolbox" style="display: none">
<category name="Looks">
<block type="looks_say">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">Hey!</field>
</shadow>
</value>
</block>
</category>
<category name="Events">
<block type="event_whenflagclicked"></block>
</category>
<category name="Motion">
<block type="motion_moveright"></block>
</category>
<!-- <block type="control_repeat"></block> -->
<category name="Pants">
<block type="control_forever"></block>
<block type="control_repeat">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
</category>
</xml>
<h1>Spec tool!</h1>
<script>
if (rtl) {
document.write('[ &larr; RTL. Switch to <A HREF="?ltr">LTR</A>. ]');
} else {
document.write('[ &rarr; LTR. Switch to <A HREF="?rtl">RTL</A>. ]');
}
</script>
</body>
</html>

View file

@ -1,7 +1,7 @@
import UIKit
import WebKit
class ViewController: UIViewController {
class ViewController: UIViewController, WKUIDelegate {
var webview : WKWebView?
override func viewDidLoad() {
@ -19,6 +19,23 @@ class ViewController: UIViewController {
let url = (wwwSource != nil && wwwSource!.characters.count > 0) ? NSURL(string:wwwSource!) : NSURL.fileURLWithPath(bundleSource!)
let req = NSURLRequest(URL:url!)
webview!.loadRequest(req)
webview!.UIDelegate = self
}
func webView(webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: (String?) -> Void) {
let alertController = UIAlertController(title: "", message: prompt, preferredStyle: .Alert)
weak var alertTextField: UITextField!
alertController.addTextFieldWithConfigurationHandler { textField in
textField.text = defaultText
alertTextField = textField
}
alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { action in
completionHandler(nil)
}))
alertController.addAction(UIAlertAction(title: "OK", style: .Default, handler: { action in
completionHandler(alertTextField.text)
}))
self.presentViewController(alertController, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {