From c4cad3c6e4d4e3fac0e68a7c49e5f820152794ed Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 17 Jun 2016 14:26:04 -0700 Subject: [PATCH 01/16] Cache screen CTM for performance improvement. --- core/scrollbar.js | 3 ++- core/utils.js | 5 ++--- core/workspace_svg.js | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/core/scrollbar.js b/core/scrollbar.js index ee080b0bc..6381b928f 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -606,7 +606,8 @@ Blockly.Scrollbar.prototype.onMouseDownBar_ = function(e) { e.stopPropagation(); return; } - var mouseXY = Blockly.mouseToSvg(e, this.workspace_.getParentSvg()); + var mouseXY = Blockly.mouseToSvg(e, this.workspace_.getParentSvg(), + this.workspace_.getInverseScreenCTM()); var mouseLocation = this.horizontal_ ? mouseXY.x : mouseXY.y; var handleXY = Blockly.getSvgXY_(this.svgHandle_, this.workspace_); diff --git a/core/utils.js b/core/utils.js index 9b9e04d60..19ccc4cc8 100644 --- a/core/utils.js +++ b/core/utils.js @@ -308,14 +308,13 @@ Blockly.isRightButton = function(e) { * The origin (0,0) is the top-left corner of the Blockly svg. * @param {!Event} e Mouse event. * @param {!Element} svg SVG element. + * @param {SVGMatrix} matrix Inverted screen CTM to use. * @return {!Object} Object with .x and .y properties. */ -Blockly.mouseToSvg = function(e, svg) { +Blockly.mouseToSvg = function(e, svg, matrix) { var svgPoint = svg.createSVGPoint(); svgPoint.x = e.clientX; svgPoint.y = e.clientY; - var matrix = svg.getScreenCTM(); - matrix = matrix.inverse(); return svgPoint.matrixTransform(matrix); }; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index de636a7a2..4816349d2 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -144,6 +144,28 @@ Blockly.WorkspaceSvg.prototype.scrollbar = null; */ Blockly.WorkspaceSvg.prototype.lastSound_ = null; +/** + * Inverted screen CTM, for use in mouseToSvg. + * @type {SVGMatrix} + * @private + */ +Blockly.WorkspaceSvg.prototype.inverseScreenCTM_ = null; + +/** + * Getter for the inverted screen CTM. + * @return {SVGMatrix} The matrix to use in mouseToSvg + */ +Blockly.WorkspaceSvg.prototype.getInverseScreenCTM = function() { + return this.inverseScreenCTM_; +}; + +/** + * Update the inverted screen CTM. + */ +Blockly.WorkspaceSvg.prototype.updateInverseScreenCTM = function() { + this.inverseScreenCTM_ = this.getParentSvg().getScreenCTM().inverse(); +}; + /** * Save resize handler data so we can delete it later in dispose. * @param {!Array.} handler Data that can be passed to unbindEvent_. @@ -330,6 +352,7 @@ Blockly.WorkspaceSvg.prototype.resizeContents = function() { // based on contents. this.scrollbar.resize(); } + this.updateInverseScreenCTM(); }; /** @@ -355,6 +378,7 @@ Blockly.WorkspaceSvg.prototype.resize = function() { if (this.scrollbar) { this.scrollbar.resize(); } + this.updateInverseScreenCTM(); }; /** @@ -661,7 +685,8 @@ Blockly.WorkspaceSvg.prototype.onMouseDown_ = function(e) { */ Blockly.WorkspaceSvg.prototype.startDrag = function(e, xy) { // Record the starting offset between the bubble's location and the mouse. - var point = Blockly.mouseToSvg(e, this.getParentSvg()); + var point = Blockly.mouseToSvg(e, this.getParentSvg(), + this.getInverseScreenCTM()); // Fix scale of mouse event. point.x /= this.scale; point.y /= this.scale; @@ -674,7 +699,8 @@ Blockly.WorkspaceSvg.prototype.startDrag = function(e, xy) { * @return {!goog.math.Coordinate} New location of object. */ Blockly.WorkspaceSvg.prototype.moveDrag = function(e) { - var point = Blockly.mouseToSvg(e, this.getParentSvg()); + var point = Blockly.mouseToSvg(e, this.getParentSvg(), + this.getInverseScreenCTM()); // Fix scale of mouse event. point.x /= this.scale; point.y /= this.scale; @@ -690,7 +716,8 @@ Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) { // TODO: Remove terminateDrag and compensate for coordinate skew during zoom. Blockly.terminateDrag_(); var delta = e.deltaY > 0 ? -1 : 1; - var position = Blockly.mouseToSvg(e, this.getParentSvg()); + var position = Blockly.mouseToSvg(e, this.getParentSvg(), + this.getInverseScreenCTM()); this.zoom(position.x, position.y, delta); e.preventDefault(); }; From 42f620daa2a1048c991594cae1885c81d5ba56ab Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 21 Jun 2016 15:56:00 -0700 Subject: [PATCH 02/16] Check if matrix is null in mouseToSvg --- core/utils.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/utils.js b/core/utils.js index 19ccc4cc8..89644fff9 100644 --- a/core/utils.js +++ b/core/utils.js @@ -315,6 +315,10 @@ Blockly.mouseToSvg = function(e, svg, matrix) { var svgPoint = svg.createSVGPoint(); svgPoint.x = e.clientX; svgPoint.y = e.clientY; + + if (!matrix) { + matrix = svg.getScreenCTM().inverse(); + } return svgPoint.matrixTransform(matrix); }; From fed0f2f19f14794994e1ec221d543d04d1e13172 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Wed, 22 Jun 2016 16:35:41 +0200 Subject: [PATCH 03/16] Localisation updates from https://translatewiki.net. --- msg/json/be-tarask.json | 1 + msg/json/pt-br.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/msg/json/be-tarask.json b/msg/json/be-tarask.json index b08f882e4..7791d1918 100644 --- a/msg/json/be-tarask.json +++ b/msg/json/be-tarask.json @@ -280,6 +280,7 @@ "LISTS_SORT_ORDER_DESCENDING": "па зьмяншэньні", "LISTS_SORT_TYPE_NUMERIC": "як лікі", "LISTS_SORT_TYPE_TEXT": "паводле альфабэту", + "LISTS_SORT_TYPE_IGNORECASE": "паводле альфабэту, ігнараваць рэгістар", "LISTS_SPLIT_LIST_FROM_TEXT": "стварыць сьпіс з тэксту", "LISTS_SPLIT_TEXT_FROM_LIST": "стварыць тэкст са сьпісу", "LISTS_SPLIT_WITH_DELIMITER": "з падзяляльнікам", diff --git a/msg/json/pt-br.json b/msg/json/pt-br.json index fb910c5ad..26b3c98c7 100644 --- a/msg/json/pt-br.json +++ b/msg/json/pt-br.json @@ -70,7 +70,7 @@ "CONTROLS_FOR_TOOLTIP": "Faz com que a variável '%1' assuma os valores do número inicial ao número final, contando de acordo com o intervalo especificado e executa os blocos especificados.", "CONTROLS_FOR_TITLE": "contar com %1 de %2 até %3 por %4", "CONTROLS_FOREACH_TITLE": "para cada item %1 na lista %2", - "CONTROLS_FOREACH_TOOLTIP": "Para cada item em uma lista, atribui o item à variável '%1' e então realiza algumas instruções.", + "CONTROLS_FOREACH_TOOLTIP": "Para cada item em uma lista, atribuir o item à variável '%1' e então realiza algumas instruções.", "CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK": "encerra o laço", "CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE": "continua com a próxima iteração do laço", "CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK": "Encerra o laço.", @@ -285,6 +285,7 @@ "LISTS_GET_SUBLIST_END_FROM_END": "até nº a partir do final", "LISTS_GET_SUBLIST_END_LAST": "até último", "LISTS_GET_SUBLIST_TOOLTIP": "Cria uma cópia da porção especificada de uma lista.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", "LISTS_SORT_TITLE": "ordenar %1 %2 %3", "LISTS_SORT_TOOLTIP": "Ordenar uma cópia de uma lista.", "LISTS_SORT_ORDER_ASCENDING": "ascendente", From 98617d8ddc15dd1fd6ee7a8c58d810a83e2b03fd Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 22 Jun 2016 12:56:56 -0700 Subject: [PATCH 04/16] Move tokenizeIntepolation into Blockly.utils namespace. --- core/block.js | 2 +- core/blockly.js | 3 ++- core/utils.js | 2 +- tests/jsunit/blockly_test.js | 14 +++++++------- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/block.js b/core/block.js index 36a703171..584a9fa56 100644 --- a/core/block.js +++ b/core/block.js @@ -1021,7 +1021,7 @@ Blockly.Block.prototype.jsonInit = function(json) { * @private */ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { - var tokens = Blockly.tokenizeInterpolation(message); + var tokens = Blockly.utils.tokenizeInterpolation(message); // Interpolate the arguments. Build a list of elements. var indexDup = []; var indexCount = 0; diff --git a/core/blockly.js b/core/blockly.js index 4fd5de0ca..c51191b08 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -549,7 +549,8 @@ Blockly.addChangeListener = function(func) { /** * Returns the main workspace. Returns the last used main workspace (based on - * focus). + * focus). Try not to use this function, particularly if there are multiple + * Blockly instances on a page. * @return {!Blockly.Workspace} The main workspace. */ Blockly.getMainWorkspace = function() { diff --git a/core/utils.js b/core/utils.js index 89644fff9..fe13572ac 100644 --- a/core/utils.js +++ b/core/utils.js @@ -423,7 +423,7 @@ Blockly.isNumber = function(str) { * @param {string} message Text containing interpolation tokens. * @return {!Array.} Array of strings and numbers. */ -Blockly.tokenizeInterpolation = function(message) { +Blockly.utils.tokenizeInterpolation = function(message) { var tokens = []; var chars = message.split(''); chars.push(''); // End marker. diff --git a/tests/jsunit/blockly_test.js b/tests/jsunit/blockly_test.js index 88f0cebaa..0278b8442 100644 --- a/tests/jsunit/blockly_test.js +++ b/tests/jsunit/blockly_test.js @@ -123,18 +123,18 @@ function test_commonWordSuffix() { } function test_tokenizeInterpolation() { - var tokens = Blockly.tokenizeInterpolation(''); + var tokens = Blockly.utils.tokenizeInterpolation(''); assertArrayEquals('Null interpolation', [], tokens); - tokens = Blockly.tokenizeInterpolation('Hello'); + tokens = Blockly.utils.tokenizeInterpolation('Hello'); assertArrayEquals('No interpolation', ['Hello'], tokens); - tokens = Blockly.tokenizeInterpolation('Hello%World'); + tokens = Blockly.utils.tokenizeInterpolation('Hello%World'); assertArrayEquals('Unescaped %.', ['Hello%World'], tokens); - tokens = Blockly.tokenizeInterpolation('Hello%%World'); + tokens = Blockly.utils.tokenizeInterpolation('Hello%%World'); assertArrayEquals('Escaped %.', ['Hello%World'], tokens); - tokens = Blockly.tokenizeInterpolation('Hello %1 World'); + tokens = Blockly.utils.tokenizeInterpolation('Hello %1 World'); assertArrayEquals('Interpolation.', ['Hello ', 1, ' World'], tokens); - tokens = Blockly.tokenizeInterpolation('%123Hello%456World%789'); + tokens = Blockly.utils.tokenizeInterpolation('%123Hello%456World%789'); assertArrayEquals('Interpolations.', [123, 'Hello', 456, 'World', 789], tokens); - tokens = Blockly.tokenizeInterpolation('%%%x%%0%00%01%'); + tokens = Blockly.utils.tokenizeInterpolation('%%%x%%0%00%01%'); assertArrayEquals('Torture interpolations.', ['%%x%0', 0, 1, '%'], tokens); } From 685288836f96f46ed85b7cce08518ec3ff409506 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 22 Jun 2016 12:58:02 -0700 Subject: [PATCH 05/16] Use simpler message interpolation in Code demo. --- demos/code/code.js | 23 +++++++---------------- demos/code/index.html | 30 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/demos/code/code.js b/demos/code/code.js index a55633754..2dfebbbfb 100644 --- a/demos/code/code.js +++ b/demos/code/code.js @@ -382,7 +382,12 @@ Code.init = function() { }; window.addEventListener('resize', onresize, false); - var toolbox = document.getElementById('toolbox'); + // Interpolate translated messages into toolbox. + var toolboxText = document.getElementById('toolbox').outerHTML; + toolboxText = toolboxText.replace(/{(\w+)}/g, + function(m, p1) {return MSG[p1]}); + var toolboxXml = Blockly.Xml.textToDom(toolboxText); + Code.workspace = Blockly.inject('content_blocks', {grid: {spacing: 25, @@ -391,7 +396,7 @@ Code.init = function() { snap: true}, media: '../../media/', rtl: rtl, - toolbox: toolbox, + toolbox: toolboxXml, zoom: {controls: true, wheel: true} @@ -481,20 +486,6 @@ Code.initLanguage = function() { document.getElementById('linkButton').title = MSG['linkTooltip']; document.getElementById('runButton').title = MSG['runTooltip']; document.getElementById('trashButton').title = MSG['trashTooltip']; - - var categories = ['catLogic', 'catLoops', 'catMath', 'catText', 'catLists', - 'catColour', 'catVariables', 'catFunctions']; - for (var i = 0, cat; cat = categories[i]; i++) { - document.getElementById(cat).setAttribute('name', MSG[cat]); - } - var textVars = document.getElementsByClassName('textVar'); - for (var i = 0, textVar; textVar = textVars[i]; i++) { - textVar.textContent = MSG['textVariable']; - } - var listVars = document.getElementsByClassName('listVar'); - for (var i = 0, listVar; listVar = listVars[i]; i++) { - listVar.textContent = MSG['listVariable']; - } }; /** diff --git a/demos/code/index.html b/demos/code/index.html index 4e12249dd..54a59acda 100644 --- a/demos/code/index.html +++ b/demos/code/index.html @@ -74,7 +74,7 @@ From 91b10cae2fccc341640b5bcc7bdb09dbb9979483 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 22 Jun 2016 13:00:29 -0700 Subject: [PATCH 06/16] Create console stub for IE 9. --- core/blockly.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/blockly.js b/core/blockly.js index c51191b08..cda45992e 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -557,6 +557,14 @@ Blockly.getMainWorkspace = function() { return Blockly.mainWorkspace; }; +// IE9 does not have a console. Create a stub to stop errors. +if (!goog.global['console']) { + goog.global['console'] = { + 'log': function() {}, + 'warn': function() {} + }; +} + // Export symbols that would otherwise be renamed by Closure compiler. if (!goog.global['Blockly']) { goog.global['Blockly'] = {}; From ac0e704c699133e57e8a57bd49a971ce01014535 Mon Sep 17 00:00:00 2001 From: Markus Bordihn Date: Wed, 22 Jun 2016 22:10:25 +0200 Subject: [PATCH 07/16] Fixed Parse error. Non-JSDoc comment has annotations. (#451) --- core/field_date.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/field_date.js b/core/field_date.js index e2a203a2f..55e93fb8f 100644 --- a/core/field_date.js +++ b/core/field_date.js @@ -193,14 +193,14 @@ Blockly.FieldDate.loadLanguage_ = function() { */ Blockly.FieldDate.CSS = [ /* Copied from: goog/css/datepicker.css */ - /* + /** * Copyright 2009 The Closure Library Authors. All Rights Reserved. * * Use of this source code is governed by the Apache License, Version 2.0. * See the COPYING file for details. */ - /* + /** * Standard styling for a goog.ui.DatePicker. * * @author arv@google.com (Erik Arvidsson) From 425513b7297da4f194a8595d1f0a4169afb79769 Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Wed, 22 Jun 2016 13:11:19 -0700 Subject: [PATCH 08/16] Don't output blockId if not set (e.g., toolbox category event). (#443) --- core/events.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/events.js b/core/events.js index f1971478d..f78222d03 100644 --- a/core/events.js +++ b/core/events.js @@ -303,8 +303,10 @@ Blockly.Events.Abstract = function(block) { Blockly.Events.Abstract.prototype.toJson = function() { var json = { 'type': this.type, - 'blockId': this.blockId }; + if (this.blockId) { + json['blockId'] = this.blockId; + } if (this.group) { json['group'] = this.group; } From 0be0cc89c7f686ed6377248f7e1caa76578669c3 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 22 Jun 2016 15:07:07 -0700 Subject: [PATCH 09/16] Second version of FieldNumber API. --- core/block.js | 6 ++-- core/field_number.js | 67 ++++++++++-------------------------- demos/blockfactory/blocks.js | 4 +-- 3 files changed, 22 insertions(+), 55 deletions(-) diff --git a/core/block.js b/core/block.js index 584a9fa56..92b533be8 100644 --- a/core/block.js +++ b/core/block.js @@ -1111,10 +1111,8 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { element['width'], element['height'], element['alt']); break; case 'field_number': - field = new Blockly.FieldNumber(element['value']); - field.setPrecision(element['precision']); - field.setMin(element['min']); - field.setMax(element['max']); + field = new Blockly.FieldNumber(element['value'], + element['min'], element['max'], element['precision']); break; case 'field_date': if (Blockly.FieldDate) { diff --git a/core/field_number.js b/core/field_number.js index 0ce38bf1e..000022998 100644 --- a/core/field_number.js +++ b/core/field_number.js @@ -32,6 +32,9 @@ goog.require('goog.math'); /** * Class for an editable number field. * @param {string} value The initial content of the field. + * @param {number|string|undefined} opt_min Minimum value. + * @param {number|string|undefined} opt_max Maximum value. + * @param {number|string|undefined} opt_precision Precision for value. * @param {Function=} opt_validator An optional function that is called * to validate any constraints on what the user entered. Takes the new * text as an argument and returns either the accepted text, a replacement @@ -39,66 +42,32 @@ goog.require('goog.math'); * @extends {Blockly.FieldTextInput} * @constructor */ -Blockly.FieldNumber = function(value, opt_validator) { +Blockly.FieldNumber = + function(value, opt_min, opt_max, opt_precision, opt_validator) { Blockly.FieldNumber.superClass_.constructor.call(this, value, opt_validator); + this.setConstraints(opt_min, opt_max, opt_precision); }; goog.inherits(Blockly.FieldNumber, Blockly.FieldTextInput); /** - * Steps between allowed numbers. - * @private - * @type {number} - */ -Blockly.FieldNumber.prototype.precision_ = 0; - -/** - * Minimum allowed value. - * @private - * @type {number} - */ -Blockly.FieldNumber.prototype.min_ = -Infinity; - -/** - * Maximum allowed value. - * @private - * @type {number} - */ -Blockly.FieldNumber.prototype.max_ = Infinity; - -/** + * Set the maximum, minimum and precision constraints on this field. + * Any of these properties may be undefiend or NaN to be disabled. * Setting precision (usually a power of 10) enforces a minimum step between * values. That is, the user's value will rounded to the closest multiple of * precision. The least significant digit place is inferred from the precision. * Integers values can be enforces by choosing an integer precision. + * @param {number|string|undefined} min Minimum value. + * @param {number|string|undefined} max Maximum value. * @param {number|string|undefined} precision Precision for value. */ -Blockly.FieldNumber.prototype.setPrecision = function(precision) { +Blockly.FieldNumber.prototype.setConstraints = function(min, max, precision) { precision = parseFloat(precision); - if (!isNaN(precision)) { - this.precision_ = precision; - } -}; - -/** - * Set a maximum limit on this field's value. - * @param {number|string|undefined} max Maximum value. - */ -Blockly.FieldNumber.prototype.setMin = function(min) { + this.precision_ = isNaN(precision) ? 0 : precision; min = parseFloat(min); - if (!isNaN(min)) { - this.min_ = min; - } -}; - -/** - * Set a maximum limit on this field's value. - * @param {number|string|undefined} max Minimum value. - */ -Blockly.FieldNumber.prototype.setMax = function(max) { + this.min_ = isNaN(min) ? -Infinity : min; max = parseFloat(max); - if (!isNaN(max)) { - this.max_ = max; - } + this.max_ = isNaN(max) ? Infinity : max; + this.setValue(this.numberValidator(this.getValue)); }; /** @@ -117,7 +86,7 @@ Blockly.FieldNumber.prototype.setValidator = function(handler) { if (v1 === undefined) { v1 = value; } - var v2 = Blockly.FieldNumber.numberValidator.call(this, v1); + var v2 = this.numberValidator(v1); if (v2 === undefined) { v2 = v1; } @@ -125,7 +94,7 @@ Blockly.FieldNumber.prototype.setValidator = function(handler) { return v2 === value ? undefined : v2; }; } else { - wrappedHandler = Blockly.FieldNumber.numberValidator; + wrappedHandler = this.numberValidator; } Blockly.FieldNumber.superClass_.setValidator.call(this, wrappedHandler); }; @@ -135,7 +104,7 @@ Blockly.FieldNumber.prototype.setValidator = function(handler) { * @param {string} text The user's text. * @return {?string} A string representing a valid number, or null if invalid. */ -Blockly.FieldNumber.numberValidator = function(text) { +Blockly.FieldNumber.prototype.numberValidator = function(text) { if (text === null) { return null; } diff --git a/demos/blockfactory/blocks.js b/demos/blockfactory/blocks.js index f2d8dd290..5008e6a20 100644 --- a/demos/blockfactory/blocks.js +++ b/demos/blockfactory/blocks.js @@ -470,9 +470,9 @@ Blockly.Blocks['field_image'] = { .appendField(new Blockly.FieldTextInput(src), 'SRC'); this.appendDummyInput() .appendField('width') - .appendField(new Blockly.FieldNumber('15'), 'WIDTH') + .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH') .appendField('height') - .appendField(new Blockly.FieldNumber('15'), 'HEIGHT') + .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT') .appendField('alt text') .appendField(new Blockly.FieldTextInput('*'), 'ALT'); this.setPreviousStatement(true, 'Field'); From 16fef9f2e2a09a36df72e01333a1258bee13e44f Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Mon, 27 Jun 2016 15:52:35 -0700 Subject: [PATCH 10/16] Reduce more unneeded parentheses in JS and Python. --- generators/javascript.js | 32 ++++++++++++++++++++------------ generators/python.js | 25 +++++++++++++++++++++---- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/generators/javascript.js b/generators/javascript.js index 1254c88e8..b9e44929c 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -71,23 +71,23 @@ Blockly.JavaScript.addReservedWords( * https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence */ Blockly.JavaScript.ORDER_ATOMIC = 0; // 0 "" ... -Blockly.JavaScript.ORDER_MEMBER = 1.1; // . [] -Blockly.JavaScript.ORDER_NEW = 1.2; // new +Blockly.JavaScript.ORDER_NEW = 1.1; // new +Blockly.JavaScript.ORDER_MEMBER = 1.2; // . [] Blockly.JavaScript.ORDER_FUNCTION_CALL = 2; // () Blockly.JavaScript.ORDER_INCREMENT = 3; // ++ Blockly.JavaScript.ORDER_DECREMENT = 3; // -- -Blockly.JavaScript.ORDER_LOGICAL_NOT = 4.1; // ! -Blockly.JavaScript.ORDER_BITWISE_NOT = 4.2; // ~ -Blockly.JavaScript.ORDER_UNARY_PLUS = 4.3; // + -Blockly.JavaScript.ORDER_UNARY_NEGATION = 4.4; // - +Blockly.JavaScript.ORDER_BITWISE_NOT = 4.1; // ~ +Blockly.JavaScript.ORDER_UNARY_PLUS = 4.2; // + +Blockly.JavaScript.ORDER_UNARY_NEGATION = 4.3; // - +Blockly.JavaScript.ORDER_LOGICAL_NOT = 4.4; // ! Blockly.JavaScript.ORDER_TYPEOF = 4.5; // typeof Blockly.JavaScript.ORDER_VOID = 4.6; // void Blockly.JavaScript.ORDER_DELETE = 4.7; // delete -Blockly.JavaScript.ORDER_MULTIPLICATION = 5.1; // * -Blockly.JavaScript.ORDER_DIVISION = 5.2; // / +Blockly.JavaScript.ORDER_DIVISION = 5.1; // / +Blockly.JavaScript.ORDER_MULTIPLICATION = 5.2; // * Blockly.JavaScript.ORDER_MODULUS = 5.3; // % -Blockly.JavaScript.ORDER_ADDITION = 6.1; // + -Blockly.JavaScript.ORDER_SUBTRACTION = 6.2; // - +Blockly.JavaScript.ORDER_SUBTRACTION = 6.1; // - +Blockly.JavaScript.ORDER_ADDITION = 6.2; // + Blockly.JavaScript.ORDER_BITWISE_SHIFT = 7; // << >> >>> Blockly.JavaScript.ORDER_RELATIONAL = 8; // < <= > >= Blockly.JavaScript.ORDER_IN = 8; // in @@ -108,12 +108,20 @@ Blockly.JavaScript.ORDER_NONE = 99; // (...) * @type {!Array.>} */ Blockly.JavaScript.ORDER_OVERRIDES = [ - // (foo()).bar() -> foo().bar() + // (foo()).bar -> foo().bar // (foo())[0] -> foo()[0] [Blockly.JavaScript.ORDER_FUNCTION_CALL, Blockly.JavaScript.ORDER_MEMBER], - // (foo[0])[1] -> foo[0][1] + // (foo())() -> foo()() + [Blockly.JavaScript.ORDER_FUNCTION_CALL, Blockly.JavaScript.ORDER_FUNCTION_CALL], // (foo.bar).baz -> foo.bar.baz + // (foo.bar)[0] -> foo.bar[0] + // (foo[0]).bar -> foo[0].bar + // (foo[0])[1] -> foo[0][1] [Blockly.JavaScript.ORDER_MEMBER, Blockly.JavaScript.ORDER_MEMBER], + // (foo.bar)() -> foo.bar() + // (foo[0])() -> foo[0]() + [Blockly.JavaScript.ORDER_MEMBER, Blockly.JavaScript.ORDER_FUNCTION_CALL] + // !(!foo) -> !!foo [Blockly.JavaScript.ORDER_LOGICAL_NOT, Blockly.JavaScript.ORDER_LOGICAL_NOT], // a * (b * c) -> a * b * c diff --git a/generators/python.js b/generators/python.js index 18fb26a3e..020e30107 100644 --- a/generators/python.js +++ b/generators/python.js @@ -59,8 +59,8 @@ Blockly.Python.addReservedWords( Blockly.Python.ORDER_ATOMIC = 0; // 0 "" ... Blockly.Python.ORDER_COLLECTION = 1; // tuples, lists, dictionaries Blockly.Python.ORDER_STRING_CONVERSION = 1; // `expression...` -Blockly.Python.ORDER_MEMBER = 2; // . [] -Blockly.Python.ORDER_FUNCTION_CALL = 2; // () +Blockly.Python.ORDER_MEMBER = 2.1; // . [] +Blockly.Python.ORDER_FUNCTION_CALL = 2.2; // () Blockly.Python.ORDER_EXPONENTIATION = 3; // ** Blockly.Python.ORDER_UNARY_SIGN = 4; // + - Blockly.Python.ORDER_BITWISE_NOT = 4; // ~ @@ -84,9 +84,26 @@ Blockly.Python.ORDER_NONE = 99; // (...) * @type {!Array.>} */ Blockly.Python.ORDER_OVERRIDES = [ - // (foo()).bar() -> foo().bar() + // (foo()).bar -> foo().bar // (foo())[0] -> foo()[0] - [Blockly.Python.ORDER_FUNCTION_CALL, Blockly.Python.ORDER_MEMBER] + [Blockly.Python.ORDER_FUNCTION_CALL, Blockly.Python.ORDER_MEMBER], + // (foo())() -> foo()() + [Blockly.Python.ORDER_FUNCTION_CALL, Blockly.Python.ORDER_FUNCTION_CALL], + // (foo.bar).baz -> foo.bar.baz + // (foo.bar)[0] -> foo.bar[0] + // (foo[0]).bar -> foo[0].bar + // (foo[0])[1] -> foo[0][1] + [Blockly.Python.ORDER_MEMBER, Blockly.Python.ORDER_MEMBER], + // (foo.bar)() -> foo.bar() + // (foo[0])() -> foo[0]() + [Blockly.Python.ORDER_MEMBER, Blockly.Python.ORDER_FUNCTION_CALL] + + // not (not foo) -> not not foo + [Blockly.Python.ORDER_LOGICAL_NOT, Blockly.Python.ORDER_LOGICAL_NOT], + // a and (b and c) -> a and b and c + [Blockly.Python.ORDER_LOGICAL_AND, Blockly.Python.ORDER_LOGICAL_AND], + // a or (b or c) -> a or b or c + [Blockly.Python.ORDER_LOGICAL_OR, Blockly.Python.ORDER_LOGICAL_OR] ]; /** From aca074891d2c6f9009f9176f7ede96e4bf457b0d Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Mon, 27 Jun 2016 17:27:08 -0700 Subject: [PATCH 11/16] Fix some problems with flyout rendering (#461) --- core/flyout.js | 68 +++++++++++++++++------------------------------ core/scrollbar.js | 2 +- 2 files changed, 25 insertions(+), 45 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index 85aaee983..e8aa2f872 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -286,8 +286,8 @@ Blockly.Flyout.prototype.getMetrics_ = function() { contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale, viewTop: -this.workspace_.scrollY, viewLeft: -this.workspace_.scrollX, - contentTop: 0, // TODO: #349 - contentLeft: 0, // TODO: #349 + contentTop: optionBox.y, + contentLeft: optionBox.x, absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; @@ -426,9 +426,8 @@ Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) { * rounded corners. * @private */ -Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = - function(width, height) { - /* eslint-disable indent */ +Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width, + height) { var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP; // Start at top left. var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)]; @@ -461,7 +460,7 @@ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = path.push('z'); } this.svgBackground_.setAttribute('d', path.join(' ')); -}; /* eslint-enable indent */ +}; /** * Scroll the flyout to the top. @@ -588,7 +587,6 @@ Blockly.Flyout.prototype.show = function(xmlList) { } this.reflow(); - this.offsetHorizontalRtlBlocks(this.workspace_.getTopBlocks(false)); this.filterForCapacity_(); // Correctly position the flyout's scrollbar when it opens. @@ -605,9 +603,12 @@ Blockly.Flyout.prototype.show = function(xmlList) { * @private */ Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps) { - var margin = this.MARGIN * this.workspace_.scale; + var margin = this.MARGIN; var cursorX = this.RTL ? margin : margin + Blockly.BlockSvg.TAB_WIDTH; var cursorY = margin; + if (this.horizontalLayout_ && this.RTL) { + blocks = blocks.reverse(); + } for (var i = 0, block; block = blocks[i]; i++) { var allBlocks = block.getDescendants(); for (var j = 0, child; child = allBlocks[j]; j++) { @@ -623,8 +624,13 @@ Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps) { if (this.horizontalLayout_) { cursorX += tab; } - block.moveBy((this.horizontalLayout_ && this.RTL) ? -cursorX : cursorX, - cursorY); + + if (this.horizontalLayout_ && this.RTL) { + block.moveBy(cursorX + blockHW.width - tab, cursorY); + } else { + block.moveBy(cursorX, cursorY); + } + if (this.horizontalLayout_) { cursorX += (blockHW.width + gaps[i] - tab); } else { @@ -1074,10 +1080,9 @@ Blockly.Flyout.prototype.reflowVertical = function(blocks) { if (this.RTL) { // With the flyoutWidth known, right-align the blocks. var oldX = block.getRelativeToSurfaceXY().x; - var dx = flyoutWidth - this.MARGIN; - dx /= this.workspace_.scale; - dx -= Blockly.BlockSvg.TAB_WIDTH; - block.moveBy(dx - oldX, 0); + var newX = flyoutWidth / this.workspace_.scale - this.MARGIN; + newX -= Blockly.BlockSvg.TAB_WIDTH; + block.moveBy(newX - oldX, 0); } if (block.flyoutRect_) { block.flyoutRect_.setAttribute('width', blockHW.width); @@ -1109,41 +1114,16 @@ Blockly.Flyout.prototype.reflowVertical = function(blocks) { * Reflow blocks and their buttons. */ Blockly.Flyout.prototype.reflow = function() { + if (this.reflowWrapper_) { + this.workspace_.removeChangeListener(this.reflowWrapper_); + } var blocks = this.workspace_.getTopBlocks(false); if (this.horizontalLayout_) { this.reflowHorizontal(blocks); } else { this.reflowVertical(blocks); } -}; - -/** - * In the horizontal RTL case all of the blocks will be laid out to the left of - * the origin, but we won't know how big the workspace is until the layout pass - * is done. - * Now that it's done, shunt all the blocks to be right of the origin. - * @param {!Array} blocks The blocks to reposition. - */ -Blockly.Flyout.prototype.offsetHorizontalRtlBlocks = function(blocks) { - if (this.horizontalLayout_ && this.RTL) { - // We don't know this workspace's view width yet. - this.position(); - try { - var optionBox = this.workspace_.getCanvas().getBBox(); - } catch (e) { - // Firefox has trouble with hidden elements (Bug 528969). - optionBox = {height: 0, y: 0, width: 0, x: 0}; - } - - var offset = Math.max(-optionBox.x + this.MARGIN, - this.width_ / this.workspace_.scale); - - for (var i = 0, block; block = blocks[i]; i++) { - block.moveBy(offset, 0); - if (block.flyoutRect_) { - block.flyoutRect_.setAttribute('x', - offset + Number(block.flyoutRect_.getAttribute('x'))); - } - } + if (this.reflowWrapper_) { + this.workspace_.addChangeListener(this.reflowWrapper_); } }; diff --git a/core/scrollbar.js b/core/scrollbar.js index 6381b928f..da2dd942d 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -694,7 +694,7 @@ Blockly.Scrollbar.prototype.onMouseUpHandle_ = function() { * @private */ Blockly.Scrollbar.prototype.constrainHandle_ = function(value) { - if (value <= 0 || isNaN(value)) { + if (value <= 0 || isNaN(value) || this.scrollViewSize_ < this.handleLength_) { value = 0; } else { value = Math.min(value, this.scrollViewSize_ - this.handleLength_); From 62a644686c3144b2a620bd18ed49b613b2f84dc4 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Mon, 27 Jun 2016 17:40:30 -0700 Subject: [PATCH 12/16] Routine recompile. --- blockly_compressed.js | 58 +++++++++++++++++++++------------------- blockly_uncompressed.js | 7 ++--- generators/javascript.js | 2 +- generators/python.js | 2 +- javascript_compressed.js | 8 +++--- msg/js/be-tarask.js | 2 +- msg/js/pt-br.js | 4 +-- python_compressed.js | 5 ++-- 8 files changed, 46 insertions(+), 42 deletions(-) diff --git a/blockly_compressed.js b/blockly_compressed.js index 8fa47ec9c..c8154fd65 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -972,11 +972,12 @@ Blockly.Scrollbar.prototype.resizeViewVertical=function(a){var b=a.viewHeight-1; Blockly.Scrollbar.prototype.resizeContentVertical=function(a){this.pair_||this.setVisible(this.scrollViewSize_=c+this.handleLength_&&(d+=e);this.setHandlePosition(this.constrainHandle_(d));this.onScroll_();a.stopPropagation();a.preventDefault()}}; -Blockly.Scrollbar.prototype.onMouseDownHandle_=function(a){this.onMouseUpHandle_();Blockly.isRightButton(a)?a.stopPropagation():(this.startDragHandle=this.handlePosition_,this.startDragMouse=this.horizontal_?a.clientX:a.clientY,Blockly.Scrollbar.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,this.onMouseUpHandle_),Blockly.Scrollbar.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.onMouseMoveHandle_),a.stopPropagation(),a.preventDefault())}; +Blockly.Scrollbar.prototype.onMouseDownBar_=function(a){this.onMouseUpHandle_();if(Blockly.isRightButton(a))a.stopPropagation();else{var b=Blockly.mouseToSvg(a,this.workspace_.getParentSvg(),this.workspace_.getInverseScreenCTM()),b=this.horizontal_?b.x:b.y,c=Blockly.getSvgXY_(this.svgHandle_,this.workspace_),c=this.horizontal_?c.x:c.y,d=this.handlePosition_,e=.95*this.handleLength_;b<=c?d-=e:b>=c+this.handleLength_&&(d+=e);this.setHandlePosition(this.constrainHandle_(d));this.onScroll_();a.stopPropagation(); +a.preventDefault()}};Blockly.Scrollbar.prototype.onMouseDownHandle_=function(a){this.onMouseUpHandle_();Blockly.isRightButton(a)?a.stopPropagation():(this.startDragHandle=this.handlePosition_,this.startDragMouse=this.horizontal_?a.clientX:a.clientY,Blockly.Scrollbar.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,this.onMouseUpHandle_),Blockly.Scrollbar.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.onMouseMoveHandle_),a.stopPropagation(),a.preventDefault())}; Blockly.Scrollbar.prototype.onMouseMoveHandle_=function(a){this.setHandlePosition(this.constrainHandle_(this.startDragHandle+((this.horizontal_?a.clientX:a.clientY)-this.startDragMouse)));this.onScroll_()}; -Blockly.Scrollbar.prototype.onMouseUpHandle_=function(){Blockly.hideChaff(!0);Blockly.Scrollbar.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_),Blockly.Scrollbar.onMouseUpWrapper_=null);Blockly.Scrollbar.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseMoveWrapper_),Blockly.Scrollbar.onMouseMoveWrapper_=null)};Blockly.Scrollbar.prototype.constrainHandle_=function(a){return a=0>=a||isNaN(a)?0:Math.min(a,this.scrollViewSize_-this.handleLength_)}; -Blockly.Scrollbar.prototype.onScroll_=function(){var a=this.handlePosition_/this.scrollViewSize_;isNaN(a)&&(a=0);var b={};this.horizontal_?b.x=a:b.y=a;this.workspace_.setMetrics(b)};Blockly.Scrollbar.prototype.set=function(a){this.setHandlePosition(this.constrainHandle_(a*this.ratio_));this.onScroll_()};Blockly.Scrollbar.insertAfter_=function(a,b){var c=b.nextSibling,d=b.parentNode;if(!d)throw"Reference node has no parent.";c?d.insertBefore(a,c):d.appendChild(a)};Blockly.Trashcan=function(a){this.workspace_=a};Blockly.Trashcan.prototype.WIDTH_=47;Blockly.Trashcan.prototype.BODY_HEIGHT_=44;Blockly.Trashcan.prototype.LID_HEIGHT_=16;Blockly.Trashcan.prototype.MARGIN_BOTTOM_=20;Blockly.Trashcan.prototype.MARGIN_SIDE_=20;Blockly.Trashcan.prototype.MARGIN_HOTSPOT_=10;Blockly.Trashcan.prototype.SPRITE_LEFT_=0;Blockly.Trashcan.prototype.SPRITE_TOP_=32;Blockly.Trashcan.prototype.isOpen=!1;Blockly.Trashcan.prototype.svgGroup_=null; +Blockly.Scrollbar.prototype.onMouseUpHandle_=function(){Blockly.hideChaff(!0);Blockly.Scrollbar.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_),Blockly.Scrollbar.onMouseUpWrapper_=null);Blockly.Scrollbar.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseMoveWrapper_),Blockly.Scrollbar.onMouseMoveWrapper_=null)}; +Blockly.Scrollbar.prototype.constrainHandle_=function(a){return a=0>=a||isNaN(a)||this.scrollViewSize_b.bottomRight.x&&(b.bottomRight.x=d.bottomRight.x);d.topLeft.yb.bottomRight.y&&(b.bottomRight.y=d.bottomRight.y)}return{x:b.topLeft.x,y:b.topLeft.y,width:b.bottomRight.x- b.topLeft.x,height:b.bottomRight.y-b.topLeft.y}};Blockly.WorkspaceSvg.prototype.cleanUp_=function(){Blockly.Events.setGroup(!0);for(var a=this.getTopBlocks(!0),b=0,c=0,d;d=a[c];c++){var e=d.getRelativeToSurfaceXY();d.moveBy(-e.x,b-e.y);d.snapToGrid();b=d.getRelativeToSurfaceXY().y+d.getHeightWidth().height+Blockly.BlockSvg.MIN_BLOCK_Y}Blockly.Events.setGroup(!1);Blockly.resizeSvgContents(this)}; Blockly.WorkspaceSvg.prototype.showContextMenu_=function(a){function b(a){if(a.isDeletable())l=l.concat(a.getDescendants());else{a=a.getChildren();for(var c=0;c"+a.xml+"").firstChild;this.ids=a.ids}; Blockly.Events.Create.prototype.run=function(a){var b=Blockly.Workspace.getById(this.workspaceId);if(a)a=goog.dom.createDom("xml"),a.appendChild(this.xml),Blockly.Xml.domToWorkspace(a,b);else{a=0;for(var c;c=this.ids[a];a++){var d=b.getBlockById(c);d?d.dispose(!1,!1):c==this.blockId&&console.warn("Can't uncreate non-existant block: "+c)}}}; @@ -1271,9 +1273,9 @@ Blockly.FieldDropdown.prototype.setText=function(a){this.sourceBlock_&&this.arro Blockly.FieldDropdown.prototype.dispose=function(){Blockly.WidgetDiv.hideIfOwner(this);Blockly.FieldDropdown.superClass_.dispose.call(this)};Blockly.FieldImage=function(a,b,c,d){this.sourceBlock_=null;this.height_=Number(c);this.width_=Number(b);this.size_=new goog.math.Size(this.width_,this.height_+2*Blockly.BlockSvg.INLINE_PADDING_Y);this.text_=d||"";this.setValue(a)};goog.inherits(Blockly.FieldImage,Blockly.Field);Blockly.FieldImage.prototype.rectElement_=null;Blockly.FieldImage.prototype.EDITABLE=!1; Blockly.FieldImage.prototype.init=function(){if(!this.fieldGroup_){this.fieldGroup_=Blockly.createSvgElement("g",{},null);this.visible_||(this.fieldGroup_.style.display="none");this.imageElement_=Blockly.createSvgElement("image",{height:this.height_+"px",width:this.width_+"px"},this.fieldGroup_);this.setValue(this.src_);goog.userAgent.GECKO&&(this.rectElement_=Blockly.createSvgElement("rect",{height:this.height_+"px",width:this.width_+"px","fill-opacity":0},this.fieldGroup_));this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); var a=this.rectElement_||this.imageElement_;a.tooltip=this.sourceBlock_;Blockly.Tooltip.bindMouseEvents(a)}};Blockly.FieldImage.prototype.dispose=function(){goog.dom.removeNode(this.fieldGroup_);this.rectElement_=this.imageElement_=this.fieldGroup_=null};Blockly.FieldImage.prototype.setTooltip=function(a){(this.rectElement_||this.imageElement_).tooltip=a};Blockly.FieldImage.prototype.getValue=function(){return this.src_}; -Blockly.FieldImage.prototype.setValue=function(a){null!==a&&(this.src_=a,this.imageElement_&&this.imageElement_.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",goog.isString(a)?a:""))};Blockly.FieldImage.prototype.setText=function(a){null!==a&&(this.text_=a)};Blockly.FieldImage.prototype.render_=function(){};Blockly.FieldNumber=function(a,b){Blockly.FieldNumber.superClass_.constructor.call(this,a,b)};goog.inherits(Blockly.FieldNumber,Blockly.FieldTextInput);Blockly.FieldNumber.prototype.precision_=0;Blockly.FieldNumber.prototype.min_=-Infinity;Blockly.FieldNumber.prototype.max_=Infinity;Blockly.FieldNumber.prototype.setPrecision=function(a){a=parseFloat(a);isNaN(a)||(this.precision_=a)};Blockly.FieldNumber.prototype.setMin=function(a){a=parseFloat(a);isNaN(a)||(this.min_=a)}; -Blockly.FieldNumber.prototype.setMax=function(a){a=parseFloat(a);isNaN(a)||(this.max_=a)};Blockly.FieldNumber.prototype.setValidator=function(a){Blockly.FieldNumber.superClass_.setValidator.call(this,a?function(b){var c=a.call(this,b);if(null===c)var d=c;else void 0===c&&(c=b),d=Blockly.FieldNumber.numberValidator.call(this,c),void 0===d&&(d=c);return d===b?void 0:d}:Blockly.FieldNumber.numberValidator)}; -Blockly.FieldNumber.numberValidator=function(a){if(null===a)return null;a=String(a);a=a.replace(/O/ig,"0");a=a.replace(/,/g,"");a=parseFloat(a||0);if(isNaN(a))return null;this.precision_&&Number.isFinite(a)&&(a=Math.round(a/this.precision_)*this.precision_);a=goog.math.clamp(a,this.min_,this.max_);return String(a)};Blockly.Variables={};Blockly.Variables.NAME_TYPE="VARIABLE";Blockly.Variables.allVariables=function(a){var b;if(a.getDescendants)b=a.getDescendants();else if(a.getAllBlocks)b=a.getAllBlocks();else throw"Not Block or Workspace: "+a;a=Object.create(null);for(var c=0;c=g?(c=2,e=g,(g=d.join(""))&&b.push(g),d.length=0):(d.push("%",g),c=0):2==c&&("0"<=g&&"9">=g?e+=g:(b.push(parseInt(e,10)),f--,c=0))}(g=d.join(""))&&b.push(g);return b};Blockly.genUid=function(){for(var a=Blockly.genUid.soup_.length,b=[],c=0;20>c;c++)b[c]=Blockly.genUid.soup_.charAt(Math.random()*a);return b.join("")}; -Blockly.genUid.soup_="!#%()*+,-./:;=?@[]^_`{|}~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Blockly.utils.wrap=function(a,b){for(var c=a.split("\n"),d=0;d=g?(c=2,e=g,(g=d.join(""))&&b.push(g),d.length=0):(d.push("%",g),c=0):2==c&&("0"<=g&&"9">=g?e+=g:(b.push(parseInt(e,10)),f--,c=0))}(g=d.join(""))&&b.push(g);return b}; +Blockly.genUid=function(){for(var a=Blockly.genUid.soup_.length,b=[],c=0;20>c;c++)b[c]=Blockly.genUid.soup_.charAt(Math.random()*a);return b.join("")};Blockly.genUid.soup_="!#%()*+,-./:;=?@[]^_`{|}~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Blockly.utils.wrap=function(a,b){for(var c=a.split("\n"),d=0;db&&(b=c[d].length);var e,d=-Infinity,f,g=1;do{e=d;f=a;for(var h=[],k=c.length/g,m=1,d=0;de);return f}; Blockly.utils.wrapScore_=function(a,b,c){for(var d=[0],e=[],f=0;fd&&(d=h,e=g)}return e?Blockly.utils.wrapMutate_(a,e,c):b};Blockly.utils.wrapToText_=function(a,b){for(var c=[],d=0;d foo.bar() // (foo[0])() -> foo[0]() - [Blockly.JavaScript.ORDER_MEMBER, Blockly.JavaScript.ORDER_FUNCTION_CALL] + [Blockly.JavaScript.ORDER_MEMBER, Blockly.JavaScript.ORDER_FUNCTION_CALL], // !(!foo) -> !!foo [Blockly.JavaScript.ORDER_LOGICAL_NOT, Blockly.JavaScript.ORDER_LOGICAL_NOT], diff --git a/generators/python.js b/generators/python.js index 020e30107..2e77c0753 100644 --- a/generators/python.js +++ b/generators/python.js @@ -96,7 +96,7 @@ Blockly.Python.ORDER_OVERRIDES = [ [Blockly.Python.ORDER_MEMBER, Blockly.Python.ORDER_MEMBER], // (foo.bar)() -> foo.bar() // (foo[0])() -> foo[0]() - [Blockly.Python.ORDER_MEMBER, Blockly.Python.ORDER_FUNCTION_CALL] + [Blockly.Python.ORDER_MEMBER, Blockly.Python.ORDER_FUNCTION_CALL], // not (not foo) -> not not foo [Blockly.Python.ORDER_LOGICAL_NOT, Blockly.Python.ORDER_LOGICAL_NOT], diff --git a/javascript_compressed.js b/javascript_compressed.js index 3ff439a51..ad00ad4be 100644 --- a/javascript_compressed.js +++ b/javascript_compressed.js @@ -4,11 +4,11 @@ // Copyright 2012 Google Inc. Apache License 2.0 Blockly.JavaScript=new Blockly.Generator("JavaScript");Blockly.JavaScript.addReservedWords("Blockly,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,enum,export,extends,import,super,implements,interface,let,package,private,protected,public,static,yield,const,null,true,false,Array,ArrayBuffer,Boolean,Date,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Error,eval,EvalError,Float32Array,Float64Array,Function,Infinity,Int16Array,Int32Array,Int8Array,isFinite,isNaN,Iterator,JSON,Math,NaN,Number,Object,parseFloat,parseInt,RangeError,ReferenceError,RegExp,StopIteration,String,SyntaxError,TypeError,Uint16Array,Uint32Array,Uint8Array,Uint8ClampedArray,undefined,uneval,URIError,applicationCache,closed,Components,content,_content,controllers,crypto,defaultStatus,dialogArguments,directories,document,frameElement,frames,fullScreen,globalStorage,history,innerHeight,innerWidth,length,location,locationbar,localStorage,menubar,messageManager,mozAnimationStartTime,mozInnerScreenX,mozInnerScreenY,mozPaintCount,name,navigator,opener,outerHeight,outerWidth,pageXOffset,pageYOffset,parent,performance,personalbar,pkcs11,returnValue,screen,screenX,screenY,scrollbars,scrollMaxX,scrollMaxY,scrollX,scrollY,self,sessionStorage,sidebar,status,statusbar,toolbar,top,URL,window,addEventListener,alert,atob,back,blur,btoa,captureEvents,clearImmediate,clearInterval,clearTimeout,close,confirm,disableExternalCapture,dispatchEvent,dump,enableExternalCapture,escape,find,focus,forward,GeckoActiveXObject,getAttention,getAttentionWithCycleCount,getComputedStyle,getSelection,home,matchMedia,maximize,minimize,moveBy,moveTo,mozRequestAnimationFrame,open,openDialog,postMessage,print,prompt,QueryInterface,releaseEvents,removeEventListener,resizeBy,resizeTo,restore,routeEvent,scroll,scrollBy,scrollByLines,scrollByPages,scrollTo,setCursor,setImmediate,setInterval,setResizable,setTimeout,showModalDialog,sizeToContent,stop,unescape,updateCommands,XPCNativeWrapper,XPCSafeJSObjectWrapper,onabort,onbeforeunload,onblur,onchange,onclick,onclose,oncontextmenu,ondevicemotion,ondeviceorientation,ondragdrop,onerror,onfocus,onhashchange,onkeydown,onkeypress,onkeyup,onload,onmousedown,onmousemove,onmouseout,onmouseover,onmouseup,onmozbeforepaint,onpaint,onpopstate,onreset,onresize,onscroll,onselect,onsubmit,onunload,onpageshow,onpagehide,Image,Option,Worker,Event,Range,File,FileReader,Blob,BlobBuilder,Attr,CDATASection,CharacterData,Comment,console,DocumentFragment,DocumentType,DomConfiguration,DOMError,DOMErrorHandler,DOMException,DOMImplementation,DOMImplementationList,DOMImplementationRegistry,DOMImplementationSource,DOMLocator,DOMObject,DOMString,DOMStringList,DOMTimeStamp,DOMUserData,Entity,EntityReference,MediaQueryList,MediaQueryListListener,NameList,NamedNodeMap,Node,NodeFilter,NodeIterator,NodeList,Notation,Plugin,PluginArray,ProcessingInstruction,SharedWorker,Text,TimeRanges,Treewalker,TypeInfo,UserDataHandler,Worker,WorkerGlobalScope,HTMLDocument,HTMLElement,HTMLAnchorElement,HTMLAppletElement,HTMLAudioElement,HTMLAreaElement,HTMLBaseElement,HTMLBaseFontElement,HTMLBodyElement,HTMLBRElement,HTMLButtonElement,HTMLCanvasElement,HTMLDirectoryElement,HTMLDivElement,HTMLDListElement,HTMLEmbedElement,HTMLFieldSetElement,HTMLFontElement,HTMLFormElement,HTMLFrameElement,HTMLFrameSetElement,HTMLHeadElement,HTMLHeadingElement,HTMLHtmlElement,HTMLHRElement,HTMLIFrameElement,HTMLImageElement,HTMLInputElement,HTMLKeygenElement,HTMLLabelElement,HTMLLIElement,HTMLLinkElement,HTMLMapElement,HTMLMenuElement,HTMLMetaElement,HTMLModElement,HTMLObjectElement,HTMLOListElement,HTMLOptGroupElement,HTMLOptionElement,HTMLOutputElement,HTMLParagraphElement,HTMLParamElement,HTMLPreElement,HTMLQuoteElement,HTMLScriptElement,HTMLSelectElement,HTMLSourceElement,HTMLSpanElement,HTMLStyleElement,HTMLTableElement,HTMLTableCaptionElement,HTMLTableCellElement,HTMLTableDataCellElement,HTMLTableHeaderCellElement,HTMLTableColElement,HTMLTableRowElement,HTMLTableSectionElement,HTMLTextAreaElement,HTMLTimeElement,HTMLTitleElement,HTMLTrackElement,HTMLUListElement,HTMLUnknownElement,HTMLVideoElement,HTMLCanvasElement,CanvasRenderingContext2D,CanvasGradient,CanvasPattern,TextMetrics,ImageData,CanvasPixelArray,HTMLAudioElement,HTMLVideoElement,NotifyAudioAvailableEvent,HTMLCollection,HTMLAllCollection,HTMLFormControlsCollection,HTMLOptionsCollection,HTMLPropertiesCollection,DOMTokenList,DOMSettableTokenList,DOMStringMap,RadioNodeList,SVGDocument,SVGElement,SVGAElement,SVGAltGlyphElement,SVGAltGlyphDefElement,SVGAltGlyphItemElement,SVGAnimationElement,SVGAnimateElement,SVGAnimateColorElement,SVGAnimateMotionElement,SVGAnimateTransformElement,SVGSetElement,SVGCircleElement,SVGClipPathElement,SVGColorProfileElement,SVGCursorElement,SVGDefsElement,SVGDescElement,SVGEllipseElement,SVGFilterElement,SVGFilterPrimitiveStandardAttributes,SVGFEBlendElement,SVGFEColorMatrixElement,SVGFEComponentTransferElement,SVGFECompositeElement,SVGFEConvolveMatrixElement,SVGFEDiffuseLightingElement,SVGFEDisplacementMapElement,SVGFEDistantLightElement,SVGFEFloodElement,SVGFEGaussianBlurElement,SVGFEImageElement,SVGFEMergeElement,SVGFEMergeNodeElement,SVGFEMorphologyElement,SVGFEOffsetElement,SVGFEPointLightElement,SVGFESpecularLightingElement,SVGFESpotLightElement,SVGFETileElement,SVGFETurbulenceElement,SVGComponentTransferFunctionElement,SVGFEFuncRElement,SVGFEFuncGElement,SVGFEFuncBElement,SVGFEFuncAElement,SVGFontElement,SVGFontFaceElement,SVGFontFaceFormatElement,SVGFontFaceNameElement,SVGFontFaceSrcElement,SVGFontFaceUriElement,SVGForeignObjectElement,SVGGElement,SVGGlyphElement,SVGGlyphRefElement,SVGGradientElement,SVGLinearGradientElement,SVGRadialGradientElement,SVGHKernElement,SVGImageElement,SVGLineElement,SVGMarkerElement,SVGMaskElement,SVGMetadataElement,SVGMissingGlyphElement,SVGMPathElement,SVGPathElement,SVGPatternElement,SVGPolylineElement,SVGPolygonElement,SVGRectElement,SVGScriptElement,SVGStopElement,SVGStyleElement,SVGSVGElement,SVGSwitchElement,SVGSymbolElement,SVGTextElement,SVGTextPathElement,SVGTitleElement,SVGTRefElement,SVGTSpanElement,SVGUseElement,SVGViewElement,SVGVKernElement,SVGAngle,SVGColor,SVGICCColor,SVGElementInstance,SVGElementInstanceList,SVGLength,SVGLengthList,SVGMatrix,SVGNumber,SVGNumberList,SVGPaint,SVGPoint,SVGPointList,SVGPreserveAspectRatio,SVGRect,SVGStringList,SVGTransform,SVGTransformList,SVGAnimatedAngle,SVGAnimatedBoolean,SVGAnimatedEnumeration,SVGAnimatedInteger,SVGAnimatedLength,SVGAnimatedLengthList,SVGAnimatedNumber,SVGAnimatedNumberList,SVGAnimatedPreserveAspectRatio,SVGAnimatedRect,SVGAnimatedString,SVGAnimatedTransformList,SVGPathSegList,SVGPathSeg,SVGPathSegArcAbs,SVGPathSegArcRel,SVGPathSegClosePath,SVGPathSegCurvetoCubicAbs,SVGPathSegCurvetoCubicRel,SVGPathSegCurvetoCubicSmoothAbs,SVGPathSegCurvetoCubicSmoothRel,SVGPathSegCurvetoQuadraticAbs,SVGPathSegCurvetoQuadraticRel,SVGPathSegCurvetoQuadraticSmoothAbs,SVGPathSegCurvetoQuadraticSmoothRel,SVGPathSegLinetoAbs,SVGPathSegLinetoHorizontalAbs,SVGPathSegLinetoHorizontalRel,SVGPathSegLinetoRel,SVGPathSegLinetoVerticalAbs,SVGPathSegLinetoVerticalRel,SVGPathSegMovetoAbs,SVGPathSegMovetoRel,ElementTimeControl,TimeEvent,SVGAnimatedPathData,SVGAnimatedPoints,SVGColorProfileRule,SVGCSSRule,SVGExternalResourcesRequired,SVGFitToViewBox,SVGLangSpace,SVGLocatable,SVGRenderingIntent,SVGStylable,SVGTests,SVGTextContentElement,SVGTextPositioningElement,SVGTransformable,SVGUnitTypes,SVGURIReference,SVGViewSpec,SVGZoomAndPan"); -Blockly.JavaScript.ORDER_ATOMIC=0;Blockly.JavaScript.ORDER_MEMBER=1.1;Blockly.JavaScript.ORDER_NEW=1.2;Blockly.JavaScript.ORDER_FUNCTION_CALL=2;Blockly.JavaScript.ORDER_INCREMENT=3;Blockly.JavaScript.ORDER_DECREMENT=3;Blockly.JavaScript.ORDER_LOGICAL_NOT=4.1;Blockly.JavaScript.ORDER_BITWISE_NOT=4.2;Blockly.JavaScript.ORDER_UNARY_PLUS=4.3;Blockly.JavaScript.ORDER_UNARY_NEGATION=4.4;Blockly.JavaScript.ORDER_TYPEOF=4.5;Blockly.JavaScript.ORDER_VOID=4.6;Blockly.JavaScript.ORDER_DELETE=4.7; -Blockly.JavaScript.ORDER_MULTIPLICATION=5.1;Blockly.JavaScript.ORDER_DIVISION=5.2;Blockly.JavaScript.ORDER_MODULUS=5.3;Blockly.JavaScript.ORDER_ADDITION=6.1;Blockly.JavaScript.ORDER_SUBTRACTION=6.2;Blockly.JavaScript.ORDER_BITWISE_SHIFT=7;Blockly.JavaScript.ORDER_RELATIONAL=8;Blockly.JavaScript.ORDER_IN=8;Blockly.JavaScript.ORDER_INSTANCEOF=8;Blockly.JavaScript.ORDER_EQUALITY=9;Blockly.JavaScript.ORDER_BITWISE_AND=10;Blockly.JavaScript.ORDER_BITWISE_XOR=11;Blockly.JavaScript.ORDER_BITWISE_OR=12; +Blockly.JavaScript.ORDER_ATOMIC=0;Blockly.JavaScript.ORDER_NEW=1.1;Blockly.JavaScript.ORDER_MEMBER=1.2;Blockly.JavaScript.ORDER_FUNCTION_CALL=2;Blockly.JavaScript.ORDER_INCREMENT=3;Blockly.JavaScript.ORDER_DECREMENT=3;Blockly.JavaScript.ORDER_BITWISE_NOT=4.1;Blockly.JavaScript.ORDER_UNARY_PLUS=4.2;Blockly.JavaScript.ORDER_UNARY_NEGATION=4.3;Blockly.JavaScript.ORDER_LOGICAL_NOT=4.4;Blockly.JavaScript.ORDER_TYPEOF=4.5;Blockly.JavaScript.ORDER_VOID=4.6;Blockly.JavaScript.ORDER_DELETE=4.7; +Blockly.JavaScript.ORDER_DIVISION=5.1;Blockly.JavaScript.ORDER_MULTIPLICATION=5.2;Blockly.JavaScript.ORDER_MODULUS=5.3;Blockly.JavaScript.ORDER_SUBTRACTION=6.1;Blockly.JavaScript.ORDER_ADDITION=6.2;Blockly.JavaScript.ORDER_BITWISE_SHIFT=7;Blockly.JavaScript.ORDER_RELATIONAL=8;Blockly.JavaScript.ORDER_IN=8;Blockly.JavaScript.ORDER_INSTANCEOF=8;Blockly.JavaScript.ORDER_EQUALITY=9;Blockly.JavaScript.ORDER_BITWISE_AND=10;Blockly.JavaScript.ORDER_BITWISE_XOR=11;Blockly.JavaScript.ORDER_BITWISE_OR=12; Blockly.JavaScript.ORDER_LOGICAL_AND=13;Blockly.JavaScript.ORDER_LOGICAL_OR=14;Blockly.JavaScript.ORDER_CONDITIONAL=15;Blockly.JavaScript.ORDER_ASSIGNMENT=16;Blockly.JavaScript.ORDER_COMMA=17;Blockly.JavaScript.ORDER_NONE=99; -Blockly.JavaScript.ORDER_OVERRIDES=[[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_LOGICAL_NOT,Blockly.JavaScript.ORDER_LOGICAL_NOT],[Blockly.JavaScript.ORDER_MULTIPLICATION,Blockly.JavaScript.ORDER_MULTIPLICATION],[Blockly.JavaScript.ORDER_ADDITION,Blockly.JavaScript.ORDER_ADDITION],[Blockly.JavaScript.ORDER_LOGICAL_AND,Blockly.JavaScript.ORDER_LOGICAL_AND],[Blockly.JavaScript.ORDER_LOGICAL_OR, -Blockly.JavaScript.ORDER_LOGICAL_OR]];Blockly.JavaScript.ONE_BASED_INDEXING=!0; +Blockly.JavaScript.ORDER_OVERRIDES=[[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_LOGICAL_NOT,Blockly.JavaScript.ORDER_LOGICAL_NOT],[Blockly.JavaScript.ORDER_MULTIPLICATION,Blockly.JavaScript.ORDER_MULTIPLICATION],[Blockly.JavaScript.ORDER_ADDITION, +Blockly.JavaScript.ORDER_ADDITION],[Blockly.JavaScript.ORDER_LOGICAL_AND,Blockly.JavaScript.ORDER_LOGICAL_AND],[Blockly.JavaScript.ORDER_LOGICAL_OR,Blockly.JavaScript.ORDER_LOGICAL_OR]];Blockly.JavaScript.ONE_BASED_INDEXING=!0; Blockly.JavaScript.init=function(a){Blockly.JavaScript.definitions_=Object.create(null);Blockly.JavaScript.functionNames_=Object.create(null);Blockly.JavaScript.variableDB_?Blockly.JavaScript.variableDB_.reset():Blockly.JavaScript.variableDB_=new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);var b=[];a=Blockly.Variables.allVariables(a);if(a.length){for(var c=0;c Date: Tue, 28 Jun 2016 16:41:18 -0700 Subject: [PATCH 13/16] Clean up keystroke handling in treeService. --- accessible/app.component.js | 3 - accessible/toolbox.component.js | 5 +- accessible/tree.service.js | 212 +++++++++++-------------- accessible/workspace-tree.component.js | 4 +- 4 files changed, 100 insertions(+), 124 deletions(-) diff --git a/accessible/app.component.js b/accessible/app.component.js index 5de03e575..763a13c11 100644 --- a/accessible/app.component.js +++ b/accessible/app.component.js @@ -25,9 +25,6 @@ blocklyApp.workspace = new Blockly.Workspace(); -// If the debug flag is true, print console.logs to help with debugging. -blocklyApp.debug = false; - blocklyApp.AppView = ng.core .Component({ selector: 'blockly-app', diff --git a/accessible/toolbox.component.js b/accessible/toolbox.component.js index 589a73712..081803103 100644 --- a/accessible/toolbox.component.js +++ b/accessible/toolbox.component.js @@ -98,7 +98,7 @@ blocklyApp.ToolboxComponent = ng.core this.idMap['Parent' + i] = 'blockly-toolbox-tree-node' + i; } } else { - // Create a single category is created with all the top-level blocks. + // Create a single category with all the top-level blocks. this.xmlHasCategories = false; this.toolboxCategories = [Array.from(xmlToolboxElt.children)]; } @@ -108,8 +108,7 @@ blocklyApp.ToolboxComponent = ng.core // descendant after the ids have been computed. if (this.xmlHasCategories) { this.treeService.setActiveDesc( - document.getElementById('blockly-toolbox-tree-node0'), - document.getElementById('blockly-toolbox-tree')); + 'blockly-toolbox-tree-node0', 'blockly-toolbox-tree'); } }, getToolboxWorkspace: function(categoryNode) { diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 84ac80541..287ef25b6 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -27,12 +27,11 @@ blocklyApp.TreeService = ng.core .Class({ constructor: function() { - // Keeping track of the last key pressed. If the user presses - // enter (to edit a text input or press a button), the keyboard - // focus shifts to that element. In the next keystroke, if the user - // navigates away from the element using the arrow keys, we want - // to shift focus back to the tree as a whole. - this.previousKey_ = null; + // Keeping track of whether the user has just focused into an input + // field. In the next keystroke, if the user navigates away from the + // field using the arrow keys, we want to shift focus back to the tree as + // a whole. + this.justFocusedIntoField_ = false; // Stores active descendant ids for each tree in the page. this.activeDescendantIds_ = {}; }, @@ -76,6 +75,16 @@ blocklyApp.TreeService = ng.core return this.getToolboxTreeNode_(); }, + focusOnCurrentTree_: function(treeId) { + var trees = this.getAllTreeNodes_(); + for (var i = 0; i < trees.length; i++) { + if (trees[i].id == treeId) { + trees[i].focus(); + return true; + } + } + return false; + }, focusOnNextTree_: function(treeId) { var trees = this.getAllTreeNodes_(); for (var i = 0; i < trees.length - 1; i++) { @@ -129,127 +138,102 @@ blocklyApp.TreeService = ng.core }, 0); }, // Make a given node the active descendant of a given tree. - setActiveDesc: function(newActiveDesc, tree) { - this.unmarkActiveDesc_(this.getActiveDescId(tree.id)); - this.markActiveDesc_(newActiveDesc.id); - this.activeDescendantIds_[tree.id] = newActiveDesc.id; + setActiveDesc: function(newActiveDescId, treeId) { + this.unmarkActiveDesc_(this.getActiveDescId(treeId)); + this.markActiveDesc_(newActiveDescId); + this.activeDescendantIds_[treeId] = newActiveDescId; }, onWorkspaceToolbarKeypress: function(e, treeId) { - switch (e.keyCode) { - case 9: - // 16,9: shift, tab - if (e.shiftKey) { - // If the previous key is shift, we're shift-tabbing mode. - this.focusOnPreviousTree_(treeId); - } else { - // If previous key isn't shift, we're tabbing. - this.focusOnNextTree_(treeId); - } - e.preventDefault(); - e.stopPropagation(); - break; + if (e.keyCode == 9) { + // Tab key. + if (e.shiftKey) { + this.focusOnPreviousTree_(treeId); + } else { + this.focusOnNextTree_(treeId); + } + e.preventDefault(); + e.stopPropagation(); } }, + isButtonOrFieldNode_: function(node) { + return ['BUTTON', 'INPUT'].indexOf(node.tagName) != -1; + }, onKeypress: function(e, tree) { var treeId = tree.id; - var node = document.getElementById(this.getActiveDescId(treeId)); - var keepFocus = this.previousKey_ == 13; - if (!node) { - blocklyApp.debug && console.log('KeyHandler: no active descendant'); + var activeDesc = document.getElementById(this.getActiveDescId(treeId)); + if (!activeDesc) { + console.log('ERROR: no active descendant for current tree.'); + return; } - switch (e.keyCode) { - case 9: - // 16,9: shift, tab - if (e.shiftKey) { - // If the previous key is shift, we're shift-tabbing. - this.focusOnPreviousTree_(treeId); - } else { - // If previous key isn't shift, we're tabbing - // we want to go to the run code button. - this.focusOnNextTree_(treeId); + + var isFocusingIntoField = false; + + if (e.keyCode == 13) { + // Enter key. The user wants to interact with a child. + if (activeDesc.children.length == 1) { + var child = activeDesc.children[0]; + if (child.tagName == 'BUTTON') { + child.click(); + this.isFocusingIntoField = true; + } else if (child.tagName == 'INPUT') { + child.focus(); } - // Setting the previous key variable in each case because - // we only want to save the previous navigation keystroke, - // not any typing. - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 37: - // Left-facing arrow: go out a level, if possible. If not, do nothing. - var nextNode = node.parentNode; - if (node.tagName == 'BUTTON' || node.tagName == 'INPUT') { + } + } else if (e.keyCode == 9) { + // Tab key. + if (e.shiftKey) { + this.focusOnPreviousTree_(treeId); + } else { + this.focusOnNextTree_(treeId); + } + e.preventDefault(); + e.stopPropagation(); + } else if (e.keyCode >= 37 && e.keyCode <= 40) { + // Arrow keys. + + // If the user has just focused into a text field, shift focus back to + // the main tree. + if (this.justFocusedIntoField_) { + this.focusOnCurrentTree_(treeId); + } + + if (e.keyCode == 37) { + // Left arrow key. Go up a level, if possible. + var nextNode = activeDesc.parentNode; + if (this.isButtonOrFieldNode_(activeDesc)) { nextNode = nextNode.parentNode; } - while (nextNode && nextNode.className != 'treeview' && - nextNode.tagName != 'LI') { + while (nextNode && nextNode.tagName != 'LI') { nextNode = nextNode.parentNode; } - if (!nextNode || nextNode.className == 'treeview') { - return; + if (nextNode) { + this.setActiveDesc(nextNode.id, treeId); } - this.setActiveDesc(nextNode, tree); - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 38: - // Up-facing arrow: go up a level, if possible. If not, do nothing. - var prevSibling = this.getPreviousSibling(node); - if (prevSibling && prevSibling.tagName != 'H1') { - this.setActiveDesc(prevSibling, tree); - } else { - blocklyApp.debug && console.log('no previous sibling'); + } else if (e.keyCode == 38) { + // Up arrow key. Go to the previous sibling, if possible. + var prevSibling = this.getPreviousSibling(activeDesc); + if (prevSibling) { + this.setActiveDesc(prevSibling.id, treeId); } - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 39: - var firstChild = this.getFirstChild(node); + } else if (e.keyCode == 39) { + // Right arrow key. Go down a level, if possible. + var firstChild = this.getFirstChild(activeDesc); if (firstChild) { - this.setActiveDesc(firstChild, tree); - } else { - blocklyApp.debug && console.log('no valid child'); + this.setActiveDesc(firstChild.id, treeId); } - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 40: - // Down-facing arrow: go down a level, if possible. - // If not, do nothing. - var nextSibling = this.getNextSibling(node); + } else if (e.keyCode == 40) { + // Down arrow key. Go to the next sibling, if possible. + var nextSibling = this.getNextSibling(activeDesc); if (nextSibling) { - this.setActiveDesc(nextSibling, tree); - } else { - blocklyApp.debug && console.log('no next sibling'); + this.setActiveDesc(nextSibling.id, treeId); } - this.previousKey_ = e.keyCode; - e.preventDefault(); - e.stopPropagation(); - break; - case 13: - // If I've pressed enter, I want to interact with a child. - var activeDesc = node; - if (activeDesc) { - var children = activeDesc.children; - var child = children[0]; - if (children.length == 1 && (child.tagName == 'INPUT' || - child.tagName == 'BUTTON')) { - if (child.tagName == 'BUTTON') { - child.click(); - } - else if (child.tagName == 'INPUT') { - child.focus(); - } - } - } else { - blocklyApp.debug && console.log('no activeDesc'); - } - this.previousKey_ = e.keyCode; - break; + } + + e.preventDefault(); + e.stopPropagation(); } + + this.justFocusedIntoField_ = isFocusingIntoField; }, getFirstChild: function(element) { if (!element) { @@ -273,13 +257,12 @@ blocklyApp.TreeService = ng.core if (element.nextElementSibling) { // If there is a sibling, find the list element child of the sibling. var node = element.nextElementSibling; - if (node.tagName != 'LI') { - var listElems = node.getElementsByTagName('li'); - // getElementsByTagName returns in DFS order - // therefore the first element is the first relevant list child. - return listElems[0]; + if (node.tagName == 'LI') { + return node; } else { - return element.nextElementSibling; + // getElementsByTagName returns in DFS order, therefore the first + // element is the first relevant list child. + return node.getElementsByTagName('li')[0]; } } else { var parent = element.parentNode; @@ -343,7 +326,6 @@ blocklyApp.TreeService = ng.core } } } - blocklyApp.debug && console.log('no last child'); return null; } } diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index 2fe99002c..938854494 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -180,9 +180,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core if (this.tree && this.isTopLevel && !this.treeService.getActiveDescId(this.tree.id)) { - this.treeService.setActiveDesc( - document.getElementById(this.idMap['parentList']), - this.tree); + this.treeService.setActiveDesc(this.idMap['parentList'], this.tree.id); } }, hasPreviousConnection: function(block) { From 5b63b9c193a056795160253847aa8405f53496a0 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Tue, 28 Jun 2016 17:13:07 -0700 Subject: [PATCH 14/16] Remove justFocusedIntoField_ special case. --- accessible/tree.service.js | 14 -------------- accessible/workspace-tree.component.js | 8 ++++---- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 287ef25b6..d9c0784fa 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -27,11 +27,6 @@ blocklyApp.TreeService = ng.core .Class({ constructor: function() { - // Keeping track of whether the user has just focused into an input - // field. In the next keystroke, if the user navigates away from the - // field using the arrow keys, we want to shift focus back to the tree as - // a whole. - this.justFocusedIntoField_ = false; // Stores active descendant ids for each tree in the page. this.activeDescendantIds_ = {}; }, @@ -190,13 +185,6 @@ blocklyApp.TreeService = ng.core e.stopPropagation(); } else if (e.keyCode >= 37 && e.keyCode <= 40) { // Arrow keys. - - // If the user has just focused into a text field, shift focus back to - // the main tree. - if (this.justFocusedIntoField_) { - this.focusOnCurrentTree_(treeId); - } - if (e.keyCode == 37) { // Left arrow key. Go up a level, if possible. var nextNode = activeDesc.parentNode; @@ -232,8 +220,6 @@ blocklyApp.TreeService = ng.core e.preventDefault(); e.stopPropagation(); } - - this.justFocusedIntoField_ = isFocusingIntoField; }, getFirstChild: function(element) { if (!element) { diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index 938854494..cf867ab45 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -198,6 +198,10 @@ blocklyApp.WorkspaceTreeComponent = ng.core return topBlock.id == block.id; }); }, + generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { + return this.utilsService.generateAriaLabelledByAttr( + mainLabel, secondLabel, isDisabled); + }, pasteAbove: function(block) { var that = this; this.treeService.runWhilePreservingFocus(function() { @@ -232,10 +236,6 @@ blocklyApp.WorkspaceTreeComponent = ng.core block.dispose(true); } }, - generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { - return this.utilsService.generateAriaLabelledByAttr( - mainLabel, secondLabel, isDisabled); - }, sendToMarkedSpot: function(block) { this.clipboardService.pasteToMarkedConnection(block, false); From 2dcb6cb68b199b45fbe5f84f65f5734fc19e7605 Mon Sep 17 00:00:00 2001 From: Rodrigo Queiro Date: Wed, 29 Jun 2016 03:11:48 +0200 Subject: [PATCH 15/16] Ignore mouseup at end of drag with isDragging (#416) This includes both block drags and workspace scrolls and fixes #404. I used isDragging rather than checking the origin of click so that it can easily be extended to the case where the flyout is scrolled by dragging a block, a la LLK/scratch-blocks#206. --- core/field.js | 2 +- core/flyout.js | 3 +-- core/icon.js | 2 +- core/workspace_svg.js | 8 ++++++++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/core/field.js b/core/field.js index 0ea102fff..0e919ecd3 100644 --- a/core/field.js +++ b/core/field.js @@ -433,7 +433,7 @@ Blockly.Field.prototype.onMouseUp_ = function(e) { } else if (Blockly.isRightButton(e)) { // Right-click. return; - } else if (Blockly.dragMode_ == Blockly.DRAG_FREE) { + } else if (this.sourceBlock_.workspace.isDragging()) { // Drag operation is concluding. Don't open the editor. return; } else if (this.sourceBlock_.isEditable()) { diff --git a/core/flyout.js b/core/flyout.js index e8aa2f872..c65276fea 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -761,8 +761,7 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { * @private */ Blockly.Flyout.prototype.onMouseUp_ = function(e) { - if (Blockly.dragMode_ != Blockly.DRAG_FREE && - !Blockly.WidgetDiv.isVisible()) { + if (!this.workspace_.isDragging() && !Blockly.WidgetDiv.isVisible()) { Blockly.Events.fire( new Blockly.Events.Ui(Blockly.Flyout.startBlock_, 'click', undefined, undefined)); diff --git a/core/icon.js b/core/icon.js index 9d2cbeaad..395c98544 100644 --- a/core/icon.js +++ b/core/icon.js @@ -124,7 +124,7 @@ Blockly.Icon.prototype.isVisible = function() { * @private */ Blockly.Icon.prototype.iconClick_ = function(e) { - if (Blockly.dragMode_ == Blockly.DRAG_FREE) { + if (this.block_.workspace.isDragging()) { // Drag operation is concluding. Don't open the editor. return; } diff --git a/core/workspace_svg.js b/core/workspace_svg.js index d857bdf21..6f5f26a1b 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -710,6 +710,14 @@ Blockly.WorkspaceSvg.prototype.moveDrag = function(e) { return goog.math.Coordinate.sum(this.dragDeltaXY_, point); }; +/** + * Is the user currently dragging a block or scrolling the workspace? + * @return {boolean} True if currently dragging or scrolling + */ +Blockly.WorkspaceSvg.prototype.isDragging = function() { + return Blockly.dragMode_ == Blockly.DRAG_FREE || this.isScrolling; +}; + /** * Handle a mouse-wheel on SVG drawing surface. * @param {!Event} e Mouse wheel event. From 63bb89108b09c43e73f5d0e82ae0023ed0f35b99 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Tue, 28 Jun 2016 18:28:47 -0700 Subject: [PATCH 16/16] Do not allow a block to be moved to a marked connection that it contains. Fix an error where block ids did not update when a new block is inserted in a list. --- accessible/clipboard.service.js | 68 +++++++++++++++----------- accessible/toolbox-tree.component.js | 7 ++- accessible/workspace-tree.component.js | 15 ++++-- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index de9b382a3..eb8c0896d 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -30,6 +30,43 @@ blocklyApp.ClipboardService = ng.core this.clipboardBlockNextConnection_ = null; this.markedConnection_ = null; }, + areConnectionsCompatible_: function(blockConnection, connection) { + // Check that both connections exist, that it's the right kind of + // connection, and that the types match. + return Boolean( + connection && blockConnection && + Blockly.OPPOSITE_TYPE[blockConnection.type] == connection.type && + connection.checkType_(blockConnection)); + }, + isCompatibleWithClipboard: function(connection) { + var superiorConnection = this.clipboardBlockSuperiorConnection_; + var nextConnection = this.clipboardBlockNextConnection_; + return Boolean( + this.areConnectionsCompatible_(connection, superiorConnection) || + this.areConnectionsCompatible_(connection, nextConnection)); + }, + canBeMovedToMarkedConnection: function(block) { + // It should not be possible to move a block to one of its own + // connections. + if (this.markedConnection_ && + this.markedConnection_.sourceBlock_.id == block.id) { + return false; + } + + return this.canBeCopiedToMarkedConnection(block); + }, + canBeCopiedToMarkedConnection: function(block) { + var blockConnection = block.outputConnection || block.previousConnection; + return Boolean( + this.markedConnection_ && + this.markedConnection_.sourceBlock_.workspace && + this.areConnectionsCompatible_( + blockConnection, this.markedConnection_)); + }, + markConnection: function(connection) { + this.markedConnection_ = connection; + alert(Blockly.Msg.MARKED_SPOT_MSG); + }, cut: function(block) { var blockSummary = block.toString(); this.copy(block, false); @@ -64,8 +101,8 @@ blocklyApp.ClipboardService = ng.core }, pasteToMarkedConnection: function(block, announce) { var xml = Blockly.Xml.blockToDom(block); - var reconstitutedBlock = - Blockly.Xml.domToBlock(blocklyApp.workspace, xml); + var reconstitutedBlock = Blockly.Xml.domToBlock( + blocklyApp.workspace, xml); this.markedConnection_.connect( reconstitutedBlock.outputConnection || reconstitutedBlock.previousConnection); @@ -74,32 +111,5 @@ blocklyApp.ClipboardService = ng.core Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG + reconstitutedBlock.toString()); } - }, - markConnection: function(connection) { - this.markedConnection_ = connection; - alert(Blockly.Msg.MARKED_SPOT_MSG); - }, - isCompatibleWithConnection_: function(blockConnection, connection) { - // Check that both connections exist, that the types match, and that it's - // the right kind of connection. - return Boolean( - connection && blockConnection && - Blockly.OPPOSITE_TYPE[blockConnection.type] == connection.type && - connection.checkType_(blockConnection)); - }, - isBlockCompatibleWithMarkedConnection: function(block) { - var blockConnection = block.outputConnection || block.previousConnection; - return Boolean( - this.markedConnection_ && - this.markedConnection_.sourceBlock_.workspace && - this.isCompatibleWithConnection_( - blockConnection, this.markedConnection_)); - }, - isClipboardCompatibleWithConnection: function(connection) { - var superiorConnection = this.clipboardBlockSuperiorConnection_; - var nextConnection = this.clipboardBlockNextConnection_; - return Boolean( - this.isCompatibleWithConnection_(connection, superiorConnection) || - this.isCompatibleWithConnection_(connection, nextConnection)); } }); diff --git a/accessible/toolbox-tree.component.js b/accessible/toolbox-tree.component.js index cc701fc35..64320458d 100644 --- a/accessible/toolbox-tree.component.js +++ b/accessible/toolbox-tree.component.js @@ -59,12 +59,12 @@ blocklyApp.ToolboxTreeComponent = ng.core
  • @@ -138,6 +138,9 @@ blocklyApp.ToolboxTreeComponent = ng.core return this.utilsService.generateAriaLabelledByAttr( mainLabel, secondLabel, isDisabled); }, + canBeCopiedToMarkedConnection: function(block) { + return this.clipboardService.canBeCopiedToMarkedConnection(block); + }, copyToWorkspace: function(block) { var xml = Blockly.Xml.blockToDom(block); Blockly.Xml.domToBlock(blocklyApp.workspace, xml); diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index cf867ab45..48ecd2c14 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -79,10 +79,10 @@ blocklyApp.WorkspaceTreeComponent = ng.core [disabled]="!hasPreviousConnection(block)">{{'MARK_SPOT_ABOVE'|translate}}
  • + [disabled]="!canBeMovedToMarkedConnection(block)">{{'MOVE_TO_MARKED_SPOT'|translate}}