From 74fa3bb71a092ef00be9b3da768d01bb812167cb Mon Sep 17 00:00:00 2001 From: alschmiedt Date: Tue, 13 Aug 2019 13:37:01 -0700 Subject: [PATCH] Initial commit for changing key mappings (#2787) * Added ability to easily change key mappings --- blockly_compressed.js | 123 ++++++++-------- blockly_uncompressed.js | 23 ++- core/blockly.js | 7 +- core/inject.js | 1 + core/keyboard_nav/key_action.js | 42 ++++++ core/keyboard_nav/key_map.js | 142 ++++++++++++++++++ core/keyboard_nav/navigation.js | 248 ++++++++++++++++---------------- core/options.js | 2 + tests/mocha/index.html | 1 + tests/mocha/key_map_test.js | 66 +++++++++ tests/mocha/navigation_test.js | 65 ++++----- 11 files changed, 494 insertions(+), 226 deletions(-) create mode 100644 core/keyboard_nav/key_action.js create mode 100644 core/keyboard_nav/key_map.js create mode 100644 tests/mocha/key_map_test.js diff --git a/blockly_compressed.js b/blockly_compressed.js index dabc3fab9..55a0666e6 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -1061,11 +1061,11 @@ Blockly.Comment.prototype.setVisible=function(a){if(a!=this.isVisible())if(Block this.updateColour()):(this.bubble_.dispose(),this.foreignObject_=this.textarea_=this.bubble_=null);this.setText(b);this.setBubbleSize(c.width,c.height)}};Blockly.Comment.prototype.textareaFocus_=function(a){this.bubble_.promote_()&&this.textarea_.focus()};Blockly.Comment.prototype.getBubbleSize=function(){return this.isVisible()?this.bubble_.getBubbleSize():{width:this.width_,height:this.height_}}; Blockly.Comment.prototype.setBubbleSize=function(a,b){this.textarea_?this.bubble_.setBubbleSize(a,b):(this.width_=a,this.height_=b)};Blockly.Comment.prototype.getText=function(){return this.textarea_?this.textarea_.value:this.text_};Blockly.Comment.prototype.setText=function(a){this.text_!=a&&(Blockly.Events.fire(new Blockly.Events.BlockChange(this.block_,"comment",null,this.text_,a)),this.text_=a);this.textarea_&&(this.textarea_.value=a)}; Blockly.Comment.prototype.dispose=function(){Blockly.Events.isEnabled()&&this.setText("");this.block_.comment=null;Blockly.Icon.prototype.dispose.call(this)};Blockly.Connection=function(a,b){this.sourceBlock_=a;this.type=b;a.workspace.connectionDBList&&(this.db_=a.workspace.connectionDBList[b],this.dbOpposite_=a.workspace.connectionDBList[Blockly.OPPOSITE_TYPE[b]],this.hidden_=!this.db_)};Blockly.Connection.CAN_CONNECT=0;Blockly.Connection.REASON_SELF_CONNECTION=1;Blockly.Connection.REASON_WRONG_TYPE=2;Blockly.Connection.REASON_TARGET_NULL=3;Blockly.Connection.REASON_CHECKS_FAILED=4;Blockly.Connection.REASON_DIFFERENT_WORKSPACES=5; -Blockly.Connection.REASON_SHADOW_PARENT=6;Blockly.Connection.prototype.targetConnection=null;Blockly.Connection.prototype.check_=null;Blockly.Connection.prototype.shadowDom_=null;Blockly.Connection.prototype.x_=0;Blockly.Connection.prototype.y_=0;Blockly.Connection.prototype.inDB_=!1;Blockly.Connection.prototype.db_=null;Blockly.Connection.prototype.dbOpposite_=null;Blockly.Connection.prototype.hidden_=null; +Blockly.Connection.REASON_SHADOW_PARENT=6;Blockly.Connection.prototype.targetConnection=null;Blockly.Connection.prototype.disposed=!1;Blockly.Connection.prototype.check_=null;Blockly.Connection.prototype.shadowDom_=null;Blockly.Connection.prototype.x_=0;Blockly.Connection.prototype.y_=0;Blockly.Connection.prototype.inDB_=!1;Blockly.Connection.prototype.db_=null;Blockly.Connection.prototype.dbOpposite_=null;Blockly.Connection.prototype.hidden_=null; Blockly.Connection.prototype.connect_=function(a){var b=this,c=b.getSourceBlock(),d=a.getSourceBlock();a.isConnected()&&a.disconnect();if(b.isConnected()){var e=b.targetBlock(),f=b.getShadowDom();b.setShadowDom(null);if(e.isShadow())f=Blockly.Xml.blockToDom(e),e.dispose(),e=null;else if(b.type==Blockly.INPUT_VALUE){if(!e.outputConnection)throw Error("Orphan block does not have an output connection.");var g=Blockly.Connection.lastConnectionInRow_(d,e);g&&(e.outputConnection.connect(g),e=null)}else if(b.type== Blockly.NEXT_STATEMENT){if(!e.previousConnection)throw Error("Orphan block does not have a previous connection.");for(g=d;g.nextConnection;){var h=g.getNextBlock();if(h&&!h.isShadow())g=h;else{e.previousConnection.checkType_(g.nextConnection)&&(g.nextConnection.connect(e.previousConnection),e=null);break}}}if(e&&(b.disconnect(),Blockly.Events.recordUndo)){var k=Blockly.Events.getGroup();setTimeout(function(){e.workspace&&!e.getParent()&&(Blockly.Events.setGroup(k),e.outputConnection?e.outputConnection.bumpAwayFrom_(b): -e.previousConnection&&e.previousConnection.bumpAwayFrom_(b),Blockly.Events.setGroup(!1))},Blockly.BUMP_DELAY)}b.setShadowDom(f)}var l;Blockly.Events.isEnabled()&&(l=new Blockly.Events.BlockMove(d));Blockly.Connection.connectReciprocally_(b,a);d.setParent(c);l&&(l.recordNew(),Blockly.Events.fire(l))};Blockly.Connection.prototype.dispose=function(){if(this.isConnected())throw Error("Disconnect connection before disposing of it.");this.inDB_&&this.db_.removeConnection_(this)}; -Blockly.Connection.prototype.getSourceBlock=function(){return this.sourceBlock_};Blockly.Connection.prototype.isSuperior=function(){return this.type==Blockly.INPUT_VALUE||this.type==Blockly.NEXT_STATEMENT};Blockly.Connection.prototype.isConnected=function(){return!!this.targetConnection}; +e.previousConnection&&e.previousConnection.bumpAwayFrom_(b),Blockly.Events.setGroup(!1))},Blockly.BUMP_DELAY)}b.setShadowDom(f)}var l;Blockly.Events.isEnabled()&&(l=new Blockly.Events.BlockMove(d));Blockly.Connection.connectReciprocally_(b,a);d.setParent(c);l&&(l.recordNew(),Blockly.Events.fire(l))}; +Blockly.Connection.prototype.dispose=function(){if(this.isConnected()){this.setShadowDom(null);var a=this.targetBlock();a.isShadow()?a.dispose():a.unplug()}this.inDB_&&this.db_.removeConnection_(this);this.disposed=!0};Blockly.Connection.prototype.getSourceBlock=function(){return this.sourceBlock_};Blockly.Connection.prototype.isSuperior=function(){return this.type==Blockly.INPUT_VALUE||this.type==Blockly.NEXT_STATEMENT};Blockly.Connection.prototype.isConnected=function(){return!!this.targetConnection}; Blockly.Connection.prototype.canConnectWithReason_=function(a){if(!a)return Blockly.Connection.REASON_TARGET_NULL;if(this.isSuperior())var b=this.sourceBlock_,c=a.getSourceBlock();else c=this.sourceBlock_,b=a.getSourceBlock();return b&&b==c?Blockly.Connection.REASON_SELF_CONNECTION:a.type!=Blockly.OPPOSITE_TYPE[this.type]?Blockly.Connection.REASON_WRONG_TYPE:b&&c&&b.workspace!==c.workspace?Blockly.Connection.REASON_DIFFERENT_WORKSPACES:this.checkType_(a)?b.isShadow()&&!c.isShadow()?Blockly.Connection.REASON_SHADOW_PARENT: Blockly.Connection.CAN_CONNECT:Blockly.Connection.REASON_CHECKS_FAILED}; Blockly.Connection.prototype.checkConnection_=function(a){switch(this.canConnectWithReason_(a)){case Blockly.Connection.CAN_CONNECT:break;case Blockly.Connection.REASON_SELF_CONNECTION:throw Error("Attempted to connect a block to itself.");case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:throw Error("Blocks not on same workspace.");case Blockly.Connection.REASON_WRONG_TYPE:throw Error("Attempt to connect incompatible types.");case Blockly.Connection.REASON_TARGET_NULL:throw Error("Target connection is null."); @@ -1219,9 +1219,9 @@ Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_ Blockly.Grid.prototype.update=function(a){this.scale_=a;var b=this.spacing_*a||100;this.gridPattern_.setAttribute("width",b);this.gridPattern_.setAttribute("height",b);b=Math.floor(this.spacing_/2)+.5;var c=b-this.length_/2,d=b+this.length_/2;b*=a;c*=a;d*=a;this.setLineAttributes_(this.line1_,a,c,d,b,b);this.setLineAttributes_(this.line2_,a,b,b,c,d)}; Blockly.Grid.prototype.setLineAttributes_=function(a,b,c,d,e,f){a&&(a.setAttribute("stroke-width",b),a.setAttribute("x1",c),a.setAttribute("y1",e),a.setAttribute("x2",d),a.setAttribute("y2",f))};Blockly.Grid.prototype.moveTo=function(a,b){this.gridPattern_.setAttribute("x",a);this.gridPattern_.setAttribute("y",b);(Blockly.utils.userAgent.IE||Blockly.utils.userAgent.EDGE)&&this.update(this.scale_)}; Blockly.Grid.createDom=function(a,b,c){a=Blockly.utils.dom.createSvgElement("pattern",{id:"blocklyGridPattern"+a,patternUnits:"userSpaceOnUse"},c);0 document.");}else a=null;return a};Blockly.ScrollbarPair=function(a){this.workspace_=a;this.hScroll=new Blockly.Scrollbar(a,!0,!0,"blocklyMainWorkspaceScrollbar");this.vScroll=new Blockly.Scrollbar(a,!1,!0,"blocklyMainWorkspaceScrollbar");this.corner_=Blockly.utils.dom.createSvgElement("rect",{height:Blockly.Scrollbar.scrollbarThickness,width:Blockly.Scrollbar.scrollbarThickness,"class":"blocklyScrollbarBackground"},null);Blockly.utils.dom.insertAfter(this.corner_,a.getBubbleCanvas())}; @@ -1374,8 +1374,8 @@ b+=10};k={enabled:k};k.text=Blockly.Msg.COLLAPSE_ALL;k.callback=function(){m(!0) c()})}};d.push(h);this.configureContextMenu&&this.configureContextMenu(d);Blockly.ContextMenu.show(a,d,this.RTL)}}; Blockly.WorkspaceSvg.prototype.updateToolbox=function(a){if(a=Blockly.Options.parseToolboxTree(a)){if(!this.options.languageTree)throw Error("Existing toolbox is null. Can't create new toolbox.");if(a.getElementsByTagName("category").length){if(!this.toolbox_)throw Error("Existing toolbox has no categories. Can't change mode.");this.options.languageTree=a;a=this.toolbox_.populate_(a);this.toolbox_.addColour_();this.toolbox_.position();this.toolbox_.tree_.setSelectedItem(a)}else{if(!this.flyout_)throw Error("Existing toolbox has categories. Can't change mode."); this.options.languageTree=a;this.flyout_.show(a.childNodes)}}else if(this.options.languageTree)throw Error("Can't nullify an existing toolbox.");};Blockly.WorkspaceSvg.prototype.markFocused=function(){this.options.parentWorkspace?this.options.parentWorkspace.markFocused():(Blockly.mainWorkspace=this,this.setBrowserFocus())};Blockly.WorkspaceSvg.prototype.setBrowserFocus=function(){document.activeElement&&document.activeElement.blur();try{this.getParentSvg().focus()}catch(a){try{this.getParentSvg().parentNode.setActive()}catch(b){this.getParentSvg().parentNode.focus()}}}; -Blockly.WorkspaceSvg.prototype.zoom=function(a,b,c){c=Math.pow(this.options.zoomOptions.scaleSpeed,c);var d=this.scale*c;if(this.scale!=d){d>this.options.zoomOptions.maxScale?c=this.options.zoomOptions.maxScale/this.scale:dthis.options.zoomOptions.maxScale?c=this.options.zoomOptions.maxScale/this.scale:dthis.maxDisplayLength&&(a=a.substring(0,this.maxDisplayLength-2)+"\u2026");a=a.replace(/\s/g,Blockly.Field.NBSP);this.sourceBlock_.RTL&&(a+="\u200f");return a};Blockly.Field.prototype.getText=function(){return this.text_};Blockly.Field.prototype.setText=function(a){null!==a&&(a=String(a),a!==this.text_&&(this.text_=a,this.forceRerender()))}; Blockly.Field.prototype.forceRerender=function(){this.isDirty_=!0;this.sourceBlock_&&this.sourceBlock_.rendered&&(this.sourceBlock_.render(),this.sourceBlock_.bumpNeighbours_())}; Blockly.Field.prototype.setValue=function(a){if(null!==a){var b=this.doClassValidation_(a);a=this.processValidation_(a,b);if(!(a instanceof Error)){if(b=this.getValidator())if(b=b.call(this,a),a=this.processValidation_(a,b),a instanceof Error)return;b=this.getValue();b!==a&&(this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.BlockChange(this.sourceBlock_,"field",this.name,b,a)),this.doValueUpdate_(a),this.isDirty_&&this.forceRerender())}}}; Blockly.Field.prototype.processValidation_=function(a,b){if(null===b)return this.doValueInvalid_(a),this.isDirty_&&this.forceRerender(),Error();void 0!==b&&(a=b);return a};Blockly.Field.prototype.getValue=function(){return this.value_};Blockly.Field.prototype.doClassValidation_=function(a){return a=this.classValidator(a)};Blockly.Field.prototype.doValueUpdate_=function(a){this.value_=a;this.isDirty_=!0;this.text_=String(a)};Blockly.Field.prototype.doValueInvalid_=function(a){}; Blockly.Field.prototype.onMouseDown_=function(a){this.sourceBlock_&&this.sourceBlock_.workspace&&(a=this.sourceBlock_.workspace.getGesture(a))&&a.setStartField(this)};Blockly.Field.prototype.setTooltip=function(a){var b=this.getClickTarget_();b?b.tooltip=a||""===a?a:this.sourceBlock_:this.tooltip_=a};Blockly.Field.prototype.getClickTarget_=function(){return this.clickTarget_||this.getSvgRoot()};Blockly.Field.prototype.getAbsoluteXY_=function(){return Blockly.utils.style.getPageOffset(this.borderRect_)}; -Blockly.Field.prototype.referencesVariables=function(){return!1};Blockly.Field.prototype.getParentInput=function(){for(var a=null,b=this.sourceBlock_,c=b.inputList,d=0;da||a>this.fieldRow.length)throw Error("index "+a+" out of bounds.");if(!b&&!c)return a;"string"==typeof b&&(b=new Blockly.FieldLabel(b));b.setSourceBlock(this.sourceBlock_);this.sourceBlock_.rendered&&b.init();b.name=c;b.prefixField&&(a=this.insertFieldAt(a,b.prefixField));this.fieldRow.splice(a,0,b);++a;b.suffixField&&(a=this.insertFieldAt(a,b.suffixField));this.sourceBlock_.rendered&&(this.sourceBlock_.render(),this.sourceBlock_.bumpNeighbours_()); return a};Blockly.Input.prototype.removeField=function(a){for(var b=0,c;c=this.fieldRow[b];b++)if(c.name===a){c.dispose();this.fieldRow.splice(b,1);this.sourceBlock_.rendered&&(this.sourceBlock_.render(),this.sourceBlock_.bumpNeighbours_());return}throw Error('Field "%s" not found.',a);};Blockly.Input.prototype.isVisible=function(){return this.visible_}; Blockly.Input.prototype.setVisible=function(a){var b=[];if(this.visible_==a)return b;for(var c=(this.visible_=a)?"block":"none",d=0,e;e=this.fieldRow[d];d++)e.setVisible(a);this.connection&&(a?b=this.connection.unhideAll():this.connection.hideAll(),d=this.connection.targetBlock())&&(d.getSvgRoot().style.display=c,a||(d.rendered=!1));return b};Blockly.Input.prototype.setCheck=function(a){if(!this.connection)throw Error("This input does not have a connection.");this.connection.setCheck(a);return this}; @@ -1454,11 +1456,11 @@ Blockly.Warning.prototype.setVisible=function(a){if(a!=this.isVisible())if(Block a=this.bubble_.getBubbleSize();this.bubble_.setBubbleSize(a.width,a.height)}else this.bubble_.dispose(),this.body_=this.bubble_=null};Blockly.Warning.prototype.bodyFocus_=function(a){this.bubble_.promote_()};Blockly.Warning.prototype.setText=function(a,b){this.text_[b]!=a&&(a?this.text_[b]=a:delete this.text_[b],this.isVisible()&&(this.setVisible(!1),this.setVisible(!0)))};Blockly.Warning.prototype.getText=function(){var a=[],b;for(b in this.text_)a.push(this.text_[b]);return a.join("\n")}; Blockly.Warning.prototype.dispose=function(){this.block_.warning=null;Blockly.Icon.prototype.dispose.call(this)};Blockly.Block=function(a,b,c){if("undefined"!==typeof Blockly.Generator.prototype[b])throw Error('Block prototypeName "'+b+'" conflicts with Blockly.Generator members.');this.id=c&&!a.getBlockById(c)?c:Blockly.utils.genUid();a.blockDB_[this.id]=this;this.previousConnection=this.nextConnection=this.outputConnection=null;this.inputList=[];this.inputsInline=void 0;this.disabled=!1;this.tooltip="";this.contextMenu=!0;this.parentBlock_=null;this.childBlocks_=[];this.editable_=this.movable_=this.deletable_= !0;this.collapsed_=this.isShadow_=!1;this.comment=null;this.xy_=new Blockly.utils.Coordinate(0,0);this.workspace=a;this.isInFlyout=a.isFlyout;this.isInMutator=a.isMutator;this.RTL=a.RTL;this.isInsertionMarker_=!1;this.hat=void 0;if(b){this.type=b;c=Blockly.Blocks[b];if(!c||"object"!=typeof c)throw TypeError("Unknown block type: "+b);goog.mixin(this,c)}a.addTopBlock(this);a.addTypedBlock(this);"function"==typeof this.init&&this.init();this.inputsInlineDefault=this.inputsInline;if(Blockly.Events.isEnabled()){(a= -Blockly.Events.getGroup())||Blockly.Events.setGroup(!0);try{Blockly.Events.fire(new Blockly.Events.BlockCreate(this))}finally{a||Blockly.Events.setGroup(!1)}}"function"==typeof this.onchange&&this.setOnChange(this.onchange)};Blockly.Block.obtain=function(a,b){console.warn("Deprecated call to Blockly.Block.obtain, use workspace.newBlock instead.");return a.newBlock(b)};Blockly.Block.prototype.data=null;Blockly.Block.prototype.hue_=null;Blockly.Block.prototype.colour_="#000000"; -Blockly.Block.prototype.colourSecondary_=null;Blockly.Block.prototype.colourTertiary_=null;Blockly.Block.prototype.styleName_=null; +Blockly.Events.getGroup())||Blockly.Events.setGroup(!0);try{Blockly.Events.fire(new Blockly.Events.BlockCreate(this))}finally{a||Blockly.Events.setGroup(!1)}}"function"==typeof this.onchange&&this.setOnChange(this.onchange)};Blockly.Block.obtain=function(a,b){console.warn("Deprecated call to Blockly.Block.obtain, use workspace.newBlock instead.");return a.newBlock(b)};Blockly.Block.prototype.data=null;Blockly.Block.prototype.disposed=!1;Blockly.Block.prototype.hue_=null; +Blockly.Block.prototype.colour_="#000000";Blockly.Block.prototype.colourSecondary_=null;Blockly.Block.prototype.colourTertiary_=null;Blockly.Block.prototype.styleName_=null; Blockly.Block.prototype.dispose=function(a){if(this.workspace){this.onchangeWrapper_&&this.workspace.removeChangeListener(this.onchangeWrapper_);this.unplug(a);Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.BlockDelete(this));Blockly.Events.disable();try{this.workspace&&(this.workspace.removeTopBlock(this),this.workspace.removeTypedBlock(this),delete this.workspace.blockDB_[this.id],this.workspace=null);Blockly.selected==this&&(Blockly.selected=null);for(var b=this.childBlocks_.length- -1;0<=b;b--)this.childBlocks_[b].dispose(!1);b=0;for(var c;c=this.inputList[b];b++)c.dispose();this.inputList.length=0;var d=this.getConnections_(!0);for(b=0;b=this.inputList.length)throw RangeError("Input index "+a+" out of bounds.");if(b>this.inputList.length)throw RangeError("Reference input "+b+" out of bounds.");var c=this.inputList[a];this.inputList.splice(a,1);aa&&(a+=360);a>Blockly.FieldAngle.WRAP&&(a-=360);return a};Blockly.Field.register("field_angle",Blockly.FieldAngle);Blockly.FieldCheckbox=function(a,b){a=this.doClassValidation_(a);null===a&&(a="FALSE");Blockly.FieldCheckbox.superClass_.constructor.call(this,a,b);this.size_.width=Blockly.FieldCheckbox.WIDTH};goog.inherits(Blockly.FieldCheckbox,Blockly.Field);Blockly.FieldCheckbox.fromJson=function(a){return new Blockly.FieldCheckbox(a.checked)};Blockly.FieldCheckbox.WIDTH=5;Blockly.FieldCheckbox.CHECK_CHAR="\u2713";Blockly.FieldCheckbox.CHECK_X_OFFSET=-3;Blockly.FieldCheckbox.CHECK_Y_OFFSET=14; -Blockly.FieldCheckbox.prototype.SERIALIZABLE=!0;Blockly.FieldCheckbox.prototype.CURSOR="default";Blockly.FieldCheckbox.prototype.isDirty_=!1; +Blockly.FieldAngle.prototype.doClassValidation_=function(a){a=Number(a)%360;if(isNaN(a))return null;0>a&&(a+=360);a>Blockly.FieldAngle.WRAP&&(a-=360);return a};Blockly.Field.register("field_angle",Blockly.FieldAngle);Blockly.FieldCheckbox=function(a,b){a=this.doClassValidation_(a);null===a&&(a="FALSE");Blockly.FieldCheckbox.superClass_.constructor.call(this,a,b);this.size_.width=Blockly.FieldCheckbox.WIDTH};goog.inherits(Blockly.FieldCheckbox,Blockly.Field);Blockly.FieldCheckbox.fromJson=function(a){return new Blockly.FieldCheckbox(a.checked)};Blockly.FieldCheckbox.WIDTH=15;Blockly.FieldCheckbox.CHECK_CHAR="\u2713";Blockly.FieldCheckbox.CHECK_X_OFFSET=Blockly.Field.DEFAULT_TEXT_OFFSET-3; +Blockly.FieldCheckbox.CHECK_Y_OFFSET=14;Blockly.FieldCheckbox.prototype.SERIALIZABLE=!0;Blockly.FieldCheckbox.prototype.CURSOR="default";Blockly.FieldCheckbox.prototype.isDirty_=!1; Blockly.FieldCheckbox.prototype.initView=function(){Blockly.FieldCheckbox.superClass_.initView.call(this);this.textElement_.setAttribute("x",Blockly.FieldCheckbox.CHECK_X_OFFSET);this.textElement_.setAttribute("y",Blockly.FieldCheckbox.CHECK_Y_OFFSET);Blockly.utils.dom.addClass(this.textElement_,"blocklyCheckbox");var a=document.createTextNode(Blockly.FieldCheckbox.CHECK_CHAR);this.textElement_.appendChild(a);this.textElement_.style.display=this.value_?"block":"none"}; Blockly.FieldCheckbox.prototype.showEditor_=function(){this.setValue(!this.value_)};Blockly.FieldCheckbox.prototype.doClassValidation_=function(a){return!0===a||"TRUE"===a?"TRUE":!1===a||"FALSE"===a?"FALSE":null};Blockly.FieldCheckbox.prototype.doValueUpdate_=function(a){this.value_=this.convertValueToBool_(a);this.textElement_&&(this.textElement_.style.display=this.value_?"block":"none")};Blockly.FieldCheckbox.prototype.getValue=function(){return this.value_?"TRUE":"FALSE"}; -Blockly.FieldCheckbox.prototype.getValueBoolean=function(){return this.value_};Blockly.FieldCheckbox.prototype.getText=function(){return String(this.convertValueToBool_(this.value_))};Blockly.FieldCheckbox.prototype.convertValueToBool_=function(a){return"string"==typeof a?"TRUE"==a:!!a};Blockly.FieldCheckbox.prototype.getCorrectedSize=function(){this.getSize();return new Blockly.utils.Size(this.size_.width+Blockly.BlockSvg.SEP_SPACE_X,Blockly.Field.BORDER_RECT_DEFAULT_HEIGHT)}; -Blockly.Field.register("field_checkbox",Blockly.FieldCheckbox);Blockly.utils.colour={};Blockly.utils.colour.parse=function(a){a=String(a).toLowerCase().trim();var b=Blockly.utils.colour.names[a];if(b)return b;b="#"==a[0]?a:"#"+a;if(/^#[0-9a-f]{6}$/.test(b))return b;if(/^#[0-9a-f]{3}$/.test(b))return["#",b[1],b[1],b[2],b[2],b[3],b[3]].join("");var c=a.match(/^(?:rgb)?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);return c&&(a=Number(c[1]),b=Number(c[2]),c=Number(c[3]),0<=a&&256>a&&0<=b&&256>b&&0<=c&&256>c)?Blockly.utils.colour.rgbToHex(a,b,c):null}; +Blockly.FieldCheckbox.prototype.getValueBoolean=function(){return this.value_};Blockly.FieldCheckbox.prototype.getText=function(){return String(this.convertValueToBool_(this.value_))};Blockly.FieldCheckbox.prototype.convertValueToBool_=function(a){return"string"==typeof a?"TRUE"==a:!!a};Blockly.Field.register("field_checkbox",Blockly.FieldCheckbox);Blockly.utils.colour={};Blockly.utils.colour.parse=function(a){a=String(a).toLowerCase().trim();var b=Blockly.utils.colour.names[a];if(b)return b;b="#"==a[0]?a:"#"+a;if(/^#[0-9a-f]{6}$/.test(b))return b;if(/^#[0-9a-f]{3}$/.test(b))return["#",b[1],b[1],b[2],b[2],b[3],b[3]].join("");var c=a.match(/^(?:rgb)?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);return c&&(a=Number(c[1]),b=Number(c[2]),c=Number(c[3]),0<=a&&256>a&&0<=b&&256>b&&0<=c&&256>c)?Blockly.utils.colour.rgbToHex(a,b,c):null}; Blockly.utils.colour.rgbToHex=function(a,b,c){b=a<<16|b<<8|c;return 16>a?"#"+(16777216|b).toString(16).substr(1):"#"+b.toString(16)};Blockly.utils.colour.hexToRgb=function(a){a=parseInt(a.substr(1),16);return[a>>16,a>>8&255,a&255]}; Blockly.utils.colour.hsvToHex=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=c;else{var g=Math.floor(a/60),h=a/60-g;a=c*(1-b);var k=c*(1-b*h);b=c*(1-b*(1-h));switch(g){case 1:d=k;e=c;f=a;break;case 2:d=a;e=c;f=b;break;case 3:d=a;e=k;f=c;break;case 4:d=b;e=a;f=c;break;case 5:d=c;e=a;f=k;break;case 6:case 0:d=c,e=b,f=a}}return Blockly.utils.colour.rgbToHex(Math.floor(d),Math.floor(e),Math.floor(f))}; Blockly.utils.colour.blend=function(a,b,c){a=Blockly.utils.colour.hexToRgb(Blockly.utils.colour.parse(a));b=Blockly.utils.colour.hexToRgb(Blockly.utils.colour.parse(b));return Blockly.utils.colour.rgbToHex(Math.round(b[0]+c*(a[0]-b[0])),Math.round(b[1]+c*(a[1]-b[1])),Math.round(b[2]+c*(a[2]-b[2])))}; -Blockly.utils.colour.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00"};Blockly.FieldColour=function(a,b){a=this.doClassValidation_(a);null===a&&(a=Blockly.FieldColour.COLOURS[0]);Blockly.FieldColour.superClass_.constructor.call(this,a,b)};goog.inherits(Blockly.FieldColour,Blockly.Field);Blockly.FieldColour.fromJson=function(a){var b=new Blockly.FieldColour(a.colour);a.colourOptions&&b.setColours(a.colourOptions,a.colourTitles);a.columns&&b.setColumns(a.columns);return b};Blockly.FieldColour.DEFAULT_WIDTH=16;Blockly.FieldColour.DEFAULT_HEIGHT=12; +Blockly.utils.colour.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00"};Blockly.FieldColour=function(a,b){a=this.doClassValidation_(a);null===a&&(a=Blockly.FieldColour.COLOURS[0]);Blockly.FieldColour.superClass_.constructor.call(this,a,b)};goog.inherits(Blockly.FieldColour,Blockly.Field);Blockly.FieldColour.fromJson=function(a){var b=new Blockly.FieldColour(a.colour);a.colourOptions&&b.setColours(a.colourOptions,a.colourTitles);a.columns&&b.setColumns(a.columns);return b};Blockly.FieldColour.DEFAULT_WIDTH=26;Blockly.FieldColour.DEFAULT_HEIGHT=Blockly.Field.BORDER_RECT_DEFAULT_HEIGHT; Blockly.FieldColour.prototype.SERIALIZABLE=!0;Blockly.FieldColour.prototype.CURSOR="default";Blockly.FieldColour.prototype.isDirty_=!1;Blockly.FieldColour.prototype.colours_=null;Blockly.FieldColour.prototype.titles_=null;Blockly.FieldColour.prototype.columns_=0;Blockly.FieldColour.prototype.DROPDOWN_BORDER_COLOUR="silver";Blockly.FieldColour.prototype.DROPDOWN_BACKGROUND_COLOUR="white"; Blockly.FieldColour.prototype.initView=function(){this.size_=new Blockly.utils.Size(Blockly.FieldColour.DEFAULT_WIDTH,Blockly.FieldColour.DEFAULT_HEIGHT);this.createBorderRect_();this.borderRect_.style.fillOpacity=1;this.borderRect_.style.fill=this.value_};Blockly.FieldColour.prototype.doClassValidation_=function(a){return"string"!=typeof a?null:Blockly.utils.colour.parse(a)};Blockly.FieldColour.prototype.doValueUpdate_=function(a){this.value_=a;this.borderRect_&&(this.borderRect_.style.fill=a)}; Blockly.FieldColour.prototype.getText=function(){var a=this.value_;/^#(.)\1(.)\2(.)\3$/.test(a)&&(a="#"+a[1]+a[3]+a[5]);return a};Blockly.FieldColour.COLOURS="#ffffff #cccccc #c0c0c0 #999999 #666666 #333333 #000000 #ffcccc #ff6666 #ff0000 #cc0000 #990000 #660000 #330000 #ffcc99 #ff9966 #ff9900 #ff6600 #cc6600 #993300 #663300 #ffff99 #ffff66 #ffcc66 #ffcc33 #cc9933 #996633 #663333 #ffffcc #ffff33 #ffff00 #ffcc00 #999900 #666600 #333300 #99ff99 #66ff99 #33ff33 #33cc00 #009900 #006600 #003300 #99ffff #33ffff #66cccc #00cccc #339999 #336666 #003333 #ccffff #66ffff #33ccff #3366ff #3333ff #000099 #000066 #ccccff #9999ff #6666cc #6633ff #6600cc #333399 #330099 #ffccff #ff99ff #cc66cc #cc33cc #993399 #663366 #330033".split(" "); Blockly.FieldColour.TITLES=[];Blockly.FieldColour.COLUMNS=7;Blockly.FieldColour.prototype.setColours=function(a,b){this.colours_=a;b&&(this.titles_=b);return this};Blockly.FieldColour.prototype.setColumns=function(a){this.columns_=a;return this}; Blockly.FieldColour.prototype.showEditor_=function(){var a=this.dropdownCreate_();Blockly.DropDownDiv.getContentDiv().appendChild(a);Blockly.DropDownDiv.setColour(this.DROPDOWN_BACKGROUND_COLOUR,this.DROPDOWN_BORDER_COLOUR);Blockly.DropDownDiv.showPositionedByField(this,this.dropdownDispose_.bind(this))};Blockly.FieldColour.prototype.onClick_=function(a){(a=a.target)&&!a.label&&(a=a.parentNode);a=a&&a.label;null!==a&&(this.setValue(a),Blockly.DropDownDiv.hideIfOwner(this))}; Blockly.FieldColour.prototype.dropdownCreate_=function(){var a=this.columns_||Blockly.FieldColour.COLUMNS,b=this.colours_||Blockly.FieldColour.COLOURS,c=this.titles_||Blockly.FieldColour.TITLES,d=this.getValue(),e=document.createElement("table");e.className="blocklyColourTable";for(var f,g=0;g=this.height_||0>=this.width_)throw Error("Height and width values of an image field must be greater than 0.");this.size_=new Blockly.utils.Size(this.width_,this.height_+2*Blockly.BlockSvg.INLINE_PADDING_Y);this.flipRtl_=f;this.text_= -d||"";this.setValue(a||"");"function"==typeof e&&(this.clickHandler_=e)};goog.inherits(Blockly.FieldImage,Blockly.Field);Blockly.FieldImage.fromJson=function(a){var b=Blockly.utils.replaceMessageReferences(a.src),c=Number(Blockly.utils.replaceMessageReferences(a.width)),d=Number(Blockly.utils.replaceMessageReferences(a.height)),e=Blockly.utils.replaceMessageReferences(a.alt);return new Blockly.FieldImage(b,c,d,e,null,!!a.flipRtl)};Blockly.FieldImage.prototype.EDITABLE=!1; -Blockly.FieldImage.prototype.isDirty_=!1;Blockly.FieldImage.prototype.initView=function(){this.imageElement_=Blockly.utils.dom.createSvgElement("image",{height:this.height_+"px",width:this.width_+"px",alt:this.text_},this.fieldGroup_);this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",this.value_)};Blockly.FieldImage.prototype.doClassValidation_=function(a){return"string"!=typeof a?null:a}; +d[0]+" in: ",d)):(b=!0,console.error("Invalid option["+c+"]: Each FieldDropdown option must be an array. Found: ",d))}if(b)throw TypeError("Found invalid FieldDropdown options.");};Blockly.Field.register("field_dropdown",Blockly.FieldDropdown);Blockly.FieldLabelSerializable=function(a,b){Blockly.FieldLabelSerializable.superClass_.constructor.call(this,a,b)};goog.inherits(Blockly.FieldLabelSerializable,Blockly.FieldLabel);Blockly.FieldLabelSerializable.fromJson=function(a){var b=Blockly.utils.replaceMessageReferences(a.text);return new Blockly.FieldLabelSerializable(b,a["class"])};Blockly.FieldLabelSerializable.prototype.EDITABLE=!1;Blockly.FieldLabelSerializable.prototype.SERIALIZABLE=!0; +Blockly.Field.register("field_label_serializable",Blockly.FieldLabelSerializable);Blockly.FieldImage=function(a,b,c,d,e,f){this.sourceBlock_=null;if(!a)throw Error("Src value of an image field is required");if(isNaN(c)||isNaN(b))throw Error("Height and width values of an image field must cast to numbers.");c=Number(c);b=Number(b);if(0>=c||0>=b)throw Error("Height and width values of an image field must be greater than 0.");this.imageHeight_=c;this.size_=new Blockly.utils.Size(b,c+Blockly.FieldImage.Y_PADDING);this.flipRtl_=f;this.text_=d||"";this.setValue(a||"");"function"==typeof e&& +(this.clickHandler_=e)};goog.inherits(Blockly.FieldImage,Blockly.Field);Blockly.FieldImage.fromJson=function(a){var b=Blockly.utils.replaceMessageReferences(a.src),c=Number(Blockly.utils.replaceMessageReferences(a.width)),d=Number(Blockly.utils.replaceMessageReferences(a.height)),e=Blockly.utils.replaceMessageReferences(a.alt);return new Blockly.FieldImage(b,c,d,e,null,!!a.flipRtl)};Blockly.FieldImage.Y_PADDING=1;Blockly.FieldImage.prototype.EDITABLE=!1;Blockly.FieldImage.prototype.isDirty_=!1; +Blockly.FieldImage.prototype.initView=function(){this.imageElement_=Blockly.utils.dom.createSvgElement("image",{height:this.imageHeight_+"px",width:this.size_.width+"px",alt:this.text_},this.fieldGroup_);this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",this.value_)};Blockly.FieldImage.prototype.doClassValidation_=function(a){return"string"!=typeof a?null:a}; Blockly.FieldImage.prototype.doValueUpdate_=function(a){this.value_=a;this.imageElement_&&this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",this.value_||"")};Blockly.FieldImage.prototype.getFlipRtl=function(){return this.flipRtl_};Blockly.FieldImage.prototype.setText=function(a){this.setAlt(a)};Blockly.FieldImage.prototype.setAlt=function(a){null!==a&&(this.text_=a,this.imageElement_&&this.imageElement_.setAttribute("alt",a||""))}; -Blockly.FieldImage.prototype.showEditor_=function(){this.clickHandler_&&this.clickHandler_(this)};Blockly.FieldImage.prototype.setOnClickHandler=function(a){this.clickHandler_=a};Blockly.FieldImage.prototype.getCorrectedSize=function(){this.getSize();return new Blockly.utils.Size(this.size_.width,this.height_+1)};Blockly.Field.register("field_image",Blockly.FieldImage);Blockly.FieldNumber=function(a,b,c,d,e){this.setConstraints(b,c,d);a=this.doClassValidation_(a);null===a&&(a=0);Blockly.FieldNumber.superClass_.constructor.call(this,a,e)};goog.inherits(Blockly.FieldNumber,Blockly.FieldTextInput);Blockly.FieldNumber.fromJson=function(a){return new Blockly.FieldNumber(a.value,a.min,a.max,a.precision)};Blockly.FieldNumber.prototype.SERIALIZABLE=!0; +Blockly.FieldImage.prototype.showEditor_=function(){this.clickHandler_&&this.clickHandler_(this)};Blockly.FieldImage.prototype.setOnClickHandler=function(a){this.clickHandler_=a};Blockly.Field.register("field_image",Blockly.FieldImage);Blockly.FieldNumber=function(a,b,c,d,e){this.setConstraints(b,c,d);a=this.doClassValidation_(a);null===a&&(a=0);Blockly.FieldNumber.superClass_.constructor.call(this,a,e)};goog.inherits(Blockly.FieldNumber,Blockly.FieldTextInput);Blockly.FieldNumber.fromJson=function(a){return new Blockly.FieldNumber(a.value,a.min,a.max,a.precision)};Blockly.FieldNumber.prototype.SERIALIZABLE=!0; Blockly.FieldNumber.prototype.setConstraints=function(a,b,c){c=Number(c);this.precision_=isNaN(c)?0:c;c=this.precision_.toString();var d=c.indexOf(".");this.fractionalDigits_=-1==d?-1:c.length-(d+1);a=Number(a);this.min_=isNaN(a)?-Infinity:a;b=Number(b);this.max_=isNaN(b)?Infinity:b;this.setValue(this.getValue())}; Blockly.FieldNumber.prototype.doClassValidation_=function(a){if(null===a||void 0===a)return null;a=String(a);a=a.replace(/O/ig,"0");a=a.replace(/,/g,"");a=Number(a||0);if(isNaN(a))return null;a=Math.min(Math.max(a,this.min_),this.max_);this.precision_&&isFinite(a)&&(a=Math.round(a/this.precision_)*this.precision_);return a=-1==this.fractionalDigits_?a:Number(a.toFixed(this.fractionalDigits_))};Blockly.Field.register("field_number",Blockly.FieldNumber);Blockly.FieldVariable=function(a,b,c,d){this.menuGenerator_=Blockly.FieldVariable.dropdownCreate;this.size_=new Blockly.utils.Size(0,Blockly.BlockSvg.MIN_BLOCK_Y);b&&this.setValidator(b);this.defaultVariableName=a||"";this.setTypes_(c,d);this.value_=null};goog.inherits(Blockly.FieldVariable,Blockly.FieldDropdown);Blockly.FieldVariable.fromJson=function(a){var b=Blockly.utils.replaceMessageReferences(a.variable);return new Blockly.FieldVariable(b,null,a.variableTypes,a.defaultType)}; Blockly.FieldVariable.prototype.workspace_=null;Blockly.FieldVariable.prototype.SERIALIZABLE=!0;Blockly.FieldVariable.prototype.initModel=function(){if(!this.variable_){var a=Blockly.Variables.getOrCreateVariablePackage(this.workspace_,null,this.defaultVariableName,this.defaultType_);Blockly.Events.disable();this.setValue(a.getId());Blockly.Events.enable()}}; @@ -1805,7 +1805,7 @@ c&&this.ORDER_OVERRIDES[e][1]==b){d=!1;break}d&&(a="("+a+")");return a};Blockly. Blockly.Generator.prototype.addLoopTrap=function(a,b){this.INFINITE_LOOP_TRAP&&(a=this.prefixLines(this.injectId(this.INFINITE_LOOP_TRAP,b),this.INDENT)+a);this.STATEMENT_SUFFIX&&!b.suppressPrefixSuffix&&(a=this.prefixLines(this.injectId(this.STATEMENT_SUFFIX,b),this.INDENT)+a);this.STATEMENT_PREFIX&&!b.suppressPrefixSuffix&&(a+=this.prefixLines(this.injectId(this.STATEMENT_PREFIX,b),this.INDENT));return a}; Blockly.Generator.prototype.injectId=function(a,b){var c=b.id.replace(/\$/g,"$$$$");return a.replace(/%1/g,"'"+c+"'")};Blockly.Generator.prototype.RESERVED_WORDS_="";Blockly.Generator.prototype.addReservedWords=function(a){this.RESERVED_WORDS_+=a+","};Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_="{leCUI8hutHZI4480Dc}"; Blockly.Generator.prototype.provideFunction_=function(a,b){if(!this.definitions_[a]){var c=this.variableDB_.getDistinctName(a,Blockly.Procedures.NAME_TYPE);this.functionNames_[a]=c;c=b.join("\n").replace(this.FUNCTION_NAME_PLACEHOLDER_REGEXP_,c);for(var d;d!=c;)d=c,c=c.replace(/^(( {2})*) {2}/gm,"$1\x00");c=c.replace(/\0/g,this.INDENT);this.definitions_[a]=c}return this.functionNames_[a]};Blockly.Generator.prototype.init=function(a){};Blockly.Generator.prototype.scrub_=function(a,b){return b}; -Blockly.Generator.prototype.finish=function(a){return a};Blockly.Generator.prototype.scrubNakedValue=function(a){return a};Blockly.ASTNode=function(a,b,c){if(!b)throw Error("Cannot create a node without a location.");this.type_=a;this.isConnection_=Blockly.ASTNode.isConnectionType(a);this.location_=b;this.processParams_(c||null)};Blockly.ASTNode.types={FIELD:"field",BLOCK:"block",INPUT:"input",OUTPUT:"output",NEXT:"next",PREVIOUS:"previous",STACK:"stack",WORKSPACE:"workspace"};Blockly.ASTNode.wsMove_=10;Blockly.ASTNode.DEFAULT_OFFSET_Y=-20;Blockly.ASTNode.isConnectionType=function(a){switch(a){case Blockly.ASTNode.types.PREVIOUS:case Blockly.ASTNode.types.NEXT:case Blockly.ASTNode.types.INPUT:case Blockly.ASTNode.types.OUTPUT:return!0}return!1}; +Blockly.Generator.prototype.finish=function(a){return a};Blockly.Generator.prototype.scrubNakedValue=function(a){return a};Blockly.Action=function(a,b,c){this.name=a;this.desc=b;this.func=c};Blockly.ASTNode=function(a,b,c){if(!b)throw Error("Cannot create a node without a location.");this.type_=a;this.isConnection_=Blockly.ASTNode.isConnectionType(a);this.location_=b;this.processParams_(c||null)};Blockly.ASTNode.types={FIELD:"field",BLOCK:"block",INPUT:"input",OUTPUT:"output",NEXT:"next",PREVIOUS:"previous",STACK:"stack",WORKSPACE:"workspace"};Blockly.ASTNode.wsMove_=10;Blockly.ASTNode.DEFAULT_OFFSET_Y=-20;Blockly.ASTNode.isConnectionType=function(a){switch(a){case Blockly.ASTNode.types.PREVIOUS:case Blockly.ASTNode.types.NEXT:case Blockly.ASTNode.types.INPUT:case Blockly.ASTNode.types.OUTPUT:return!0}return!1}; Blockly.ASTNode.createFieldNode=function(a){return new Blockly.ASTNode(Blockly.ASTNode.types.FIELD,a)}; Blockly.ASTNode.createConnectionNode=function(a){return a?a.type===Blockly.INPUT_VALUE||a.type===Blockly.NEXT_STATEMENT&&a.getParentInput()?Blockly.ASTNode.createInputNode(a.getParentInput()):a.type===Blockly.NEXT_STATEMENT?new Blockly.ASTNode(Blockly.ASTNode.types.NEXT,a):a.type===Blockly.OUTPUT_VALUE?new Blockly.ASTNode(Blockly.ASTNode.types.OUTPUT,a):a.type===Blockly.PREVIOUS_STATEMENT?new Blockly.ASTNode(Blockly.ASTNode.types.PREVIOUS,a):null:null}; Blockly.ASTNode.createInputNode=function(a){return a?new Blockly.ASTNode(Blockly.ASTNode.types.INPUT,a.connection,{input:a}):null};Blockly.ASTNode.createBlockNode=function(a){return new Blockly.ASTNode(Blockly.ASTNode.types.BLOCK,a)};Blockly.ASTNode.createStackNode=function(a){return new Blockly.ASTNode(Blockly.ASTNode.types.STACK,a)};Blockly.ASTNode.createWorkspaceNode=function(a,b){return new Blockly.ASTNode(Blockly.ASTNode.types.WORKSPACE,a,{wsCoordinate:b})}; @@ -1824,7 +1824,10 @@ Blockly.ASTNode.prototype["in"]=function(){switch(this.type_){case Blockly.ASTNo Blockly.ASTNode.prototype.prev=function(){switch(this.type_){case Blockly.ASTNode.types.WORKSPACE:var a=new Blockly.utils.Coordinate(this.wsCoordinate_.x-Blockly.ASTNode.wsMove_,this.wsCoordinate_.y);return Blockly.ASTNode.createWorkspaceNode(this.location_,a);case Blockly.ASTNode.types.STACK:return this.navigateBetweenStacks_(!1);case Blockly.ASTNode.types.FIELD:return this.findPrevForField_();case Blockly.ASTNode.types.INPUT:return this.findPrevForInput_();case Blockly.ASTNode.types.BLOCK:a=this.location_.outputConnection; if(a=this.location_.previousConnection||a)return Blockly.ASTNode.createConnectionNode(a);break;case Blockly.ASTNode.types.PREVIOUS:if(a=this.location_.targetConnection)return Blockly.ASTNode.createConnectionNode(a);break;case Blockly.ASTNode.types.NEXT:return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock())}return null}; Blockly.ASTNode.prototype.out=function(){switch(this.type_){case Blockly.ASTNode.types.STACK:var a=this.location_.getRelativeToSurfaceXY();a=new Blockly.utils.Coordinate(a.x,a.y+Blockly.ASTNode.DEFAULT_OFFSET_Y);return Blockly.ASTNode.createWorkspaceNode(this.location_.workspace,a);case Blockly.ASTNode.types.OUTPUT:return(a=this.location_.targetConnection)?Blockly.ASTNode.createConnectionNode(a):Blockly.ASTNode.createStackNode(this.location_.getSourceBlock());case Blockly.ASTNode.types.FIELD:return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock()); -case Blockly.ASTNode.types.INPUT:return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock());case Blockly.ASTNode.types.BLOCK:return this.getOutAstNodeForBlock_(this.location_);case Blockly.ASTNode.types.PREVIOUS:return this.getOutAstNodeForBlock_(this.location_.getSourceBlock());case Blockly.ASTNode.types.NEXT:return this.getOutAstNodeForBlock_(this.location_.getSourceBlock())}return null};Blockly.Navigation={};Blockly.Navigation.cursor_=null;Blockly.Navigation.marker_=null;Blockly.Navigation.currentCategory_=null;Blockly.Navigation.flyoutBlock_=null;Blockly.Navigation.loggingCallback=null;Blockly.Navigation.STATE_FLYOUT=1;Blockly.Navigation.STATE_WS=2;Blockly.Navigation.STATE_TOOLBOX=3;Blockly.Navigation.currentState_=Blockly.Navigation.STATE_WS;Blockly.Navigation.setCursor=function(a){Blockly.Navigation.cursor_=a}; +case Blockly.ASTNode.types.INPUT:return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock());case Blockly.ASTNode.types.BLOCK:return this.getOutAstNodeForBlock_(this.location_);case Blockly.ASTNode.types.PREVIOUS:return this.getOutAstNodeForBlock_(this.location_.getSourceBlock());case Blockly.ASTNode.types.NEXT:return this.getOutAstNodeForBlock_(this.location_.getSourceBlock())}return null};Blockly.user={};Blockly.user.keyMap={};Blockly.user.keyMap.map_={};Blockly.user.keyMap.setActionForKey=function(a,b){var c=Blockly.user.keyMap.getKeyByActionName(b.name);c&&delete Blockly.user.keyMap.map_[c];Blockly.user.keyMap.map_[a]=b};Blockly.user.keyMap.setKeyMap=function(a){Blockly.user.keyMap.map_=a};Blockly.user.keyMap.getKeyMap=function(){return Blockly.user.keyMap.map_};Blockly.user.keyMap.getActionByKeyCode=function(a){return Blockly.user.keyMap.map_[a]}; +Blockly.user.keyMap.getKeyByActionName=function(a){for(var b=Object.keys(Blockly.user.keyMap.map_),c=0;ce?Blockly.WidgetDiv.positionInternal_(a,0,c.height+e):Blockly.WidgetDiv.positionInternal_(a,e,c.height)}; Blockly.WidgetDiv.calculateX_=function(a,b,c,d){if(d)return b=Math.max(b.right-c.width,a.left),Math.min(b,a.right-c.width);b=Math.min(b.left,a.right-c.width);return Math.max(b,a.left)};Blockly.WidgetDiv.calculateY_=function(a,b,c){return b.bottom+c.height>=a.bottom?b.top-c.height:b.bottom};Blockly.inject=function(a,b){Blockly.checkBlockColourConstants();"string"==typeof a&&(a=document.getElementById(a)||document.querySelector(a));if(!Blockly.utils.dom.containsNode(document,a))throw Error("Error: container is not in current document.");var c=new Blockly.Options(b||{}),d=document.createElement("div");d.className="injectionDiv";a.appendChild(d);var e=Blockly.createDom_(d,c),f=new Blockly.BlockDragSurfaceSvg(d);d=new Blockly.WorkspaceDragSurfaceSvg(d);e=Blockly.createMainWorkspace_(e,c, -f,d);Blockly.setTheme(c.theme);Blockly.init_(e);Blockly.mainWorkspace=e;Blockly.svgResize(e);return e}; +f,d);Blockly.setTheme(c.theme);Blockly.user.keyMap.setKeyMap(c.keyMap);Blockly.init_(e);Blockly.mainWorkspace=e;Blockly.svgResize(e);return e}; Blockly.createDom_=function(a,b){a.setAttribute("dir","LTR");goog.ui.Component.setDefaultRightToLeft(b.RTL);Blockly.Css.inject(b.hasCss,b.pathToMedia);var c=Blockly.utils.dom.createSvgElement("svg",{xmlns:Blockly.utils.dom.SVG_NS,"xmlns:html":Blockly.utils.dom.HTML_NS,"xmlns:xlink":Blockly.utils.dom.XLINK_NS,version:"1.1","class":"blocklySvg"},a),d=Blockly.utils.dom.createSvgElement("defs",{},c),e=String(Math.random()).substring(2),f=Blockly.utils.dom.createSvgElement("filter",{id:"blocklyEmbossFilter"+ e},d);Blockly.utils.dom.createSvgElement("feGaussianBlur",{"in":"SourceAlpha",stdDeviation:1,result:"blur"},f);var g=Blockly.utils.dom.createSvgElement("feSpecularLighting",{"in":"blur",surfaceScale:1,specularConstant:.5,specularExponent:10,"lighting-color":"white",result:"specOut"},f);Blockly.utils.dom.createSvgElement("fePointLight",{x:-5E3,y:-1E4,z:2E4},g);Blockly.utils.dom.createSvgElement("feComposite",{"in":"specOut",in2:"SourceAlpha",operator:"in",result:"specOut"},f);Blockly.utils.dom.createSvgElement("feComposite", {"in":"SourceGraphic",in2:"specOut",operator:"arithmetic",k1:0,k2:1,k3:1,k4:0},f);b.embossFilterId=f.id;f=Blockly.utils.dom.createSvgElement("pattern",{id:"blocklyDisabledPattern"+e,patternUnits:"userSpaceOnUse",width:10,height:10},d);Blockly.utils.dom.createSvgElement("rect",{width:10,height:10,fill:"#aaa"},f);Blockly.utils.dom.createSvgElement("path",{d:"M 0 0 L 10 10 M 10 0 L 0 10",stroke:"#cc0"},f);b.disabledPatternId=f.id;b.gridPattern=Blockly.Grid.createDom(e,b.gridOptions,d);return c}; @@ -1989,7 +1992,7 @@ c=Blockly.Scrollbar.scrollbarThickness;b.hasTrashcan&&(c=a.trashcan.init(c));b.z Blockly.inject.bindDocumentEvents_=function(){Blockly.documentEventsBound_||(Blockly.bindEventWithChecks_(document,"scroll",null,function(){for(var a=Blockly.Workspace.getAll(),b=0,c;c=a[b];b++)c.updateInverseScreenCTM&&c.updateInverseScreenCTM()}),Blockly.bindEventWithChecks_(document,"keydown",null,Blockly.onKeyDown_),Blockly.bindEvent_(document,"touchend",null,Blockly.longStop_),Blockly.bindEvent_(document,"touchcancel",null,Blockly.longStop_),Blockly.utils.userAgent.IPAD&&Blockly.bindEventWithChecks_(window, "orientationchange",document,function(){Blockly.svgResize(Blockly.getMainWorkspace())}));Blockly.documentEventsBound_=!0}; Blockly.inject.loadSounds_=function(a,b){var c=b.getAudioManager();c.load([a+"click.mp3",a+"click.wav",a+"click.ogg"],"click");c.load([a+"disconnect.wav",a+"disconnect.mp3",a+"disconnect.ogg"],"disconnect");c.load([a+"delete.mp3",a+"delete.ogg",a+"delete.wav"],"delete");var d=[],e=function(){for(;d.length;)Blockly.unbindEvent_(d.pop());c.preload()};d.push(Blockly.bindEventWithChecks_(document,"mousemove",null,e,!0));d.push(Blockly.bindEventWithChecks_(document,"touchstart",null,e,!0))}; -Blockly.updateToolbox=function(a){console.warn("Deprecated call to Blockly.updateToolbox, use workspace.updateToolbox instead.");Blockly.getMainWorkspace().updateToolbox(a)};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.cursor=null;Blockly.keyboardAccessibilityMode=!1;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.clipboardTypeCounts_=null;Blockly.cache3dSupported_=null;Blockly.theme_=null;Blockly.svgSize=function(a){return{width:a.cachedWidth_,height:a.cachedHeight_}};Blockly.resizeSvgContents=function(a){a.resizeContents()}; +Blockly.updateToolbox=function(a){console.warn("Deprecated call to Blockly.updateToolbox, use workspace.updateToolbox instead.");Blockly.getMainWorkspace().updateToolbox(a)};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.VERSION="2.20190722.0-develop";Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.cursor=null;Blockly.keyboardAccessibilityMode=!1;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.clipboardTypeCounts_=null;Blockly.cache3dSupported_=null;Blockly.theme_=null;Blockly.svgSize=function(a){return{width:a.cachedWidth_,height:a.cachedHeight_}};Blockly.resizeSvgContents=function(a){a.resizeContents()}; Blockly.svgResize=function(a){for(;a.options.parentWorkspace;)a=a.options.parentWorkspace;var b=a.getParentSvg(),c=b.parentNode;if(c){var d=c.offsetWidth;c=c.offsetHeight;b.cachedWidth_!=d&&(b.setAttribute("width",d+"px"),b.cachedWidth_=d);b.cachedHeight_!=c&&(b.setAttribute("height",c+"px"),b.cachedHeight_=c);a.resize()}}; Blockly.onKeyDown_=function(a){var b=Blockly.mainWorkspace;if(!(b.options.readOnly||Blockly.utils.isTargetInput(a)||b.rendered&&!b.isVisible())){var c=!1;if(27==a.keyCode)Blockly.hideChaff(),Blockly.keyboardAccessibilityMode&&Blockly.Navigation.navigate(a);else if(8==a.keyCode||46==a.keyCode){a.preventDefault();if(Blockly.Gesture.inProgress())return;Blockly.selected&&Blockly.selected.isDeletable()&&(c=!0)}else if(a.altKey||a.ctrlKey||a.metaKey){if(Blockly.Gesture.inProgress())return;Blockly.selected&& Blockly.selected.isDeletable()&&Blockly.selected.isMovable()&&(67==a.keyCode?(Blockly.hideChaff(),Blockly.copy_(Blockly.selected)):88!=a.keyCode||Blockly.selected.workspace.isFlyout||(Blockly.copy_(Blockly.selected),c=!0));86==a.keyCode?Blockly.clipboardXml_&&(a=Blockly.clipboardSource_,a.isFlyout&&(a=a.targetWorkspace),Blockly.clipboardTypeCounts_&&a.isCapacityAvailable(Blockly.clipboardTypeCounts_)&&(Blockly.Events.setGroup(!0),a.paste(Blockly.clipboardXml_),Blockly.Events.setGroup(!1))):90==a.keyCode&& diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index c7ffe17d3..cc1a34fa2 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -83,7 +83,9 @@ goog.addDependency("../../../" + dir + "/core/insertion_marker_manager.js", ['Bl goog.addDependency("../../../" + dir + "/core/keyboard_nav/ast_node.js", ['Blockly.ASTNode'], []); goog.addDependency("../../../" + dir + "/core/keyboard_nav/cursor.js", ['Blockly.Cursor'], []); goog.addDependency("../../../" + dir + "/core/keyboard_nav/cursor_svg.js", ['Blockly.CursorSvg'], ['Blockly.Cursor']); -goog.addDependency("../../../" + dir + "/core/keyboard_nav/navigation.js", ['Blockly.Navigation'], ['Blockly.ASTNode']); +goog.addDependency("../../../" + dir + "/core/keyboard_nav/key_action.js", ['Blockly.Action'], []); +goog.addDependency("../../../" + dir + "/core/keyboard_nav/key_map.js", ['Blockly.user.keyMap'], []); +goog.addDependency("../../../" + dir + "/core/keyboard_nav/navigation.js", ['Blockly.Navigation'], ['Blockly.Action', 'Blockly.ASTNode', 'Blockly.user.keyMap']); goog.addDependency("../../../" + dir + "/core/msg.js", ['Blockly.Msg'], []); goog.addDependency("../../../" + dir + "/core/mutator.js", ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.xml', 'Blockly.WorkspaceSvg', 'Blockly.Xml']); goog.addDependency("../../../" + dir + "/core/names.js", ['Blockly.Names'], []); @@ -118,6 +120,7 @@ goog.addDependency("../../../" + dir + "/core/utils/dom.js", ['Blockly.utils.dom goog.addDependency("../../../" + dir + "/core/utils/math.js", ['Blockly.utils.math'], []); goog.addDependency("../../../" + dir + "/core/utils/rect.js", ['Blockly.utils.Rect'], []); goog.addDependency("../../../" + dir + "/core/utils/size.js", ['Blockly.utils.Size'], []); +goog.addDependency("../../../" + dir + "/core/utils/something/something.js", [], []); goog.addDependency("../../../" + dir + "/core/utils/string.js", ['Blockly.utils.string'], []); goog.addDependency("../../../" + dir + "/core/utils/style.js", ['Blockly.utils.style'], ['Blockly.utils.Coordinate', 'Blockly.utils.Size']); goog.addDependency("../../../" + dir + "/core/utils/svg_paths.js", ['Blockly.utils.svgPaths'], []); @@ -705,8 +708,10 @@ goog.addDependency("html/sanitizer/csssanitizer.js", ['goog.html.sanitizer.CssSa goog.addDependency("html/sanitizer/csssanitizer_test.js", [], []); goog.addDependency("html/sanitizer/elementweakmap.js", [], []); goog.addDependency("html/sanitizer/elementweakmap_test.js", [], []); +goog.addDependency("html/sanitizer/html_test_vectors.js", ['goog.html.htmlTestVectors'], []); goog.addDependency("html/sanitizer/htmlsanitizer.js", ['goog.html.sanitizer.HtmlSanitizer', 'goog.html.sanitizer.HtmlSanitizer.Builder', 'goog.html.sanitizer.HtmlSanitizerAttributePolicy', 'goog.html.sanitizer.HtmlSanitizerPolicy', 'goog.html.sanitizer.HtmlSanitizerPolicyContext', 'goog.html.sanitizer.HtmlSanitizerPolicyHints', 'goog.html.sanitizer.HtmlSanitizerUrlPolicy'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.functions', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.sanitizer.AttributeSanitizedWhitelist', 'goog.html.sanitizer.AttributeWhitelist', 'goog.html.sanitizer.CssSanitizer', 'goog.html.sanitizer.SafeDomTreeProcessor', 'goog.html.sanitizer.TagBlacklist', 'goog.html.sanitizer.TagWhitelist', 'goog.html.sanitizer.noclobber', 'goog.html.uncheckedconversions', 'goog.object', 'goog.string', 'goog.string.Const']); goog.addDependency("html/sanitizer/htmlsanitizer_test.js", [], []); +goog.addDependency("html/sanitizer/htmlsanitizer_unified_test.js", [], []); goog.addDependency("html/sanitizer/noclobber.js", [], []); goog.addDependency("html/sanitizer/noclobber_test.js", [], []); goog.addDependency("html/sanitizer/safedomtreeprocessor.js", [], []); @@ -757,6 +762,8 @@ goog.addDependency("i18n/datetimesymbols.js", ['goog.i18n.DateTimeSymbols', 'goo goog.addDependency("i18n/datetimesymbolsext.js", ['goog.i18n.DateTimeSymbolsExt', 'goog.i18n.DateTimeSymbols_af_NA', 'goog.i18n.DateTimeSymbols_af_ZA', 'goog.i18n.DateTimeSymbols_agq', 'goog.i18n.DateTimeSymbols_agq_CM', 'goog.i18n.DateTimeSymbols_ak', 'goog.i18n.DateTimeSymbols_ak_GH', 'goog.i18n.DateTimeSymbols_am_ET', 'goog.i18n.DateTimeSymbols_ar_001', 'goog.i18n.DateTimeSymbols_ar_AE', 'goog.i18n.DateTimeSymbols_ar_BH', 'goog.i18n.DateTimeSymbols_ar_DJ', 'goog.i18n.DateTimeSymbols_ar_EH', 'goog.i18n.DateTimeSymbols_ar_ER', 'goog.i18n.DateTimeSymbols_ar_IL', 'goog.i18n.DateTimeSymbols_ar_IQ', 'goog.i18n.DateTimeSymbols_ar_JO', 'goog.i18n.DateTimeSymbols_ar_KM', 'goog.i18n.DateTimeSymbols_ar_KW', 'goog.i18n.DateTimeSymbols_ar_LB', 'goog.i18n.DateTimeSymbols_ar_LY', 'goog.i18n.DateTimeSymbols_ar_MA', 'goog.i18n.DateTimeSymbols_ar_MR', 'goog.i18n.DateTimeSymbols_ar_OM', 'goog.i18n.DateTimeSymbols_ar_PS', 'goog.i18n.DateTimeSymbols_ar_QA', 'goog.i18n.DateTimeSymbols_ar_SA', 'goog.i18n.DateTimeSymbols_ar_SD', 'goog.i18n.DateTimeSymbols_ar_SO', 'goog.i18n.DateTimeSymbols_ar_SS', 'goog.i18n.DateTimeSymbols_ar_SY', 'goog.i18n.DateTimeSymbols_ar_TD', 'goog.i18n.DateTimeSymbols_ar_TN', 'goog.i18n.DateTimeSymbols_ar_XB', 'goog.i18n.DateTimeSymbols_ar_YE', 'goog.i18n.DateTimeSymbols_as', 'goog.i18n.DateTimeSymbols_as_IN', 'goog.i18n.DateTimeSymbols_asa', 'goog.i18n.DateTimeSymbols_asa_TZ', 'goog.i18n.DateTimeSymbols_ast', 'goog.i18n.DateTimeSymbols_ast_ES', 'goog.i18n.DateTimeSymbols_az_Cyrl', 'goog.i18n.DateTimeSymbols_az_Cyrl_AZ', 'goog.i18n.DateTimeSymbols_az_Latn', 'goog.i18n.DateTimeSymbols_az_Latn_AZ', 'goog.i18n.DateTimeSymbols_bas', 'goog.i18n.DateTimeSymbols_bas_CM', 'goog.i18n.DateTimeSymbols_be_BY', 'goog.i18n.DateTimeSymbols_bem', 'goog.i18n.DateTimeSymbols_bem_ZM', 'goog.i18n.DateTimeSymbols_bez', 'goog.i18n.DateTimeSymbols_bez_TZ', 'goog.i18n.DateTimeSymbols_bg_BG', 'goog.i18n.DateTimeSymbols_bm', 'goog.i18n.DateTimeSymbols_bm_ML', 'goog.i18n.DateTimeSymbols_bn_BD', 'goog.i18n.DateTimeSymbols_bn_IN', 'goog.i18n.DateTimeSymbols_bo', 'goog.i18n.DateTimeSymbols_bo_CN', 'goog.i18n.DateTimeSymbols_bo_IN', 'goog.i18n.DateTimeSymbols_br_FR', 'goog.i18n.DateTimeSymbols_brx', 'goog.i18n.DateTimeSymbols_brx_IN', 'goog.i18n.DateTimeSymbols_bs_Cyrl', 'goog.i18n.DateTimeSymbols_bs_Cyrl_BA', 'goog.i18n.DateTimeSymbols_bs_Latn', 'goog.i18n.DateTimeSymbols_bs_Latn_BA', 'goog.i18n.DateTimeSymbols_ca_AD', 'goog.i18n.DateTimeSymbols_ca_ES', 'goog.i18n.DateTimeSymbols_ca_FR', 'goog.i18n.DateTimeSymbols_ca_IT', 'goog.i18n.DateTimeSymbols_ccp', 'goog.i18n.DateTimeSymbols_ccp_BD', 'goog.i18n.DateTimeSymbols_ccp_IN', 'goog.i18n.DateTimeSymbols_ce', 'goog.i18n.DateTimeSymbols_ce_RU', 'goog.i18n.DateTimeSymbols_ceb', 'goog.i18n.DateTimeSymbols_ceb_PH', 'goog.i18n.DateTimeSymbols_cgg', 'goog.i18n.DateTimeSymbols_cgg_UG', 'goog.i18n.DateTimeSymbols_chr_US', 'goog.i18n.DateTimeSymbols_ckb', 'goog.i18n.DateTimeSymbols_ckb_IQ', 'goog.i18n.DateTimeSymbols_ckb_IR', 'goog.i18n.DateTimeSymbols_cs_CZ', 'goog.i18n.DateTimeSymbols_cy_GB', 'goog.i18n.DateTimeSymbols_da_DK', 'goog.i18n.DateTimeSymbols_da_GL', 'goog.i18n.DateTimeSymbols_dav', 'goog.i18n.DateTimeSymbols_dav_KE', 'goog.i18n.DateTimeSymbols_de_BE', 'goog.i18n.DateTimeSymbols_de_DE', 'goog.i18n.DateTimeSymbols_de_IT', 'goog.i18n.DateTimeSymbols_de_LI', 'goog.i18n.DateTimeSymbols_de_LU', 'goog.i18n.DateTimeSymbols_dje', 'goog.i18n.DateTimeSymbols_dje_NE', 'goog.i18n.DateTimeSymbols_dsb', 'goog.i18n.DateTimeSymbols_dsb_DE', 'goog.i18n.DateTimeSymbols_dua', 'goog.i18n.DateTimeSymbols_dua_CM', 'goog.i18n.DateTimeSymbols_dyo', 'goog.i18n.DateTimeSymbols_dyo_SN', 'goog.i18n.DateTimeSymbols_dz', 'goog.i18n.DateTimeSymbols_dz_BT', 'goog.i18n.DateTimeSymbols_ebu', 'goog.i18n.DateTimeSymbols_ebu_KE', 'goog.i18n.DateTimeSymbols_ee', 'goog.i18n.DateTimeSymbols_ee_GH', 'goog.i18n.DateTimeSymbols_ee_TG', 'goog.i18n.DateTimeSymbols_el_CY', 'goog.i18n.DateTimeSymbols_el_GR', 'goog.i18n.DateTimeSymbols_en_001', 'goog.i18n.DateTimeSymbols_en_150', 'goog.i18n.DateTimeSymbols_en_AE', 'goog.i18n.DateTimeSymbols_en_AG', 'goog.i18n.DateTimeSymbols_en_AI', 'goog.i18n.DateTimeSymbols_en_AS', 'goog.i18n.DateTimeSymbols_en_AT', 'goog.i18n.DateTimeSymbols_en_BB', 'goog.i18n.DateTimeSymbols_en_BE', 'goog.i18n.DateTimeSymbols_en_BI', 'goog.i18n.DateTimeSymbols_en_BM', 'goog.i18n.DateTimeSymbols_en_BS', 'goog.i18n.DateTimeSymbols_en_BW', 'goog.i18n.DateTimeSymbols_en_BZ', 'goog.i18n.DateTimeSymbols_en_CC', 'goog.i18n.DateTimeSymbols_en_CH', 'goog.i18n.DateTimeSymbols_en_CK', 'goog.i18n.DateTimeSymbols_en_CM', 'goog.i18n.DateTimeSymbols_en_CX', 'goog.i18n.DateTimeSymbols_en_CY', 'goog.i18n.DateTimeSymbols_en_DE', 'goog.i18n.DateTimeSymbols_en_DG', 'goog.i18n.DateTimeSymbols_en_DK', 'goog.i18n.DateTimeSymbols_en_DM', 'goog.i18n.DateTimeSymbols_en_ER', 'goog.i18n.DateTimeSymbols_en_FI', 'goog.i18n.DateTimeSymbols_en_FJ', 'goog.i18n.DateTimeSymbols_en_FK', 'goog.i18n.DateTimeSymbols_en_FM', 'goog.i18n.DateTimeSymbols_en_GD', 'goog.i18n.DateTimeSymbols_en_GG', 'goog.i18n.DateTimeSymbols_en_GH', 'goog.i18n.DateTimeSymbols_en_GI', 'goog.i18n.DateTimeSymbols_en_GM', 'goog.i18n.DateTimeSymbols_en_GU', 'goog.i18n.DateTimeSymbols_en_GY', 'goog.i18n.DateTimeSymbols_en_HK', 'goog.i18n.DateTimeSymbols_en_IL', 'goog.i18n.DateTimeSymbols_en_IM', 'goog.i18n.DateTimeSymbols_en_IO', 'goog.i18n.DateTimeSymbols_en_JE', 'goog.i18n.DateTimeSymbols_en_JM', 'goog.i18n.DateTimeSymbols_en_KE', 'goog.i18n.DateTimeSymbols_en_KI', 'goog.i18n.DateTimeSymbols_en_KN', 'goog.i18n.DateTimeSymbols_en_KY', 'goog.i18n.DateTimeSymbols_en_LC', 'goog.i18n.DateTimeSymbols_en_LR', 'goog.i18n.DateTimeSymbols_en_LS', 'goog.i18n.DateTimeSymbols_en_MG', 'goog.i18n.DateTimeSymbols_en_MH', 'goog.i18n.DateTimeSymbols_en_MO', 'goog.i18n.DateTimeSymbols_en_MP', 'goog.i18n.DateTimeSymbols_en_MS', 'goog.i18n.DateTimeSymbols_en_MT', 'goog.i18n.DateTimeSymbols_en_MU', 'goog.i18n.DateTimeSymbols_en_MW', 'goog.i18n.DateTimeSymbols_en_MY', 'goog.i18n.DateTimeSymbols_en_NA', 'goog.i18n.DateTimeSymbols_en_NF', 'goog.i18n.DateTimeSymbols_en_NG', 'goog.i18n.DateTimeSymbols_en_NL', 'goog.i18n.DateTimeSymbols_en_NR', 'goog.i18n.DateTimeSymbols_en_NU', 'goog.i18n.DateTimeSymbols_en_NZ', 'goog.i18n.DateTimeSymbols_en_PG', 'goog.i18n.DateTimeSymbols_en_PH', 'goog.i18n.DateTimeSymbols_en_PK', 'goog.i18n.DateTimeSymbols_en_PN', 'goog.i18n.DateTimeSymbols_en_PR', 'goog.i18n.DateTimeSymbols_en_PW', 'goog.i18n.DateTimeSymbols_en_RW', 'goog.i18n.DateTimeSymbols_en_SB', 'goog.i18n.DateTimeSymbols_en_SC', 'goog.i18n.DateTimeSymbols_en_SD', 'goog.i18n.DateTimeSymbols_en_SE', 'goog.i18n.DateTimeSymbols_en_SH', 'goog.i18n.DateTimeSymbols_en_SI', 'goog.i18n.DateTimeSymbols_en_SL', 'goog.i18n.DateTimeSymbols_en_SS', 'goog.i18n.DateTimeSymbols_en_SX', 'goog.i18n.DateTimeSymbols_en_SZ', 'goog.i18n.DateTimeSymbols_en_TC', 'goog.i18n.DateTimeSymbols_en_TK', 'goog.i18n.DateTimeSymbols_en_TO', 'goog.i18n.DateTimeSymbols_en_TT', 'goog.i18n.DateTimeSymbols_en_TV', 'goog.i18n.DateTimeSymbols_en_TZ', 'goog.i18n.DateTimeSymbols_en_UG', 'goog.i18n.DateTimeSymbols_en_UM', 'goog.i18n.DateTimeSymbols_en_US_POSIX', 'goog.i18n.DateTimeSymbols_en_VC', 'goog.i18n.DateTimeSymbols_en_VG', 'goog.i18n.DateTimeSymbols_en_VI', 'goog.i18n.DateTimeSymbols_en_VU', 'goog.i18n.DateTimeSymbols_en_WS', 'goog.i18n.DateTimeSymbols_en_XA', 'goog.i18n.DateTimeSymbols_en_ZM', 'goog.i18n.DateTimeSymbols_en_ZW', 'goog.i18n.DateTimeSymbols_eo', 'goog.i18n.DateTimeSymbols_eo_001', 'goog.i18n.DateTimeSymbols_es_AR', 'goog.i18n.DateTimeSymbols_es_BO', 'goog.i18n.DateTimeSymbols_es_BR', 'goog.i18n.DateTimeSymbols_es_BZ', 'goog.i18n.DateTimeSymbols_es_CL', 'goog.i18n.DateTimeSymbols_es_CO', 'goog.i18n.DateTimeSymbols_es_CR', 'goog.i18n.DateTimeSymbols_es_CU', 'goog.i18n.DateTimeSymbols_es_DO', 'goog.i18n.DateTimeSymbols_es_EA', 'goog.i18n.DateTimeSymbols_es_EC', 'goog.i18n.DateTimeSymbols_es_GQ', 'goog.i18n.DateTimeSymbols_es_GT', 'goog.i18n.DateTimeSymbols_es_HN', 'goog.i18n.DateTimeSymbols_es_IC', 'goog.i18n.DateTimeSymbols_es_NI', 'goog.i18n.DateTimeSymbols_es_PA', 'goog.i18n.DateTimeSymbols_es_PE', 'goog.i18n.DateTimeSymbols_es_PH', 'goog.i18n.DateTimeSymbols_es_PR', 'goog.i18n.DateTimeSymbols_es_PY', 'goog.i18n.DateTimeSymbols_es_SV', 'goog.i18n.DateTimeSymbols_es_UY', 'goog.i18n.DateTimeSymbols_es_VE', 'goog.i18n.DateTimeSymbols_et_EE', 'goog.i18n.DateTimeSymbols_eu_ES', 'goog.i18n.DateTimeSymbols_ewo', 'goog.i18n.DateTimeSymbols_ewo_CM', 'goog.i18n.DateTimeSymbols_fa_AF', 'goog.i18n.DateTimeSymbols_fa_IR', 'goog.i18n.DateTimeSymbols_ff', 'goog.i18n.DateTimeSymbols_ff_Latn', 'goog.i18n.DateTimeSymbols_ff_Latn_BF', 'goog.i18n.DateTimeSymbols_ff_Latn_CM', 'goog.i18n.DateTimeSymbols_ff_Latn_GH', 'goog.i18n.DateTimeSymbols_ff_Latn_GM', 'goog.i18n.DateTimeSymbols_ff_Latn_GN', 'goog.i18n.DateTimeSymbols_ff_Latn_GW', 'goog.i18n.DateTimeSymbols_ff_Latn_LR', 'goog.i18n.DateTimeSymbols_ff_Latn_MR', 'goog.i18n.DateTimeSymbols_ff_Latn_NE', 'goog.i18n.DateTimeSymbols_ff_Latn_NG', 'goog.i18n.DateTimeSymbols_ff_Latn_SL', 'goog.i18n.DateTimeSymbols_ff_Latn_SN', 'goog.i18n.DateTimeSymbols_fi_FI', 'goog.i18n.DateTimeSymbols_fil_PH', 'goog.i18n.DateTimeSymbols_fo', 'goog.i18n.DateTimeSymbols_fo_DK', 'goog.i18n.DateTimeSymbols_fo_FO', 'goog.i18n.DateTimeSymbols_fr_BE', 'goog.i18n.DateTimeSymbols_fr_BF', 'goog.i18n.DateTimeSymbols_fr_BI', 'goog.i18n.DateTimeSymbols_fr_BJ', 'goog.i18n.DateTimeSymbols_fr_BL', 'goog.i18n.DateTimeSymbols_fr_CD', 'goog.i18n.DateTimeSymbols_fr_CF', 'goog.i18n.DateTimeSymbols_fr_CG', 'goog.i18n.DateTimeSymbols_fr_CH', 'goog.i18n.DateTimeSymbols_fr_CI', 'goog.i18n.DateTimeSymbols_fr_CM', 'goog.i18n.DateTimeSymbols_fr_DJ', 'goog.i18n.DateTimeSymbols_fr_DZ', 'goog.i18n.DateTimeSymbols_fr_FR', 'goog.i18n.DateTimeSymbols_fr_GA', 'goog.i18n.DateTimeSymbols_fr_GF', 'goog.i18n.DateTimeSymbols_fr_GN', 'goog.i18n.DateTimeSymbols_fr_GP', 'goog.i18n.DateTimeSymbols_fr_GQ', 'goog.i18n.DateTimeSymbols_fr_HT', 'goog.i18n.DateTimeSymbols_fr_KM', 'goog.i18n.DateTimeSymbols_fr_LU', 'goog.i18n.DateTimeSymbols_fr_MA', 'goog.i18n.DateTimeSymbols_fr_MC', 'goog.i18n.DateTimeSymbols_fr_MF', 'goog.i18n.DateTimeSymbols_fr_MG', 'goog.i18n.DateTimeSymbols_fr_ML', 'goog.i18n.DateTimeSymbols_fr_MQ', 'goog.i18n.DateTimeSymbols_fr_MR', 'goog.i18n.DateTimeSymbols_fr_MU', 'goog.i18n.DateTimeSymbols_fr_NC', 'goog.i18n.DateTimeSymbols_fr_NE', 'goog.i18n.DateTimeSymbols_fr_PF', 'goog.i18n.DateTimeSymbols_fr_PM', 'goog.i18n.DateTimeSymbols_fr_RE', 'goog.i18n.DateTimeSymbols_fr_RW', 'goog.i18n.DateTimeSymbols_fr_SC', 'goog.i18n.DateTimeSymbols_fr_SN', 'goog.i18n.DateTimeSymbols_fr_SY', 'goog.i18n.DateTimeSymbols_fr_TD', 'goog.i18n.DateTimeSymbols_fr_TG', 'goog.i18n.DateTimeSymbols_fr_TN', 'goog.i18n.DateTimeSymbols_fr_VU', 'goog.i18n.DateTimeSymbols_fr_WF', 'goog.i18n.DateTimeSymbols_fr_YT', 'goog.i18n.DateTimeSymbols_fur', 'goog.i18n.DateTimeSymbols_fur_IT', 'goog.i18n.DateTimeSymbols_fy', 'goog.i18n.DateTimeSymbols_fy_NL', 'goog.i18n.DateTimeSymbols_ga_IE', 'goog.i18n.DateTimeSymbols_gd', 'goog.i18n.DateTimeSymbols_gd_GB', 'goog.i18n.DateTimeSymbols_gl_ES', 'goog.i18n.DateTimeSymbols_gsw_CH', 'goog.i18n.DateTimeSymbols_gsw_FR', 'goog.i18n.DateTimeSymbols_gsw_LI', 'goog.i18n.DateTimeSymbols_gu_IN', 'goog.i18n.DateTimeSymbols_guz', 'goog.i18n.DateTimeSymbols_guz_KE', 'goog.i18n.DateTimeSymbols_gv', 'goog.i18n.DateTimeSymbols_gv_IM', 'goog.i18n.DateTimeSymbols_ha', 'goog.i18n.DateTimeSymbols_ha_GH', 'goog.i18n.DateTimeSymbols_ha_NE', 'goog.i18n.DateTimeSymbols_ha_NG', 'goog.i18n.DateTimeSymbols_haw_US', 'goog.i18n.DateTimeSymbols_he_IL', 'goog.i18n.DateTimeSymbols_hi_IN', 'goog.i18n.DateTimeSymbols_hr_BA', 'goog.i18n.DateTimeSymbols_hr_HR', 'goog.i18n.DateTimeSymbols_hsb', 'goog.i18n.DateTimeSymbols_hsb_DE', 'goog.i18n.DateTimeSymbols_hu_HU', 'goog.i18n.DateTimeSymbols_hy_AM', 'goog.i18n.DateTimeSymbols_ia', 'goog.i18n.DateTimeSymbols_ia_001', 'goog.i18n.DateTimeSymbols_id_ID', 'goog.i18n.DateTimeSymbols_ig', 'goog.i18n.DateTimeSymbols_ig_NG', 'goog.i18n.DateTimeSymbols_ii', 'goog.i18n.DateTimeSymbols_ii_CN', 'goog.i18n.DateTimeSymbols_is_IS', 'goog.i18n.DateTimeSymbols_it_CH', 'goog.i18n.DateTimeSymbols_it_IT', 'goog.i18n.DateTimeSymbols_it_SM', 'goog.i18n.DateTimeSymbols_it_VA', 'goog.i18n.DateTimeSymbols_ja_JP', 'goog.i18n.DateTimeSymbols_jgo', 'goog.i18n.DateTimeSymbols_jgo_CM', 'goog.i18n.DateTimeSymbols_jmc', 'goog.i18n.DateTimeSymbols_jmc_TZ', 'goog.i18n.DateTimeSymbols_jv', 'goog.i18n.DateTimeSymbols_jv_ID', 'goog.i18n.DateTimeSymbols_ka_GE', 'goog.i18n.DateTimeSymbols_kab', 'goog.i18n.DateTimeSymbols_kab_DZ', 'goog.i18n.DateTimeSymbols_kam', 'goog.i18n.DateTimeSymbols_kam_KE', 'goog.i18n.DateTimeSymbols_kde', 'goog.i18n.DateTimeSymbols_kde_TZ', 'goog.i18n.DateTimeSymbols_kea', 'goog.i18n.DateTimeSymbols_kea_CV', 'goog.i18n.DateTimeSymbols_khq', 'goog.i18n.DateTimeSymbols_khq_ML', 'goog.i18n.DateTimeSymbols_ki', 'goog.i18n.DateTimeSymbols_ki_KE', 'goog.i18n.DateTimeSymbols_kk_KZ', 'goog.i18n.DateTimeSymbols_kkj', 'goog.i18n.DateTimeSymbols_kkj_CM', 'goog.i18n.DateTimeSymbols_kl', 'goog.i18n.DateTimeSymbols_kl_GL', 'goog.i18n.DateTimeSymbols_kln', 'goog.i18n.DateTimeSymbols_kln_KE', 'goog.i18n.DateTimeSymbols_km_KH', 'goog.i18n.DateTimeSymbols_kn_IN', 'goog.i18n.DateTimeSymbols_ko_KP', 'goog.i18n.DateTimeSymbols_ko_KR', 'goog.i18n.DateTimeSymbols_kok', 'goog.i18n.DateTimeSymbols_kok_IN', 'goog.i18n.DateTimeSymbols_ks', 'goog.i18n.DateTimeSymbols_ks_IN', 'goog.i18n.DateTimeSymbols_ksb', 'goog.i18n.DateTimeSymbols_ksb_TZ', 'goog.i18n.DateTimeSymbols_ksf', 'goog.i18n.DateTimeSymbols_ksf_CM', 'goog.i18n.DateTimeSymbols_ksh', 'goog.i18n.DateTimeSymbols_ksh_DE', 'goog.i18n.DateTimeSymbols_ku', 'goog.i18n.DateTimeSymbols_ku_TR', 'goog.i18n.DateTimeSymbols_kw', 'goog.i18n.DateTimeSymbols_kw_GB', 'goog.i18n.DateTimeSymbols_ky_KG', 'goog.i18n.DateTimeSymbols_lag', 'goog.i18n.DateTimeSymbols_lag_TZ', 'goog.i18n.DateTimeSymbols_lb', 'goog.i18n.DateTimeSymbols_lb_LU', 'goog.i18n.DateTimeSymbols_lg', 'goog.i18n.DateTimeSymbols_lg_UG', 'goog.i18n.DateTimeSymbols_lkt', 'goog.i18n.DateTimeSymbols_lkt_US', 'goog.i18n.DateTimeSymbols_ln_AO', 'goog.i18n.DateTimeSymbols_ln_CD', 'goog.i18n.DateTimeSymbols_ln_CF', 'goog.i18n.DateTimeSymbols_ln_CG', 'goog.i18n.DateTimeSymbols_lo_LA', 'goog.i18n.DateTimeSymbols_lrc', 'goog.i18n.DateTimeSymbols_lrc_IQ', 'goog.i18n.DateTimeSymbols_lrc_IR', 'goog.i18n.DateTimeSymbols_lt_LT', 'goog.i18n.DateTimeSymbols_lu', 'goog.i18n.DateTimeSymbols_lu_CD', 'goog.i18n.DateTimeSymbols_luo', 'goog.i18n.DateTimeSymbols_luo_KE', 'goog.i18n.DateTimeSymbols_luy', 'goog.i18n.DateTimeSymbols_luy_KE', 'goog.i18n.DateTimeSymbols_lv_LV', 'goog.i18n.DateTimeSymbols_mas', 'goog.i18n.DateTimeSymbols_mas_KE', 'goog.i18n.DateTimeSymbols_mas_TZ', 'goog.i18n.DateTimeSymbols_mer', 'goog.i18n.DateTimeSymbols_mer_KE', 'goog.i18n.DateTimeSymbols_mfe', 'goog.i18n.DateTimeSymbols_mfe_MU', 'goog.i18n.DateTimeSymbols_mg', 'goog.i18n.DateTimeSymbols_mg_MG', 'goog.i18n.DateTimeSymbols_mgh', 'goog.i18n.DateTimeSymbols_mgh_MZ', 'goog.i18n.DateTimeSymbols_mgo', 'goog.i18n.DateTimeSymbols_mgo_CM', 'goog.i18n.DateTimeSymbols_mi', 'goog.i18n.DateTimeSymbols_mi_NZ', 'goog.i18n.DateTimeSymbols_mk_MK', 'goog.i18n.DateTimeSymbols_ml_IN', 'goog.i18n.DateTimeSymbols_mn_MN', 'goog.i18n.DateTimeSymbols_mr_IN', 'goog.i18n.DateTimeSymbols_ms_BN', 'goog.i18n.DateTimeSymbols_ms_MY', 'goog.i18n.DateTimeSymbols_ms_SG', 'goog.i18n.DateTimeSymbols_mt_MT', 'goog.i18n.DateTimeSymbols_mua', 'goog.i18n.DateTimeSymbols_mua_CM', 'goog.i18n.DateTimeSymbols_my_MM', 'goog.i18n.DateTimeSymbols_mzn', 'goog.i18n.DateTimeSymbols_mzn_IR', 'goog.i18n.DateTimeSymbols_naq', 'goog.i18n.DateTimeSymbols_naq_NA', 'goog.i18n.DateTimeSymbols_nb_NO', 'goog.i18n.DateTimeSymbols_nb_SJ', 'goog.i18n.DateTimeSymbols_nd', 'goog.i18n.DateTimeSymbols_nd_ZW', 'goog.i18n.DateTimeSymbols_nds', 'goog.i18n.DateTimeSymbols_nds_DE', 'goog.i18n.DateTimeSymbols_nds_NL', 'goog.i18n.DateTimeSymbols_ne_IN', 'goog.i18n.DateTimeSymbols_ne_NP', 'goog.i18n.DateTimeSymbols_nl_AW', 'goog.i18n.DateTimeSymbols_nl_BE', 'goog.i18n.DateTimeSymbols_nl_BQ', 'goog.i18n.DateTimeSymbols_nl_CW', 'goog.i18n.DateTimeSymbols_nl_NL', 'goog.i18n.DateTimeSymbols_nl_SR', 'goog.i18n.DateTimeSymbols_nl_SX', 'goog.i18n.DateTimeSymbols_nmg', 'goog.i18n.DateTimeSymbols_nmg_CM', 'goog.i18n.DateTimeSymbols_nn', 'goog.i18n.DateTimeSymbols_nn_NO', 'goog.i18n.DateTimeSymbols_nnh', 'goog.i18n.DateTimeSymbols_nnh_CM', 'goog.i18n.DateTimeSymbols_nus', 'goog.i18n.DateTimeSymbols_nus_SS', 'goog.i18n.DateTimeSymbols_nyn', 'goog.i18n.DateTimeSymbols_nyn_UG', 'goog.i18n.DateTimeSymbols_om', 'goog.i18n.DateTimeSymbols_om_ET', 'goog.i18n.DateTimeSymbols_om_KE', 'goog.i18n.DateTimeSymbols_or_IN', 'goog.i18n.DateTimeSymbols_os', 'goog.i18n.DateTimeSymbols_os_GE', 'goog.i18n.DateTimeSymbols_os_RU', 'goog.i18n.DateTimeSymbols_pa_Arab', 'goog.i18n.DateTimeSymbols_pa_Arab_PK', 'goog.i18n.DateTimeSymbols_pa_Guru', 'goog.i18n.DateTimeSymbols_pa_Guru_IN', 'goog.i18n.DateTimeSymbols_pl_PL', 'goog.i18n.DateTimeSymbols_ps', 'goog.i18n.DateTimeSymbols_ps_AF', 'goog.i18n.DateTimeSymbols_ps_PK', 'goog.i18n.DateTimeSymbols_pt_AO', 'goog.i18n.DateTimeSymbols_pt_CH', 'goog.i18n.DateTimeSymbols_pt_CV', 'goog.i18n.DateTimeSymbols_pt_GQ', 'goog.i18n.DateTimeSymbols_pt_GW', 'goog.i18n.DateTimeSymbols_pt_LU', 'goog.i18n.DateTimeSymbols_pt_MO', 'goog.i18n.DateTimeSymbols_pt_MZ', 'goog.i18n.DateTimeSymbols_pt_ST', 'goog.i18n.DateTimeSymbols_pt_TL', 'goog.i18n.DateTimeSymbols_qu', 'goog.i18n.DateTimeSymbols_qu_BO', 'goog.i18n.DateTimeSymbols_qu_EC', 'goog.i18n.DateTimeSymbols_qu_PE', 'goog.i18n.DateTimeSymbols_rm', 'goog.i18n.DateTimeSymbols_rm_CH', 'goog.i18n.DateTimeSymbols_rn', 'goog.i18n.DateTimeSymbols_rn_BI', 'goog.i18n.DateTimeSymbols_ro_MD', 'goog.i18n.DateTimeSymbols_ro_RO', 'goog.i18n.DateTimeSymbols_rof', 'goog.i18n.DateTimeSymbols_rof_TZ', 'goog.i18n.DateTimeSymbols_ru_BY', 'goog.i18n.DateTimeSymbols_ru_KG', 'goog.i18n.DateTimeSymbols_ru_KZ', 'goog.i18n.DateTimeSymbols_ru_MD', 'goog.i18n.DateTimeSymbols_ru_RU', 'goog.i18n.DateTimeSymbols_ru_UA', 'goog.i18n.DateTimeSymbols_rw', 'goog.i18n.DateTimeSymbols_rw_RW', 'goog.i18n.DateTimeSymbols_rwk', 'goog.i18n.DateTimeSymbols_rwk_TZ', 'goog.i18n.DateTimeSymbols_sah', 'goog.i18n.DateTimeSymbols_sah_RU', 'goog.i18n.DateTimeSymbols_saq', 'goog.i18n.DateTimeSymbols_saq_KE', 'goog.i18n.DateTimeSymbols_sbp', 'goog.i18n.DateTimeSymbols_sbp_TZ', 'goog.i18n.DateTimeSymbols_sd', 'goog.i18n.DateTimeSymbols_sd_PK', 'goog.i18n.DateTimeSymbols_se', 'goog.i18n.DateTimeSymbols_se_FI', 'goog.i18n.DateTimeSymbols_se_NO', 'goog.i18n.DateTimeSymbols_se_SE', 'goog.i18n.DateTimeSymbols_seh', 'goog.i18n.DateTimeSymbols_seh_MZ', 'goog.i18n.DateTimeSymbols_ses', 'goog.i18n.DateTimeSymbols_ses_ML', 'goog.i18n.DateTimeSymbols_sg', 'goog.i18n.DateTimeSymbols_sg_CF', 'goog.i18n.DateTimeSymbols_shi', 'goog.i18n.DateTimeSymbols_shi_Latn', 'goog.i18n.DateTimeSymbols_shi_Latn_MA', 'goog.i18n.DateTimeSymbols_shi_Tfng', 'goog.i18n.DateTimeSymbols_shi_Tfng_MA', 'goog.i18n.DateTimeSymbols_si_LK', 'goog.i18n.DateTimeSymbols_sk_SK', 'goog.i18n.DateTimeSymbols_sl_SI', 'goog.i18n.DateTimeSymbols_smn', 'goog.i18n.DateTimeSymbols_smn_FI', 'goog.i18n.DateTimeSymbols_sn', 'goog.i18n.DateTimeSymbols_sn_ZW', 'goog.i18n.DateTimeSymbols_so', 'goog.i18n.DateTimeSymbols_so_DJ', 'goog.i18n.DateTimeSymbols_so_ET', 'goog.i18n.DateTimeSymbols_so_KE', 'goog.i18n.DateTimeSymbols_so_SO', 'goog.i18n.DateTimeSymbols_sq_AL', 'goog.i18n.DateTimeSymbols_sq_MK', 'goog.i18n.DateTimeSymbols_sq_XK', 'goog.i18n.DateTimeSymbols_sr_Cyrl', 'goog.i18n.DateTimeSymbols_sr_Cyrl_BA', 'goog.i18n.DateTimeSymbols_sr_Cyrl_ME', 'goog.i18n.DateTimeSymbols_sr_Cyrl_RS', 'goog.i18n.DateTimeSymbols_sr_Cyrl_XK', 'goog.i18n.DateTimeSymbols_sr_Latn_BA', 'goog.i18n.DateTimeSymbols_sr_Latn_ME', 'goog.i18n.DateTimeSymbols_sr_Latn_RS', 'goog.i18n.DateTimeSymbols_sr_Latn_XK', 'goog.i18n.DateTimeSymbols_sv_AX', 'goog.i18n.DateTimeSymbols_sv_FI', 'goog.i18n.DateTimeSymbols_sv_SE', 'goog.i18n.DateTimeSymbols_sw_CD', 'goog.i18n.DateTimeSymbols_sw_KE', 'goog.i18n.DateTimeSymbols_sw_TZ', 'goog.i18n.DateTimeSymbols_sw_UG', 'goog.i18n.DateTimeSymbols_ta_IN', 'goog.i18n.DateTimeSymbols_ta_LK', 'goog.i18n.DateTimeSymbols_ta_MY', 'goog.i18n.DateTimeSymbols_ta_SG', 'goog.i18n.DateTimeSymbols_te_IN', 'goog.i18n.DateTimeSymbols_teo', 'goog.i18n.DateTimeSymbols_teo_KE', 'goog.i18n.DateTimeSymbols_teo_UG', 'goog.i18n.DateTimeSymbols_tg', 'goog.i18n.DateTimeSymbols_tg_TJ', 'goog.i18n.DateTimeSymbols_th_TH', 'goog.i18n.DateTimeSymbols_ti', 'goog.i18n.DateTimeSymbols_ti_ER', 'goog.i18n.DateTimeSymbols_ti_ET', 'goog.i18n.DateTimeSymbols_tk', 'goog.i18n.DateTimeSymbols_tk_TM', 'goog.i18n.DateTimeSymbols_to', 'goog.i18n.DateTimeSymbols_to_TO', 'goog.i18n.DateTimeSymbols_tr_CY', 'goog.i18n.DateTimeSymbols_tr_TR', 'goog.i18n.DateTimeSymbols_tt', 'goog.i18n.DateTimeSymbols_tt_RU', 'goog.i18n.DateTimeSymbols_twq', 'goog.i18n.DateTimeSymbols_twq_NE', 'goog.i18n.DateTimeSymbols_tzm', 'goog.i18n.DateTimeSymbols_tzm_MA', 'goog.i18n.DateTimeSymbols_ug', 'goog.i18n.DateTimeSymbols_ug_CN', 'goog.i18n.DateTimeSymbols_uk_UA', 'goog.i18n.DateTimeSymbols_ur_IN', 'goog.i18n.DateTimeSymbols_ur_PK', 'goog.i18n.DateTimeSymbols_uz_Arab', 'goog.i18n.DateTimeSymbols_uz_Arab_AF', 'goog.i18n.DateTimeSymbols_uz_Cyrl', 'goog.i18n.DateTimeSymbols_uz_Cyrl_UZ', 'goog.i18n.DateTimeSymbols_uz_Latn', 'goog.i18n.DateTimeSymbols_uz_Latn_UZ', 'goog.i18n.DateTimeSymbols_vai', 'goog.i18n.DateTimeSymbols_vai_Latn', 'goog.i18n.DateTimeSymbols_vai_Latn_LR', 'goog.i18n.DateTimeSymbols_vai_Vaii', 'goog.i18n.DateTimeSymbols_vai_Vaii_LR', 'goog.i18n.DateTimeSymbols_vi_VN', 'goog.i18n.DateTimeSymbols_vun', 'goog.i18n.DateTimeSymbols_vun_TZ', 'goog.i18n.DateTimeSymbols_wae', 'goog.i18n.DateTimeSymbols_wae_CH', 'goog.i18n.DateTimeSymbols_wo', 'goog.i18n.DateTimeSymbols_wo_SN', 'goog.i18n.DateTimeSymbols_xh', 'goog.i18n.DateTimeSymbols_xh_ZA', 'goog.i18n.DateTimeSymbols_xog', 'goog.i18n.DateTimeSymbols_xog_UG', 'goog.i18n.DateTimeSymbols_yav', 'goog.i18n.DateTimeSymbols_yav_CM', 'goog.i18n.DateTimeSymbols_yi', 'goog.i18n.DateTimeSymbols_yi_001', 'goog.i18n.DateTimeSymbols_yo', 'goog.i18n.DateTimeSymbols_yo_BJ', 'goog.i18n.DateTimeSymbols_yo_NG', 'goog.i18n.DateTimeSymbols_yue', 'goog.i18n.DateTimeSymbols_yue_Hans', 'goog.i18n.DateTimeSymbols_yue_Hans_CN', 'goog.i18n.DateTimeSymbols_yue_Hant', 'goog.i18n.DateTimeSymbols_yue_Hant_HK', 'goog.i18n.DateTimeSymbols_zgh', 'goog.i18n.DateTimeSymbols_zgh_MA', 'goog.i18n.DateTimeSymbols_zh_Hans', 'goog.i18n.DateTimeSymbols_zh_Hans_CN', 'goog.i18n.DateTimeSymbols_zh_Hans_HK', 'goog.i18n.DateTimeSymbols_zh_Hans_MO', 'goog.i18n.DateTimeSymbols_zh_Hans_SG', 'goog.i18n.DateTimeSymbols_zh_Hant', 'goog.i18n.DateTimeSymbols_zh_Hant_HK', 'goog.i18n.DateTimeSymbols_zh_Hant_MO', 'goog.i18n.DateTimeSymbols_zh_Hant_TW', 'goog.i18n.DateTimeSymbols_zu_ZA'], ['goog.i18n.DateTimeSymbols']); goog.addDependency("i18n/graphemebreak.js", ['goog.i18n.GraphemeBreak'], ['goog.asserts', 'goog.i18n.uChar', 'goog.structs.InversionMap']); goog.addDependency("i18n/graphemebreak_test.js", [], []); +goog.addDependency("i18n/localefeature.js", [], []); +goog.addDependency("i18n/localefeature_test.js", [], []); goog.addDependency("i18n/messageformat.js", ['goog.i18n.MessageFormat'], ['goog.array', 'goog.asserts', 'goog.i18n.CompactNumberFormatSymbols', 'goog.i18n.NumberFormat', 'goog.i18n.NumberFormatSymbols', 'goog.i18n.ordinalRules', 'goog.i18n.pluralRules']); goog.addDependency("i18n/messageformat_test.js", [], []); goog.addDependency("i18n/mime.js", ['goog.i18n.mime', 'goog.i18n.mime.encode'], ['goog.array', 'goog.i18n.uChar']); @@ -987,7 +994,7 @@ goog.addDependency("module/moduleinfo.js", ['goog.module.ModuleInfo'], ['goog.Di goog.addDependency("module/moduleinfo_test.js", [], []); goog.addDependency("module/moduleloadcallback.js", ['goog.module.ModuleLoadCallback'], ['goog.debug.entryPointRegistry', 'goog.module']); goog.addDependency("module/moduleloadcallback_test.js", [], []); -goog.addDependency("module/moduleloader.js", ['goog.module.ModuleLoader'], ['goog.Timer', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.safe', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventId', 'goog.events.EventTarget', 'goog.html.TrustedResourceUrl', 'goog.labs.userAgent.browser', 'goog.log', 'goog.module.AbstractModuleLoader', 'goog.net.BulkLoader', 'goog.net.EventType', 'goog.net.jsloader', 'goog.userAgent', 'goog.userAgent.product']); +goog.addDependency("module/moduleloader.js", ['goog.module.ModuleLoader'], ['goog.Timer', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.safe', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventId', 'goog.events.EventTarget', 'goog.functions', 'goog.html.TrustedResourceUrl', 'goog.labs.userAgent.browser', 'goog.log', 'goog.module.AbstractModuleLoader', 'goog.net.BulkLoader', 'goog.net.EventType', 'goog.net.jsloader', 'goog.userAgent', 'goog.userAgent.product']); goog.addDependency("module/moduleloader_test.js", ['goog.module.ModuleLoaderTest'], ['goog.Promise', 'goog.array', 'goog.dom', 'goog.dom.TagName', 'goog.events', 'goog.functions', 'goog.html.TrustedResourceUrl', 'goog.loader.activeModuleManager', 'goog.module.ModuleLoader', 'goog.module.ModuleManager', 'goog.net.BulkLoader', 'goog.net.XmlHttp', 'goog.object', 'goog.string.Const', 'goog.testing.PropertyReplacer', 'goog.testing.TestCase', 'goog.testing.events.EventObserver', 'goog.testing.jsunit', 'goog.userAgent']); goog.addDependency("module/modulemanager.js", ['goog.module.ModuleManager', 'goog.module.ModuleManager.CallbackType', 'goog.module.ModuleManager.FailureType'], ['goog.array', 'goog.asserts', 'goog.async.Deferred', 'goog.debug.Trace', 'goog.disposable.IDisposable', 'goog.disposeAll', 'goog.loader.AbstractModuleManager', 'goog.loader.activeModuleManager', 'goog.log', 'goog.module', 'goog.module.ModuleInfo', 'goog.module.ModuleLoadCallback', 'goog.object']); goog.addDependency("module/modulemanager_test.js", ['goog.module.ModuleManagerTest'], ['goog.array', 'goog.functions', 'goog.module.BaseModule', 'goog.module.ModuleManager', 'goog.testing', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.recordFunction']); @@ -1197,7 +1204,17 @@ goog.addDependency("storage/richstorage_test.js", [], []); goog.addDependency("storage/storage.js", ['goog.storage.Storage'], ['goog.json', 'goog.storage.ErrorCode']); goog.addDependency("storage/storage_test.js", [], []); goog.addDependency("storage/storagetester.js", ['goog.storage.storageTester'], ['goog.storage.Storage', 'goog.structs.Map', 'goog.testing.asserts']); +goog.addDependency("streams/defines.js", [], []); +goog.addDependency("streams/full.js", [], []); +goog.addDependency("streams/full_lite_test.js", [], []); +goog.addDependency("streams/full_test.js", [], []); goog.addDependency("streams/lite.js", [], []); +goog.addDependency("streams/lite_impl.js", [], []); +goog.addDependency("streams/lite_impl_test.js", [], []); +goog.addDependency("streams/lite_native_impl.js", [], []); +goog.addDependency("streams/lite_native_impl_test.js", [], []); +goog.addDependency("streams/lite_test_cases.js", [], ['goog.testing.jsunit']); +goog.addDependency("streams/lite_types.js", [], []); goog.addDependency("string/const.js", ['goog.string.Const'], ['goog.asserts', 'goog.string.TypedString']); goog.addDependency("string/const_test.js", [], []); goog.addDependency("string/internal.js", ['goog.string.internal'], []); @@ -1770,6 +1787,7 @@ goog.addDependency("window/window_test.js", ['goog.windowTest'], ['goog.Promise' // Load Blockly. goog.require('Blockly'); goog.require('Blockly.ASTNode'); +goog.require('Blockly.Action'); goog.require('Blockly.Block'); goog.require('Blockly.BlockDragSurfaceSvg'); goog.require('Blockly.BlockDragger'); @@ -1878,6 +1896,7 @@ goog.require('Blockly.blockRendering.constants'); goog.require('Blockly.blockRendering.highlightConstants'); goog.require('Blockly.constants'); goog.require('Blockly.inject'); +goog.require('Blockly.user.keyMap'); goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.Rect'); diff --git a/core/blockly.js b/core/blockly.js index 123ab8c57..629900978 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -218,7 +218,7 @@ Blockly.onKeyDown_ = function(e) { // Pressing esc closes the context menu. Blockly.hideChaff(); if (Blockly.keyboardAccessibilityMode) { - Blockly.Navigation.navigate(e); + Blockly.Navigation.onKeyPress(e); } } else if (e.keyCode == 8 || e.keyCode == 46) { // Delete or backspace. @@ -274,9 +274,12 @@ Blockly.onKeyDown_ = function(e) { // 'z' for undo 'Z' is for redo. Blockly.hideChaff(); mainWorkspace.undo(e.shiftKey); + } else if (Blockly.keyboardAccessibilityMode && + Blockly.Navigation.onKeyPress(e)) { + return; } } else if (Blockly.keyboardAccessibilityMode && - Blockly.Navigation.navigate(e)) { + Blockly.Navigation.onKeyPress(e)) { return; } // Common code for delete and cut. diff --git a/core/inject.js b/core/inject.js index af345aa64..e400054a9 100644 --- a/core/inject.js +++ b/core/inject.js @@ -74,6 +74,7 @@ Blockly.inject = function(container, opt_options) { var workspace = Blockly.createMainWorkspace_(svg, options, blockDragSurface, workspaceDragSurface); Blockly.setTheme(options.theme); + Blockly.user.keyMap.setKeyMap(options.keyMap); Blockly.init_(workspace); Blockly.mainWorkspace = workspace; diff --git a/core/keyboard_nav/key_action.js b/core/keyboard_nav/key_action.js new file mode 100644 index 000000000..6775a4fbd --- /dev/null +++ b/core/keyboard_nav/key_action.js @@ -0,0 +1,42 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 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 The class representing an action. + * Used primarily for keyboard navigation. + */ +'use strict'; + +goog.provide('Blockly.Action'); + +/** + * Class for a single action. + * There can be one action for each key. If the action only applies to a + * single state (toolbox, flyout, workspace) then the function should handle this. + * @param {string} name The name of the action. + * @param {string} desc The description of the action. + * @param {Function} func The function to be called when the key is pressed. + * @constructor + */ +Blockly.Action = function(name, desc, func) { + this.name = name; + this.desc = desc; + this.func = func; +}; diff --git a/core/keyboard_nav/key_map.js b/core/keyboard_nav/key_map.js new file mode 100644 index 000000000..52d180188 --- /dev/null +++ b/core/keyboard_nav/key_map.js @@ -0,0 +1,142 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 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 The namespace used to keep track of keyboard actions and the + * key codes used to execute those actions. + * This is used primarily for keyboard navigation. + */ +'use strict'; + +goog.provide('Blockly.user.keyMap'); + +/** + * Holds the serialized key to key action mapping. + * @type {Object} + */ +Blockly.user.keyMap.map_ = {}; + +/** + * List of modifier keys checked when serializing the key event. + * @type {Array} + */ +Blockly.user.keyMap.modifierKeys = ['Shift','Control','Alt','Meta']; + +/** + * Update the key map to contain the new action. + * @param {!string} keyCode The key code serialized by the serializeKeyEvent. + * @param {!Blockly.Action} action The action to be executed when the keys + * corresponding to the serialized key code is pressed. + * @package + */ +Blockly.user.keyMap.setActionForKey = function(keyCode, action) { + var oldKey = Blockly.user.keyMap.getKeyByAction(action); + // If the action already exists in the key map remove it and add the new mapping. + if (oldKey) { + delete Blockly.user.keyMap.map_[oldKey]; + } + Blockly.user.keyMap.map_[keyCode] = action; +}; + +/** + * Creates a new key map. + * @param {Object} keyMap The object holding the key + * to action mapping. + * @package + */ +Blockly.user.keyMap.setKeyMap = function(keyMap) { + Blockly.user.keyMap.map_ = keyMap; +}; + +/** + * Gets the current key map. + * @return {Object} The object holding the key to + * action mapping. + * @package + */ +Blockly.user.keyMap.getKeyMap = function() { + return Object.assign({}, Blockly.user.keyMap.map_); +}; + +/** + * Get the action by the serialized key code. + * @param {string} keyCode The serialized key code. + * @return {Blockly.Action|undefined} The action holding the function to + * call when the given keyCode is used or undefined if no action exists. + * @package + */ +Blockly.user.keyMap.getActionByKeyCode = function(keyCode) { + return Blockly.user.keyMap.map_[keyCode]; +}; + +/** + * Get the serialized key that corresponds to the action. + * @param {!Blockly.Action} action The action for which we want to get + * the key. + * @return {string} The serialized key or null if the action does not have + * a key mapping. + * @package + */ +Blockly.user.keyMap.getKeyByAction = function(action) { + var keys = Object.keys(Blockly.user.keyMap.map_); + for (var i = 0, key; key = keys[i]; i++) { + if (Blockly.user.keyMap.map_[key].name === action.name) { + return key; + } + } + return null; +}; + +/** + * Serialize the key event. + * @param {!Event} e A key up event holding the key code. + * @return {!string} A string containing the serialized key event. + */ +Blockly.user.keyMap.serializeKeyEvent = function(e) { + var modifierKeys = Blockly.user.keyMap.modifierKeys; + var key = ''; + for (var i = 0, keyName; keyName = modifierKeys[i]; i++) { + if (e.getModifierState(keyName)) { + key += keyName; + } + } + key += e.keyCode; + return key; +}; + +/** + * Creates the default key map. + * @return {!Object} An object holding the default key + * to action mapping. + */ +Blockly.user.keyMap.createDefaultKeyMap = function() { + var map = {}; + map[goog.events.KeyCodes.W] = Blockly.Navigation.ACTION_PREVIOUS; + map[goog.events.KeyCodes.A] = Blockly.Navigation.ACTION_OUT; + map[goog.events.KeyCodes.S] = Blockly.Navigation.ACTION_NEXT; + map[goog.events.KeyCodes.D] = Blockly.Navigation.ACTION_IN; + map[goog.events.KeyCodes.I] = Blockly.Navigation.ACTION_INSERT; + map[goog.events.KeyCodes.ENTER] = Blockly.Navigation.ACTION_MARK; + map[goog.events.KeyCodes.X] = Blockly.Navigation.ACTION_DISCONNECT; + map[goog.events.KeyCodes.T] = Blockly.Navigation.ACTION_TOOLBOX; + map[goog.events.KeyCodes.E] = Blockly.Navigation.ACTION_EXIT; + map[goog.events.KeyCodes.ESC] = Blockly.Navigation.ACTION_EXIT; + return map; +}; diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js index 6f9d0a5f9..8b40b9fbe 100644 --- a/core/keyboard_nav/navigation.js +++ b/core/keyboard_nav/navigation.js @@ -20,7 +20,9 @@ goog.provide('Blockly.Navigation'); +goog.require('Blockly.Action'); goog.require('Blockly.ASTNode'); +goog.require('Blockly.user.keyMap'); /** @@ -357,8 +359,8 @@ Blockly.Navigation.insertFromFlyout = function() { var prevConnection = newBlock.previousConnection; var outConnection = newBlock.outputConnection; var topConnection = prevConnection ? prevConnection : outConnection; - //TODO: This will have to be fixed when we add in a block that does not have - //a previous or output connection + // TODO: This will have to be fixed when we add in a block that does not have + // a previous or output connection var astNode = Blockly.ASTNode.createConnectionNode(topConnection); Blockly.Navigation.cursor_.setLocation(astNode); Blockly.Navigation.removeMark(); @@ -606,8 +608,8 @@ Blockly.Navigation.focusWorkspace = function() { if (Blockly.selected) { var previousConnection = Blockly.selected.previousConnection; var outputConnection = Blockly.selected.outputConnection; - //TODO: This still needs to work with blocks that have neither previous - //or output connection. + // TODO: This still needs to work with blocks that have neither previous + // or output connection. var connection = previousConnection ? previousConnection : outputConnection; var newAstNode = Blockly.ASTNode.createConnectionNode(connection); cursor.setLocation(newAstNode); @@ -647,133 +649,15 @@ Blockly.Navigation.handleEnterForWS = function() { /** - * TODO: Revisit keycodes before releasing * Handler for all the keyboard navigation events. * @param {Event} e The keyboard event. * @return {!boolean} True if the key was handled false otherwise. */ -Blockly.Navigation.navigate = function(e) { - var curState = Blockly.Navigation.currentState_; - if (e.keyCode === goog.events.KeyCodes.T) { - var workspace = Blockly.getMainWorkspace(); - if (!workspace.getToolbox()) { - Blockly.Navigation.focusFlyout(); - Blockly.Navigation.log('T: Focus Flyout'); - } else { - Blockly.Navigation.focusToolbox(); - Blockly.Navigation.log('T: Focus Toolbox'); - } - return true; - } else if (curState === Blockly.Navigation.STATE_FLYOUT) { - return Blockly.Navigation.flyoutKeyHandler(e); - } else if (curState === Blockly.Navigation.STATE_WS) { - return Blockly.Navigation.workspaceKeyHandler(e); - } else if (curState === Blockly.Navigation.STATE_TOOLBOX) { - return Blockly.Navigation.toolboxKeyHandler(e); - } else { - Blockly.Navigation.log('Not a valid key '); - } - return false; -}; - -/** - * Handles all keyboard events when the user is focused on the flyout. - * @param {Event} e The keyboard event. - * @return {!boolean} True if the key was handled false otherwise. - */ -Blockly.Navigation.flyoutKeyHandler = function(e) { - if (e.keyCode === goog.events.KeyCodes.W) { - Blockly.Navigation.selectPreviousBlockInFlyout(); - Blockly.Navigation.log('W: Flyout : Previous'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.A) { - Blockly.Navigation.focusToolbox(); - Blockly.Navigation.log('A: Flyout : Go To Toolbox'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.S) { - Blockly.Navigation.selectNextBlockInFlyout(); - Blockly.Navigation.log('S: Flyout : Next'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.ENTER) { - Blockly.Navigation.insertFromFlyout(); - Blockly.Navigation.log('Enter: Flyout : Select'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.E || - e.keyCode === goog.events.KeyCodes.ESC) { - Blockly.Navigation.focusWorkspace(); - Blockly.Navigation.log('E or ESC: Flyout: Exit'); - return true; - } - return false; -}; - -/** - * Handles all keyboard events when the user is focused on the toolbox. - * @param {Event} e The keyboard event. - * @return {!boolean} True if the key was handled false otherwise. - */ -Blockly.Navigation.toolboxKeyHandler = function(e) { - if (e.keyCode === goog.events.KeyCodes.W) { - Blockly.Navigation.previousCategory(); - Blockly.Navigation.log('W: Toolbox : Previous'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.A) { - Blockly.Navigation.outCategory(); - Blockly.Navigation.log('A: Toolbox : Out'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.S) { - Blockly.Navigation.nextCategory(); - Blockly.Navigation.log('S: Toolbox : Next'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.D) { - Blockly.Navigation.inCategory(); - Blockly.Navigation.log('D: Toolbox : Go to flyout'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.ENTER) { - //TODO: focus on flyout OR open if the category is nested - return true; - } else if (e.keyCode === goog.events.KeyCodes.E || - e.keyCode === goog.events.KeyCodes.ESC) { - Blockly.Navigation.log('E or ESC: Toolbox: Exit'); - Blockly.Navigation.focusWorkspace(); - return true; - } - return false; -}; - -/** - * Handles all keyboard events when the user is focused on the workspace. - * @param {Event} e The keyboard event. - * @return {!boolean} True if the key was handled false otherwise. - */ -Blockly.Navigation.workspaceKeyHandler = function(e) { - if (e.keyCode === goog.events.KeyCodes.W) { - Blockly.Navigation.cursor_.prev(); - Blockly.Navigation.log('W: Workspace : Out'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.A) { - Blockly.Navigation.cursor_.out(); - Blockly.Navigation.log('S: Workspace : Previous'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.S) { - Blockly.Navigation.cursor_.next(); - Blockly.Navigation.log('S: Workspace : In'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.D) { - Blockly.Navigation.cursor_.in(); - Blockly.Navigation.log('S: Workspace : Next'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.I) { - Blockly.Navigation.modify(); - Blockly.Navigation.log('I: Workspace : Insert/Connect Blocks'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.ENTER) { - Blockly.Navigation.handleEnterForWS(); - Blockly.Navigation.log('Enter: Workspace : Mark'); - return true; - } else if (e.keyCode === goog.events.KeyCodes.X) { - Blockly.Navigation.log('X: Workspace: Disconnect Blocks'); - Blockly.Navigation.disconnectBlocks(); +Blockly.Navigation.onKeyPress = function(e) { + var key = Blockly.user.keyMap.serializeKeyEvent(e); + var action = Blockly.user.keyMap.getActionByKeyCode(key); + if (action) { + action.func.call(); return true; } return false; @@ -834,3 +718,113 @@ Blockly.Navigation.error = function(msg) { console.error(msg); } }; + +/** + * The previous action. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_PREVIOUS = new Blockly.Action('previous', 'Goes to the previous location', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_WS) { + Blockly.Navigation.cursor_.prev(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_FLYOUT) { + Blockly.Navigation.selectPreviousBlockInFlyout(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_TOOLBOX) { + Blockly.Navigation.previousCategory(); + } +}); + +/** + * The previous action. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_OUT = new Blockly.Action('out', 'Goes out', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_WS) { + Blockly.Navigation.cursor_.out(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_FLYOUT) { + Blockly.Navigation.focusToolbox(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_TOOLBOX) { + Blockly.Navigation.outCategory(); + } +}); + +/** + * The previous action. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_NEXT = new Blockly.Action('next', 'Goes to the next location', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_WS) { + Blockly.Navigation.cursor_.next(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_FLYOUT) { + Blockly.Navigation.selectNextBlockInFlyout(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_TOOLBOX) { + Blockly.Navigation.nextCategory(); + } +}); + +/** + * The action to go in. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_IN = new Blockly.Action('in', 'Goes in', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_WS) { + Blockly.Navigation.cursor_.in(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_TOOLBOX) { + Blockly.Navigation.inCategory(); + } +}); + +/** + * The action to try to insert a block. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_INSERT = new Blockly.Action('insert', + 'Tries to connect the current location to the marked location', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_WS) { + Blockly.Navigation.modify(); + } + }); + +/** + * The action to mark a certain location. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_MARK = new Blockly.Action('mark', 'Marks the current location', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_WS) { + Blockly.Navigation.handleEnterForWS(); + } else if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_FLYOUT) { + Blockly.Navigation.insertFromFlyout(); + } +}); + +/** + * The action to disconnect a block. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_DISCONNECT = new Blockly.Action('disconnect', 'Disconnect the blocks', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_WS) { + Blockly.Navigation.disconnectBlocks(); + } +}); + +/** + * The action to open the toolbox. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_TOOLBOX = new Blockly.Action('toolbox', 'Open the toolbox', function() { + if (!Blockly.getMainWorkspace().getToolbox()) { + Blockly.Navigation.focusFlyout(); + } else { + Blockly.Navigation.focusToolbox(); + } +}); + +/** + * The action to exit the toolbox or flyout. + * @type Blockly.Action + */ +Blockly.Navigation.ACTION_EXIT = new Blockly.Action('exit', 'Exit the toolbox', function() { + if (Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_TOOLBOX || + Blockly.Navigation.currentState_ === Blockly.Navigation.STATE_FLYOUT) { + Blockly.Navigation.focusWorkspace(); + } +}); diff --git a/core/options.js b/core/options.js index ecd5497d0..8eb763246 100644 --- a/core/options.js +++ b/core/options.js @@ -119,6 +119,7 @@ Blockly.Options = function(options) { var oneBasedIndex = !!options['oneBasedIndex']; } var theme = options['theme'] || Blockly.Themes.Classic; + var keyMap = options['keyMap'] || Blockly.user.keyMap.createDefaultKeyMap(); this.RTL = rtl; this.oneBasedIndex = oneBasedIndex; @@ -143,6 +144,7 @@ Blockly.Options = function(options) { this.zoomOptions = Blockly.Options.parseZoomOptions_(options); this.toolboxPosition = toolboxPosition; this.theme = theme; + this.keyMap = keyMap; }; /** diff --git a/tests/mocha/index.html b/tests/mocha/index.html index 233801a72..4f606d1fc 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -42,6 +42,7 @@ + diff --git a/tests/mocha/key_map_test.js b/tests/mocha/key_map_test.js new file mode 100644 index 000000000..2b04b0bef --- /dev/null +++ b/tests/mocha/key_map_test.js @@ -0,0 +1,66 @@ +suite('Key Map Tests', function() { + setup(function() { + Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap()); + }); + + test('Test adding a new action to key map', function() { + var newAction = new Blockly.Action('test_action', 'test', function(){ + return "test"; + }); + Blockly.user.keyMap.setActionForKey('65', newAction); + assertEquals(Blockly.user.keyMap.map_['65'].name, 'test_action'); + }); + + test('Test giving an old action a new key', function() { + Blockly.user.keyMap.setActionForKey(goog.events.KeyCodes.F, Blockly.Navigation.ACTION_PREVIOUS); + assertEquals(Blockly.user.keyMap.map_[goog.events.KeyCodes.W], undefined); + assertEquals(Blockly.user.keyMap.map_[goog.events.KeyCodes.F], + Blockly.Navigation.ACTION_PREVIOUS); + }); + + test('Test get key by action defined', function() { + var key = Blockly.user.keyMap.getKeyByAction(Blockly.Navigation.ACTION_PREVIOUS); + assertEquals(key, goog.events.KeyCodes.W); + }); + + test('Test get key by action undefined', function() { + var key = Blockly.user.keyMap.getKeyByAction(new Blockly.Action('something')); + assertEquals(key, undefined); + }); + + test('Test set key map', function() { + var testKeyMap = Blockly.user.keyMap.createDefaultKeyMap(); + testKeyMap['randomKey'] = new Blockly.Action('test','',null); + Blockly.user.keyMap.setKeyMap(testKeyMap); + assertEquals(Blockly.user.keyMap.map_['randomKey'].name, 'test'); + }); + + test('Test get key map returns a clone', function() { + var keyMap = Blockly.user.keyMap.getKeyMap(); + keyMap['randomKey'] = new Blockly.Action('test', '', null); + assertEquals(Blockly.user.keyMap.map_['randomKey'], undefined); + }); + + test('Test serialize key code with modifiers', function() { + var mockEvent = { + getModifierState: function(){ + return true; + }, + keyCode: 65 + }; + var serializedKey = Blockly.user.keyMap.serializeKeyEvent(mockEvent); + assertEquals(serializedKey, 'ShiftControlAltMeta65'); + }); + + test('Test serialize key code without modifiers', function() { + var mockEvent = { + getModifierState: function(){ + return false; + }, + keyCode: 65 + }; + var serializedKey = Blockly.user.keyMap.serializeKeyEvent(mockEvent); + assertEquals(serializedKey, '65'); + }); + teardown(function() {}); +}); diff --git a/tests/mocha/navigation_test.js b/tests/mocha/navigation_test.js index f66969992..1fed55416 100644 --- a/tests/mocha/navigation_test.js +++ b/tests/mocha/navigation_test.js @@ -8,25 +8,26 @@ suite('Navigation', function() { }]); var toolbox = document.getElementById('toolbox-minimal'); + this.mockEvent = { + getModifierState: function() { + return false; + } + }; this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); }); test('Focuses workspace from flyout (e)', function() { Blockly.Navigation.currentState_ = Blockly.Navigation.STATE_FLYOUT; - var mockEvent = { - keyCode: goog.events.KeyCodes.E - }; - chai.assert.isTrue(Blockly.Navigation.navigate(mockEvent)); + this.mockEvent.keyCode = goog.events.KeyCodes.E; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_WS); }); test('Focuses workspace from flyout (escape)', function() { Blockly.Navigation.currentState_ = Blockly.Navigation.STATE_FLYOUT; - var mockEvent = { - keyCode: goog.events.KeyCodes.ESC - }; - chai.assert.isTrue(Blockly.Navigation.navigate(mockEvent)); + this.mockEvent.keyCode = goog.events.KeyCodes.ESC; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_WS); }); @@ -53,7 +54,11 @@ suite('Navigation', function() { var toolbox = document.getElementById('toolbox-categories'); this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); Blockly.Navigation.focusToolbox(); - + this.mockEvent = { + getModifierState: function() { + return false; + } + }; this.firstCategory_ = this.workspace.getToolbox().tree_.firstChild_; this.secondCategory_ = this.firstCategory_.getNextShownNode(); @@ -66,9 +71,8 @@ suite('Navigation', function() { }); test('Next', function() { - chai.assert.isTrue(Blockly.Navigation.navigate({ - keyCode: goog.events.KeyCodes.S - })); + this.mockEvent.keyCode = goog.events.KeyCodes.S; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_TOOLBOX); chai.assert.equal(Blockly.Navigation.currentCategory_, @@ -78,10 +82,9 @@ suite('Navigation', function() { // Should be a no-op. test('Next at end', function() { Blockly.Navigation.nextCategory(); + this.mockEvent.keyCode = goog.events.KeyCodes.S; var startCategory = Blockly.Navigation.currentCategory_; - chai.assert.isTrue(Blockly.Navigation.navigate({ - keyCode: goog.events.KeyCodes.S - })); + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_TOOLBOX); chai.assert.equal(Blockly.Navigation.currentCategory_, @@ -91,11 +94,10 @@ suite('Navigation', function() { test('Previous', function() { // Go forward one so that we can go back one: Blockly.Navigation.nextCategory(); + this.mockEvent.keyCode = goog.events.KeyCodes.W; chai.assert.equal(Blockly.Navigation.currentCategory_, this.secondCategory_); - chai.assert.isTrue(Blockly.Navigation.navigate({ - keyCode: goog.events.KeyCodes.W - })); + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_TOOLBOX); chai.assert.equal(Blockly.Navigation.currentCategory_, @@ -105,9 +107,8 @@ suite('Navigation', function() { // Should be a no-op. test('Previous at start', function() { var startCategory = Blockly.Navigation.currentCategory_; - chai.assert.isTrue(Blockly.Navigation.navigate({ - keyCode: goog.events.KeyCodes.W - })); + this.mockEvent.keyCode = goog.events.KeyCodes.W; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_TOOLBOX); chai.assert.equal(Blockly.Navigation.currentCategory_, @@ -115,9 +116,8 @@ suite('Navigation', function() { }); test('Out', function() { - chai.assert.isTrue(Blockly.Navigation.navigate({ - keyCode: goog.events.KeyCodes.A - })); + this.mockEvent.keyCode = goog.events.KeyCodes.A; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); // TODO (fenichel/aschmiedt): Decide whether out should go to the // workspace. chai.assert.equal(Blockly.Navigation.currentState_, @@ -125,9 +125,8 @@ suite('Navigation', function() { }); test('Go to flyout', function() { - chai.assert.isTrue(Blockly.Navigation.navigate({ - keyCode: goog.events.KeyCodes.D - })); + this.mockEvent.keyCode = goog.events.KeyCodes.D; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_FLYOUT); @@ -136,19 +135,15 @@ suite('Navigation', function() { test('Focuses workspace from toolbox (e)', function() { Blockly.Navigation.currentState_ = Blockly.Navigation.STATE_TOOLBOX; - var mockEvent = { - keyCode: goog.events.KeyCodes.E - }; - chai.assert.isTrue(Blockly.Navigation.navigate(mockEvent)); + this.mockEvent.keyCode = goog.events.KeyCodes.E; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_WS); }); test('Focuses workspace from toolbox (escape)', function() { Blockly.Navigation.currentState_ = Blockly.Navigation.STATE_TOOLBOX; - var mockEvent = { - keyCode: goog.events.KeyCodes.ESC - }; - chai.assert.isTrue(Blockly.Navigation.navigate(mockEvent)); + this.mockEvent.keyCode = goog.events.KeyCodes.E; + chai.assert.isTrue(Blockly.Navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.Navigation.currentState_, Blockly.Navigation.STATE_WS); });