From 4ba2b1bea39b480924e3552b9d392276016a5c80 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Mon, 6 Jun 2016 00:19:36 -0700 Subject: [PATCH 01/22] Use a comment block for function comments in generated JS, Python and Dart. --- generators/dart.js | 7 ++++++- generators/javascript.js | 9 ++++++++- generators/python.js | 7 ++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/generators/dart.js b/generators/dart.js index 81b011406..f0d7715ea 100644 --- a/generators/dart.js +++ b/generators/dart.js @@ -173,7 +173,12 @@ Blockly.Dart.scrub_ = function(block, code) { // Collect comment for this block. var comment = block.getCommentText(); if (comment) { - commentCode += Blockly.Dart.prefixLines(comment, '// ') + '\n'; + if (block.getProcedureDef) { + // Use documentation comment for function comments. + commentCode += Blockly.Dart.prefixLines(comment + '\n', '/// '); + } else { + commentCode += Blockly.Dart.prefixLines(comment + '\n', '// '); + } } // Collect comments for all value arguments. // Don't collect comments for nested statements. diff --git a/generators/javascript.js b/generators/javascript.js index 5bd49f9d2..2a90b57ef 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -193,7 +193,14 @@ Blockly.JavaScript.scrub_ = function(block, code) { // Collect comment for this block. var comment = block.getCommentText(); if (comment) { - commentCode += Blockly.JavaScript.prefixLines(comment, '// ') + '\n'; + if (block.getProcedureDef) { + // Use a comment block for function comments. + commentCode += '/**\n' + + Blockly.JavaScript.prefixLines(comment + '\n', ' * ') + + ' */\n'; + } else { + commentCode += Blockly.JavaScript.prefixLines(comment + '\n', '// '); + } } // Collect comments for all value arguments. // Don't collect comments for nested statements. diff --git a/generators/python.js b/generators/python.js index ddfe2f008..106812498 100644 --- a/generators/python.js +++ b/generators/python.js @@ -176,7 +176,12 @@ Blockly.Python.scrub_ = function(block, code) { // Collect comment for this block. var comment = block.getCommentText(); if (comment) { - commentCode += Blockly.Python.prefixLines(comment, '# ') + '\n'; + if (block.getProcedureDef) { + // Use a comment block for function comments. + commentCode += '"""' + comment + '\n"""\n'; + } else { + commentCode += Blockly.Python.prefixLines(comment + '\n', '# '); + } } // Collect comments for all value arguments. // Don't collect comments for nested statements. From 3012d52808655ab17a96133add8294a28cc15ad3 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 8 Jun 2016 00:12:58 -0700 Subject: [PATCH 02/22] Line wrap comments in generated code. --- core/generator.js | 7 ++ core/tooltip.js | 156 +----------------------------------- core/utils.js | 168 +++++++++++++++++++++++++++++++++++++++ generators/javascript.js | 1 + generators/lua.js | 1 + generators/php.js | 1 + generators/python.js | 1 + 7 files changed, 180 insertions(+), 155 deletions(-) diff --git a/core/generator.js b/core/generator.js index 3cab1ccf3..e9eb663ff 100644 --- a/core/generator.js +++ b/core/generator.js @@ -70,6 +70,13 @@ Blockly.Generator.prototype.STATEMENT_PREFIX = null; */ Blockly.Generator.prototype.INDENT = ' '; +/** + * Maximum length for a comment before wrapping. Does not account for + * indenting level. + * @type {number} + */ +Blockly.Generator.prototype.COMMENT_WRAP = 60; + /** * Generate code for all blocks in the workspace to the specified language. * @param {Blockly.Workspace} workspace Workspace to generate code from. diff --git a/core/tooltip.js b/core/tooltip.js index b4ded3772..3aa828812 100644 --- a/core/tooltip.js +++ b/core/tooltip.js @@ -239,7 +239,7 @@ Blockly.Tooltip.show_ = function() { while (goog.isFunction(tip)) { tip = tip(); } - tip = Blockly.Tooltip.wrap_(tip, Blockly.Tooltip.LIMIT); + tip = Blockly.utils.wrap(tip, Blockly.Tooltip.LIMIT); // Create new text, line by line. var lines = tip.split('\n'); for (var i = 0; i < lines.length; i++) { @@ -282,157 +282,3 @@ Blockly.Tooltip.show_ = function() { Blockly.Tooltip.DIV.style.top = anchorY + 'px'; Blockly.Tooltip.DIV.style.left = anchorX + 'px'; }; - -/** - * Wrap text to the specified width. - * @param {string} text Text to wrap. - * @param {number} limit Width to wrap each line. - * @return {string} Wrapped text. - * @private - */ -Blockly.Tooltip.wrap_ = function(text, limit) { - if (text.length <= limit) { - // Short text, no need to wrap. - return text; - } - // Split the text into words. - var words = text.trim().split(/\s+/); - // Set limit to be the length of the largest word. - for (var i = 0; i < words.length; i++) { - if (words[i].length > limit) { - limit = words[i].length; - } - } - - var lastScore; - var score = -Infinity; - var lastText; - var lineCount = 1; - do { - lastScore = score; - lastText = text; - // Create a list of booleans representing if a space (false) or - // a break (true) appears after each word. - var wordBreaks = []; - // Seed the list with evenly spaced linebreaks. - var steps = words.length / lineCount; - var insertedBreaks = 1; - for (var i = 0; i < words.length - 1; i++) { - if (insertedBreaks < (i + 1.5) / steps) { - insertedBreaks++; - wordBreaks[i] = true; - } else { - wordBreaks[i] = false; - } - } - wordBreaks = Blockly.Tooltip.wrapMutate_(words, wordBreaks, limit); - score = Blockly.Tooltip.wrapScore_(words, wordBreaks, limit); - text = Blockly.Tooltip.wrapToText_(words, wordBreaks); - lineCount++; - } while (score > lastScore); - return lastText; -}; - -/** - * Compute a score for how good the wrapping is. - * @param {!Array.} words Array of each word. - * @param {!Array.} wordBreaks Array of line breaks. - * @param {number} limit Width to wrap each line. - * @return {number} Larger the better. - * @private - */ -Blockly.Tooltip.wrapScore_ = function(words, wordBreaks, limit) { - // If this function becomes a performance liability, add caching. - // Compute the length of each line. - var lineLengths = [0]; - var linePunctuation = []; - for (var i = 0; i < words.length; i++) { - lineLengths[lineLengths.length - 1] += words[i].length; - if (wordBreaks[i] === true) { - lineLengths.push(0); - linePunctuation.push(words[i].charAt(words[i].length - 1)); - } else if (wordBreaks[i] === false) { - lineLengths[lineLengths.length - 1]++; - } - } - var maxLength = Math.max.apply(Math, lineLengths); - - var score = 0; - for (var i = 0; i < lineLengths.length; i++) { - // Optimize for width. - // -2 points per char over limit (scaled to the power of 1.5). - score -= Math.pow(Math.abs(limit - lineLengths[i]), 1.5) * 2; - // Optimize for even lines. - // -1 point per char smaller than max (scaled to the power of 1.5). - score -= Math.pow(maxLength - lineLengths[i], 1.5); - // Optimize for structure. - // Add score to line endings after punctuation. - if ('.?!'.indexOf(linePunctuation[i]) != -1) { - score += limit / 3; - } else if (',;)]}'.indexOf(linePunctuation[i]) != -1) { - score += limit / 4; - } - } - // All else being equal, the last line should not be longer than the - // previous line. For example, this looks wrong: - // aaa bbb - // ccc ddd eee - if (lineLengths.length > 1 && lineLengths[lineLengths.length - 1] <= - lineLengths[lineLengths.length - 2]) { - score += 0.5; - } - return score; -}; - -/** - * Mutate the array of line break locations until an optimal solution is found. - * No line breaks are added or deleted, they are simply moved around. - * @param {!Array.} words Array of each word. - * @param {!Array.} wordBreaks Array of line breaks. - * @param {number} limit Width to wrap each line. - * @return {!Array.} New array of optimal line breaks. - * @private - */ -Blockly.Tooltip.wrapMutate_ = function(words, wordBreaks, limit) { - var bestScore = Blockly.Tooltip.wrapScore_(words, wordBreaks, limit); - var bestBreaks; - // Try shifting every line break forward or backward. - for (var i = 0; i < wordBreaks.length - 1; i++) { - if (wordBreaks[i] == wordBreaks[i + 1]) { - continue; - } - var mutatedWordBreaks = [].concat(wordBreaks); - mutatedWordBreaks[i] = !mutatedWordBreaks[i]; - mutatedWordBreaks[i + 1] = !mutatedWordBreaks[i + 1]; - var mutatedScore = - Blockly.Tooltip.wrapScore_(words, mutatedWordBreaks, limit); - if (mutatedScore > bestScore) { - bestScore = mutatedScore; - bestBreaks = mutatedWordBreaks; - } - } - if (bestBreaks) { - // Found an improvement. See if it may be improved further. - return Blockly.Tooltip.wrapMutate_(words, bestBreaks, limit); - } - // No improvements found. Done. - return wordBreaks; -}; - -/** - * Reassemble the array of words into text, with the specified line breaks. - * @param {!Array.} words Array of each word. - * @param {!Array.} wordBreaks Array of line breaks. - * @return {string} Plain text. - * @private - */ -Blockly.Tooltip.wrapToText_ = function(words, wordBreaks) { - var text = []; - for (var i = 0; i < words.length; i++) { - text.push(words[i]); - if (wordBreaks[i] !== undefined) { - text.push(wordBreaks[i] ? '\n' : ' '); - } - } - return text.join(''); -}; diff --git a/core/utils.js b/core/utils.js index 1a0fba1b6..9b9e04d60 100644 --- a/core/utils.js +++ b/core/utils.js @@ -495,3 +495,171 @@ Blockly.genUid = function() { */ Blockly.genUid.soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + +/** + * Wrap text to the specified width. + * @param {string} text Text to wrap. + * @param {number} limit Width to wrap each line. + * @return {string} Wrapped text. + */ +Blockly.utils.wrap = function(text, limit) { + var lines = text.split('\n'); + for (var i = 0; i < lines.length; i++) { + lines[i] = Blockly.utils.wrap_line_(lines[i], limit); + } + return lines.join('\n'); +}; + +/** + * Wrap single line of text to the specified width. + * @param {string} text Text to wrap. + * @param {number} limit Width to wrap each line. + * @return {string} Wrapped text. + * @private + */ +Blockly.utils.wrap_line_ = function(text, limit) { + if (text.length <= limit) { + // Short text, no need to wrap. + return text; + } + // Split the text into words. + var words = text.trim().split(/\s+/); + // Set limit to be the length of the largest word. + for (var i = 0; i < words.length; i++) { + if (words[i].length > limit) { + limit = words[i].length; + } + } + + var lastScore; + var score = -Infinity; + var lastText; + var lineCount = 1; + do { + lastScore = score; + lastText = text; + // Create a list of booleans representing if a space (false) or + // a break (true) appears after each word. + var wordBreaks = []; + // Seed the list with evenly spaced linebreaks. + var steps = words.length / lineCount; + var insertedBreaks = 1; + for (var i = 0; i < words.length - 1; i++) { + if (insertedBreaks < (i + 1.5) / steps) { + insertedBreaks++; + wordBreaks[i] = true; + } else { + wordBreaks[i] = false; + } + } + wordBreaks = Blockly.utils.wrapMutate_(words, wordBreaks, limit); + score = Blockly.utils.wrapScore_(words, wordBreaks, limit); + text = Blockly.utils.wrapToText_(words, wordBreaks); + lineCount++; + } while (score > lastScore); + return lastText; +}; + +/** + * Compute a score for how good the wrapping is. + * @param {!Array.} words Array of each word. + * @param {!Array.} wordBreaks Array of line breaks. + * @param {number} limit Width to wrap each line. + * @return {number} Larger the better. + * @private + */ +Blockly.utils.wrapScore_ = function(words, wordBreaks, limit) { + // If this function becomes a performance liability, add caching. + // Compute the length of each line. + var lineLengths = [0]; + var linePunctuation = []; + for (var i = 0; i < words.length; i++) { + lineLengths[lineLengths.length - 1] += words[i].length; + if (wordBreaks[i] === true) { + lineLengths.push(0); + linePunctuation.push(words[i].charAt(words[i].length - 1)); + } else if (wordBreaks[i] === false) { + lineLengths[lineLengths.length - 1]++; + } + } + var maxLength = Math.max.apply(Math, lineLengths); + + var score = 0; + for (var i = 0; i < lineLengths.length; i++) { + // Optimize for width. + // -2 points per char over limit (scaled to the power of 1.5). + score -= Math.pow(Math.abs(limit - lineLengths[i]), 1.5) * 2; + // Optimize for even lines. + // -1 point per char smaller than max (scaled to the power of 1.5). + score -= Math.pow(maxLength - lineLengths[i], 1.5); + // Optimize for structure. + // Add score to line endings after punctuation. + if ('.?!'.indexOf(linePunctuation[i]) != -1) { + score += limit / 3; + } else if (',;)]}'.indexOf(linePunctuation[i]) != -1) { + score += limit / 4; + } + } + // All else being equal, the last line should not be longer than the + // previous line. For example, this looks wrong: + // aaa bbb + // ccc ddd eee + if (lineLengths.length > 1 && lineLengths[lineLengths.length - 1] <= + lineLengths[lineLengths.length - 2]) { + score += 0.5; + } + return score; +}; + +/** + * Mutate the array of line break locations until an optimal solution is found. + * No line breaks are added or deleted, they are simply moved around. + * @param {!Array.} words Array of each word. + * @param {!Array.} wordBreaks Array of line breaks. + * @param {number} limit Width to wrap each line. + * @return {!Array.} New array of optimal line breaks. + * @private + */ +Blockly.utils.wrapMutate_ = function(words, wordBreaks, limit) { + var bestScore = Blockly.utils.wrapScore_(words, wordBreaks, limit); + var bestBreaks; + // Try shifting every line break forward or backward. + for (var i = 0; i < wordBreaks.length - 1; i++) { + if (wordBreaks[i] == wordBreaks[i + 1]) { + continue; + } + var mutatedWordBreaks = [].concat(wordBreaks); + mutatedWordBreaks[i] = !mutatedWordBreaks[i]; + mutatedWordBreaks[i + 1] = !mutatedWordBreaks[i + 1]; + var mutatedScore = + Blockly.utils.wrapScore_(words, mutatedWordBreaks, limit); + if (mutatedScore > bestScore) { + bestScore = mutatedScore; + bestBreaks = mutatedWordBreaks; + } + } + if (bestBreaks) { + // Found an improvement. See if it may be improved further. + return Blockly.utils.wrapMutate_(words, bestBreaks, limit); + } + // No improvements found. Done. + return wordBreaks; +}; + +/** + * Reassemble the array of words into text, with the specified line breaks. + * @param {!Array.} words Array of each word. + * @param {!Array.} wordBreaks Array of line breaks. + * @return {string} Plain text. + * @private + */ +Blockly.utils.wrapToText_ = function(words, wordBreaks) { + var text = []; + for (var i = 0; i < words.length; i++) { + text.push(words[i]); + if (wordBreaks[i] !== undefined) { + text.push(wordBreaks[i] ? '\n' : ' '); + } + } + return text.join(''); +}; diff --git a/generators/javascript.js b/generators/javascript.js index 2a90b57ef..32b1e2967 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -192,6 +192,7 @@ Blockly.JavaScript.scrub_ = function(block, code) { if (!block.outputConnection || !block.outputConnection.targetConnection) { // Collect comment for this block. var comment = block.getCommentText(); + comment = Blockly.utils.wrap(comment, this.COMMENT_WRAP - 3); if (comment) { if (block.getProcedureDef) { // Use a comment block for function comments. diff --git a/generators/lua.js b/generators/lua.js index 10e732c7c..60a6c0e14 100644 --- a/generators/lua.js +++ b/generators/lua.js @@ -164,6 +164,7 @@ Blockly.Lua.scrub_ = function(block, code) { if (!block.outputConnection || !block.outputConnection.targetConnection) { // Collect comment for this block. var comment = block.getCommentText(); + comment = Blockly.utils.wrap(comment, this.COMMENT_WRAP - 3); if (comment) { commentCode += Blockly.Lua.prefixLines(comment, '-- ') + '\n'; } diff --git a/generators/php.js b/generators/php.js index ce2f453f7..d935cda1b 100644 --- a/generators/php.js +++ b/generators/php.js @@ -167,6 +167,7 @@ Blockly.PHP.scrub_ = function(block, code) { if (!block.outputConnection || !block.outputConnection.targetConnection) { // Collect comment for this block. var comment = block.getCommentText(); + comment = Blockly.utils.wrap(comment, this.COMMENT_WRAP - 3); if (comment) { commentCode += Blockly.PHP.prefixLines(comment, '// ') + '\n'; } diff --git a/generators/python.js b/generators/python.js index 106812498..0e89973dd 100644 --- a/generators/python.js +++ b/generators/python.js @@ -175,6 +175,7 @@ Blockly.Python.scrub_ = function(block, code) { if (!block.outputConnection || !block.outputConnection.targetConnection) { // Collect comment for this block. var comment = block.getCommentText(); + comment = Blockly.utils.wrap(comment, this.COMMENT_WRAP - 3); if (comment) { if (block.getProcedureDef) { // Use a comment block for function comments. From c09624a1b88eb6adf0bd87a62919a2d11fcc9897 Mon Sep 17 00:00:00 2001 From: Rodrigo Queiro Date: Wed, 8 Jun 2016 19:01:04 +0200 Subject: [PATCH 03/22] Remove reference to undefined variable (#413) REASON_MUST_DISCONNECT was removed by a refactor in 2a1ffa1. --- core/connection.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/connection.js b/core/connection.js index d245c3548..65d7fd9cf 100644 --- a/core/connection.js +++ b/core/connection.js @@ -346,8 +346,7 @@ Blockly.Connection.prototype.checkConnection_ = function(target) { Blockly.Connection.prototype.isConnectionAllowed = function(candidate) { // Type checking. var canConnect = this.canConnectWithReason_(candidate); - if (canConnect != Blockly.Connection.CAN_CONNECT && - canConnect != Blockly.Connection.REASON_MUST_DISCONNECT) { + if (canConnect != Blockly.Connection.CAN_CONNECT) { return false; } From e4900fc27c71c8ce8d32f53c922c1ffeeb5c1077 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Wed, 8 Jun 2016 10:03:14 -0700 Subject: [PATCH 04/22] Fix airstrike by grabbing the correct toolbox element. (#411) Probably broken in 266e2ffa9a017d21d7ca2f151730d6ecfcecf173. --- tests/playground.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/playground.html b/tests/playground.html index d43d32ed4..e7e6e1166 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -68,9 +68,7 @@ function start() { var match = location.search.match(/dir=([^&]+)/); var rtl = match && match[1] == 'rtl'; document.forms.options.elements.dir.selectedIndex = Number(rtl); - var match = location.search.match(/toolbox=([^&]+)/); - var toolbox = - document.getElementById('toolbox-' + (match ? match[1] : 'categories')); + var toolbox = getToolboxElement(); document.forms.options.elements.toolbox.selectedIndex = Number(toolbox.getElementsByTagName('category').length == 0); match = location.search.match(/side=([^&]+)/); @@ -118,6 +116,11 @@ function start() { taChange(); } +function getToolboxElement() { + var match = location.search.match(/toolbox=([^&]+)/); + return document.getElementById('toolbox-' + (match ? match[1] : 'categories')); +} + function toXml() { var output = document.getElementById('importExport'); var xml = Blockly.Xml.workspaceToDom(workspace); @@ -175,7 +178,7 @@ function logger(e) { function airstrike(n) { var prototypes = []; - var toolbox = document.getElementById('toolbox'); + var toolbox = getToolboxElement(); var blocks = toolbox.getElementsByTagName('block'); for (var i = 0, block; block = blocks[i]; i++) { prototypes.push(block.getAttribute('type')); From 1aecb78dd18ac84c499225aaf8e4c72430eca047 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Thu, 9 Jun 2016 02:35:32 -0700 Subject: [PATCH 05/22] Fix issue #406 by calling resize from the keypress handler on text inputs. (#408) --- core/field_textinput.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/field_textinput.js b/core/field_textinput.js index 0781e1c04..994db7263 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -198,6 +198,7 @@ Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(e) { this.sourceBlock_.render(); } this.resizeEditor_(); + Blockly.svgResize(this.sourceBlock_.workspace); }; /** From 509e158c0b0ee712ad4b47e17f8e8f26da6a485c Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Thu, 9 Jun 2016 16:53:10 -0700 Subject: [PATCH 06/22] Remove shadow blocks from Accessible Blockly demo. Update README. --- accessible/README | 63 ++++++++++++++++----- demos/accessible/index.html | 110 ++++++++++++++++++------------------ 2 files changed, 103 insertions(+), 70 deletions(-) diff --git a/accessible/README b/accessible/README index 088905570..21c2e2025 100644 --- a/accessible/README +++ b/accessible/README @@ -1,19 +1,52 @@ Accessible Blockly -======= +================== -Google's Blockly is a web-based, visual programming editor: accessible to blind users. +Google's Blockly is a web-based, visual programming editor that is accessible +to blind users. -What Does Accessible Blockly Do? +The code in this directory renders a version of the Blockly toolbox and +workspace that is fully keyboard-navigable, and compatible with JAWS/NVDA +screen readers on Firefox for Windows. (We chose this combination because JAWS +and NVDA are the most robust screen readers, and are compatible with many more +aria tags than other screen readers.) + +In the future, Accessible Blockly may be modified to suit accessibility needs +other than visual impairments. Note that deaf users are expected to continue +using Blockly over Accessible Blockly. + + +Using Accessible Blockly in Your Web App +---------------------------------------- +The demo at blockly/demos/accessible covers the absolute minimum required to +import Accessible Blockly into your web app. You will need to import the files +in the same order as in the demo: utils.service.js will need to be the first +Angular file imported. + +When the DOMContentLoaded event fires, call ng.platform.browser.bootstrap() on +the main component to be loaded. This will usually be blocklyApp.AppView, but +if you have another component that wraps it, use that one instead. + + +Customizing the Toolbar +----------------------- +The Accessible Blockly workspace comes with a customizable toolbar. + +To customize the toolbar, you will need to declare an ACCESSIBLE_GLOBALS object +in the global scope that looks like this: + + var ACCESSIBLE_GLOBALS = { + toolbarButtonConfig: [] + }; + +The value corresponding to 'toolbarButtonConfig' can be modified by adding +objects representing buttons on the toolbar. Each of these objects should have +two keys: + + - 'text' (the text to display on the button) + - 'action' (the function that gets run when the button is clicked) + + +Limitations ----------- -* renders a version of the blockly toolbox and workspace that is easily navigable using solely the keyboard -* renders a version of the blockly toolbox and workspace that is compatible with JAWS/NVDA screen readers on Firefox for Windows. - * Accessible Blockly will hopefully be modified to work well with most other screen readers, however JAWS and NVDA are the most robust screen readers and are compatible with many more aria tags than other screen readers. Therefore it was easiest to first be compatible with them. -* Accessible Blockly will be modified to suit accessibility needs other than visual impairments as well. Deaf users will be expected to continue using Blockly over Accessible Blockly. - -Use Accessible Blockly in Your Web App ------------ -1. See the basic demo under blockly/demos/accessible. This covers the absolute minimum required to import Accessible Blockly into your own web app. -2. You will need to import the files in the same order as in the demo: utils.service.js will need to be the first Angular file imported. -3. When the DOMContentLoaded event fires, call ng.platform.browser.bootstrap() on the main component to be loaded. This will usually be blocklyApp.AppView, but if you have another component that wraps it, use that one instead. -4. If you want to customize the toolbar, you will need an ACCESSIBLE_GLOBALS object in the global scope. It should have a 'toolbarButtonConfig' key, whose value is an Array. Each element in this array should be an Object with two keys: 'text' (the text to display on the button) and 'action' (the function that gets run when the button is clicked). -5. Note that we do not support having multiple Accessible Blockly apps in a single webpage. +- We do not support having multiple Accessible Blockly apps in a single webpage. +- Accessible Blockly does not support the use of shadow blocks. diff --git a/demos/accessible/index.html b/demos/accessible/index.html index 5cd2b94af..d3f3a1b52 100644 --- a/demos/accessible/index.html +++ b/demos/accessible/index.html @@ -75,28 +75,28 @@ - + 10 - + - + 1 - + - + 10 - + - + 1 - + @@ -106,92 +106,92 @@ - + 1 - + - + 1 - + - + 9 - + - + 45 - + - + 0 - + - + 1 - + - + 3.1 - + - + 64 - + - + 10 - + - + 50 - + - + 1 - + - + 100 - + - + 1 - + - + 100 - + @@ -201,21 +201,21 @@ - + - + abc - + - + - + @@ -225,9 +225,9 @@ - + abc - + @@ -246,30 +246,30 @@ - + abc - + - + abc - + - + abc - + - + abc - + @@ -280,9 +280,9 @@ - + 5 - + @@ -317,9 +317,9 @@ - + , - + From 652e57bc3842aea064da03d4b45929884e972e9f Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Thu, 9 Jun 2016 17:26:23 -0700 Subject: [PATCH 07/22] Generate for loops on one line. --- demos/blockfactory/factory.js | 2 +- generators/dart/loops.js | 8 ++++---- generators/javascript/loops.js | 8 ++++---- generators/php/loops.js | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/demos/blockfactory/factory.js b/demos/blockfactory/factory.js index 375ad1386..06eca710c 100644 --- a/demos/blockfactory/factory.js +++ b/demos/blockfactory/factory.js @@ -656,7 +656,7 @@ function updatePreview() { if (format == 'JSON') { var json = JSON.parse(code); - Blockly.Blocks[json.id || UNNAMED] = { + Blockly.Blocks[json.type || UNNAMED] = { init: function() { this.jsonInit(json); } diff --git a/generators/dart/loops.js b/generators/dart/loops.js index 2be30cc21..0dd7c0ce5 100644 --- a/generators/dart/loops.js +++ b/generators/dart/loops.js @@ -128,11 +128,11 @@ Blockly.Dart['controls_for'] = function(block) { code += 'if (' + startVar + ' > ' + endVar + ') {\n'; code += Blockly.Dart.INDENT + incVar + ' = -' + incVar + ';\n'; code += '}\n'; - code += 'for (' + variable0 + ' = ' + startVar + ';\n' + - ' ' + incVar + ' >= 0 ? ' + + code += 'for (' + variable0 + ' = ' + startVar + '; ' + + incVar + ' >= 0 ? ' + variable0 + ' <= ' + endVar + ' : ' + - variable0 + ' >= ' + endVar + ';\n' + - ' ' + variable0 + ' += ' + incVar + ') {\n' + + variable0 + ' >= ' + endVar + '; ' + + variable0 + ' += ' + incVar + ') {\n' + branch + '}\n'; } return code; diff --git a/generators/javascript/loops.js b/generators/javascript/loops.js index 944580403..1a2ade593 100644 --- a/generators/javascript/loops.js +++ b/generators/javascript/loops.js @@ -129,11 +129,11 @@ Blockly.JavaScript['controls_for'] = function(block) { code += 'if (' + startVar + ' > ' + endVar + ') {\n'; code += Blockly.JavaScript.INDENT + incVar + ' = -' + incVar + ';\n'; code += '}\n'; - code += 'for (' + variable0 + ' = ' + startVar + ';\n' + - ' ' + incVar + ' >= 0 ? ' + + code += 'for (' + variable0 + ' = ' + startVar + '; ' + + incVar + ' >= 0 ? ' + variable0 + ' <= ' + endVar + ' : ' + - variable0 + ' >= ' + endVar + ';\n' + - ' ' + variable0 + ' += ' + incVar + ') {\n' + + variable0 + ' >= ' + endVar + '; ' + + variable0 + ' += ' + incVar + ') {\n' + branch + '}\n'; } return code; diff --git a/generators/php/loops.js b/generators/php/loops.js index 4a905b4e0..f306b1053 100644 --- a/generators/php/loops.js +++ b/generators/php/loops.js @@ -128,11 +128,11 @@ Blockly.PHP['controls_for'] = function(block) { code += 'if (' + startVar + ' > ' + endVar + ') {\n'; code += Blockly.PHP.INDENT + incVar + ' = -' + incVar + ';\n'; code += '}\n'; - code += 'for (' + variable0 + ' = ' + startVar + ';\n' + - ' ' + incVar + ' >= 0 ? ' + + code += 'for (' + variable0 + ' = ' + startVar + '; ' + + incVar + ' >= 0 ? ' + variable0 + ' <= ' + endVar + ' : ' + - variable0 + ' >= ' + endVar + ';\n' + - ' ' + variable0 + ' += ' + incVar + ') {\n' + + variable0 + ' >= ' + endVar + '; ' + + variable0 + ' += ' + incVar + ') {\n' + branch + '}\n'; } return code; From 50ddbbaee4cd0137a41ba9a35a63bca12d5fa944 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Thu, 9 Jun 2016 17:57:44 -0700 Subject: [PATCH 08/22] Introduce a common translation pipe; remove local stringMap attributes. Fix variable name error in paste functions. Minor linting. --- accessible/appview.component.js | 40 ++--- accessible/clipboard.service.js | 8 +- accessible/fieldview.component.js | 6 +- accessible/toolbox_treeview.component.js | 164 +++++++++--------- accessible/translate.pipe.js | 34 ++++ accessible/workspace_treeview.component.js | 189 ++++++++++----------- accessible/workspaceview.component.js | 44 ++--- demos/accessible/index.html | 1 + 8 files changed, 249 insertions(+), 237 deletions(-) create mode 100644 accessible/translate.pipe.js diff --git a/accessible/appview.component.js b/accessible/appview.component.js index aa8e620f3..792726654 100644 --- a/accessible/appview.component.js +++ b/accessible/appview.component.js @@ -35,43 +35,31 @@ blocklyApp.AppView = ng.core
- {{stringMap['TOOLBOX_LOAD']}} + {{'TOOLBOX_LOAD'|translate}} - {{stringMap['WORKSPACE_LOAD']}} + {{'WORKSPACE_LOAD'|translate}}
- - - - - - - - - + + + + + + + + + + `, directives: [blocklyApp.ToolboxView, blocklyApp.WorkspaceView], + pipes: [blocklyApp.TranslatePipe], // ClipboardService declared here so that all components are using the same // instance of the clipboard. // https://www.sitepoint.com/angular-2-components-providers-classes-factories-values/ providers: [blocklyApp.ClipboardService] }) .Class({ - constructor: function() { - this.stringMap = { - ['TOOLBOX_LOAD']: Blockly.Msg.TOOLBOX_LOAD_MSG, - ['WORKSPACE_LOAD']: Blockly.Msg.WORKSPACE_LOAD_MSG, - ['BLOCK_SUMMARY']: Blockly.Msg.BLOCK_SUMMARY, - ['BLOCK_ACTION_LIST']: Blockly.Msg.BLOCK_ACTION_LIST, - ['OPTION_LIST']: Blockly.Msg.OPTION_LIST, - ['ARGUMENT_OPTIONS_LIST']: Blockly.Msg.ARGUMENT_OPTIONS_LIST, - ['UNAVAILABLE']: Blockly.Msg.UNAVAILABLE, - ['BUTTON']: Blockly.Msg.BUTTON, - ['TEXT']: Blockly.Msg.TEXT, - ['ARGUMENT_BLOCK_ACTION_LIST']: Blockly.Msg.ARGUMENT_BLOCK_ACTION_LIST, - ['ARGUMENT_INPUT']: Blockly.Msg.ARGUMENT_INPUT - }; - } + constructor: function() {} }); diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index 2caf8d627..747baf0e2 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -61,7 +61,9 @@ blocklyApp.ClipboardService = ng.core connection.connect(reconstitutedBlock.outputConnection); } blocklyApp.debug && console.log('paste'); - alert(Blockly.Msg.PASTED_BLOCK_FROM_CLIPBOARD_MSG + block.toString()); + alert( + Blockly.Msg.PASTED_BLOCK_FROM_CLIPBOARD_MSG + + reconstitutedBlock.toString()); }, pasteToMarkedConnection: function(block, announce) { var xml = Blockly.Xml.blockToDom(block); @@ -72,7 +74,9 @@ blocklyApp.ClipboardService = ng.core reconstitutedBlock.previousConnection); blocklyApp.debug && console.log('paste to marked connection'); if (announce) { - alert(Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG + block.toString()); + alert( + Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG + + reconstitutedBlock.toString()); } }, markConnection: function(connection) { diff --git a/accessible/fieldview.component.js b/accessible/fieldview.component.js index 7e68cd9d7..d90ad1d4a 100644 --- a/accessible/fieldview.component.js +++ b/accessible/fieldview.component.js @@ -36,7 +36,7 @@ blocklyApp.FieldView = ng.core
  • - +
    1. `, inputs: ['field', 'level', 'index', 'parentId'], + pipes: [blocklyApp.TranslatePipe], providers: [blocklyApp.TreeService, blocklyApp.UtilsService] }) .Class({ @@ -72,9 +73,6 @@ blocklyApp.FieldView = ng.core }; this.treeService = _treeService; this.utilsService = _utilsService; - this.stringMap = { - 'CURRENT_ARGUMENT_VALUE': Blockly.Msg.CURRENT_ARGUMENT_VALUE - }; }], ngOnInit: function() { var elementsNeedingIds = this.generateElementNames(this.field); diff --git a/accessible/toolbox_treeview.component.js b/accessible/toolbox_treeview.component.js index dca39da82..1e50ca196 100644 --- a/accessible/toolbox_treeview.component.js +++ b/accessible/toolbox_treeview.component.js @@ -28,83 +28,84 @@ blocklyApp.ToolboxTreeView = ng.core .Component({ selector: 'toolbox-tree-view', template: ` -
    2. - {{setActiveDesc(parentList)}} - -
        -
      1. - -
          -
        1. - -
        2. -
        3. - -
        4. -
        5. - -
        6. -
        -
      2. -
        - - - - -
      3. - - +
      4. + {{setActiveDesc(parentList)}} + +
          +
        1. + +
            +
          1. +
          2. -
      5. -
      -
    3. - - +
    4. + +
    5. +
    6. + +
    7. +
    +
  • +
    + + + + +
  • + + +
  • +
    + + + + `, - directives: [ng.core.forwardRef( - function() { return blocklyApp.ToolboxTreeView; }), - blocklyApp.FieldView], - inputs: ['block', 'displayBlockMenu', 'level', 'index', 'tree', - 'noCategories'], + directives: [ng.core.forwardRef(function() { + return blocklyApp.ToolboxTreeView; + }), blocklyApp.FieldView], + inputs: [ + 'block', 'displayBlockMenu', 'level', 'index', 'tree', 'noCategories'], + pipes: [blocklyApp.TranslatePipe], providers: [blocklyApp.TreeService, blocklyApp.UtilsService] }) .Class({ @@ -115,12 +116,6 @@ blocklyApp.ToolboxTreeView = ng.core this.clipboardService = _clipboardService; this.treeService = _treeService; this.utilsService = _utilsService; - this.stringMap = { - 'BLOCK_ACTION_LIST': Blockly.Msg.BLOCK_ACTION_LIST, - 'COPY_TO_CLIPBOARD': Blockly.Msg.COPY_TO_CLIPBOARD, - 'COPY_TO_WORKSPACE': Blockly.Msg.COPY_TO_WORKSPACE, - 'COPY_TO_MARKED_SPOT': Blockly.Msg.COPY_TO_MARKED_SPOT - }; }], ngOnInit: function() { var elementsNeedingIds = ['blockSummaryLabel']; @@ -130,8 +125,7 @@ blocklyApp.ToolboxTreeView = ng.core 'blockCopyButton', 'sendToSelected', 'sendToSelectedButton']); } for (var i = 0; i < this.block.inputList.length; i++){ - elementsNeedingIds.push('listItem' + i); - elementsNeedingIds.push('listItem' + i + 'Label') + elementsNeedingIds.push('listItem' + i, 'listItem' + i + 'Label') } this.idMap = this.utilsService.generateIds(elementsNeedingIds); if (this.index == 0 && this.noCategories) { @@ -145,8 +139,8 @@ blocklyApp.ToolboxTreeView = ng.core isCompatible); }, generateAriaLabelledByAttr: function() { - return this.utilsService.generateAriaLabelledByAttr.apply(this, - arguments); + return this.utilsService.generateAriaLabelledByAttr.apply( + this, arguments); }, setActiveDesc: function(parentList) { // If this is the first child of the toolbox and the diff --git a/accessible/translate.pipe.js b/accessible/translate.pipe.js new file mode 100644 index 000000000..c15dec898 --- /dev/null +++ b/accessible/translate.pipe.js @@ -0,0 +1,34 @@ +/** + * AccessibleBlockly + * + * Copyright 2016 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 Angular2 Pipe for internationalizing Blockly message strings. + * @author sll@google.com (Sean Lip) + */ + +blocklyApp.TranslatePipe = ng.core + .Pipe({ + name: 'translate' + }) + .Class({ + constructor: function() {}, + transform: function(messageId) { + return Blockly.Msg[messageId]; + } + }); diff --git a/accessible/workspace_treeview.component.js b/accessible/workspace_treeview.component.js index db0001320..937e666c1 100644 --- a/accessible/workspace_treeview.component.js +++ b/accessible/workspace_treeview.component.js @@ -28,119 +28,109 @@ blocklyApp.WorkspaceTreeView = ng.core .Component({ selector: 'tree-view', template: ` -
  • - {{checkParentList(parentList)}} - -
      -
    1. - +
    2. + {{checkParentList(parentList)}} + +
        +
      1. + +
          +
        1. + +
        2. +
        3. + +
        4. +
        5. + +
        6. +
        7. + +
        8. +
        9. + +
        10. +
        11. + +
        12. +
        13. + +
        14. +
        15. + +
        16. +
        +
      2. +
        + + +
      3. + +
          -
        1. - +
        2. +
        3. -
        4. - -
        5. -
        6. - -
        7. -
        8. - -
        9. -
        10. - -
        11. -
        12. - -
        13. -
        14. - -
        15. -
        16. - +
      4. -
        - - -
      5. - - -
          -
        1. - -
        2. -
        3. - -
        4. -
        -
      6. -
        -
      -
    3. - - + +
    +
  • + + `, directives: [ng.core.forwardRef( function() { return blocklyApp.WorkspaceTreeView; }), blocklyApp.FieldView], inputs: ['block', 'isTopBlock', 'topBlockIndex', 'level', 'parentId'], + pipes: [blocklyApp.TranslatePipe], providers: [blocklyApp.TreeService, blocklyApp.UtilsService], }) .Class({ - constructor: [blocklyApp.ClipboardService, blocklyApp.TreeService, - blocklyApp.UtilsService, - function(_clipboardService, _treeService, _utilsService) { + constructor: [ + blocklyApp.ClipboardService, blocklyApp.TreeService, blocklyApp.UtilsService, + function(_clipboardService, _treeService, _utilsService) { this.infoBlocks = Object.create(null); this.clipboardService = _clipboardService; this.treeService = _treeService; this.utilsService = _utilsService; - this.stringMap = { - 'BLOCK_ACTION_LIST': Blockly.Msg.BLOCK_ACTION_LIST, - 'PASTE': Blockly.Msg.PASTE, - 'PASTE_ABOVE': Blockly.Msg.PASTE_ABOVE, - 'PASTE_BELOW': Blockly.Msg.PASTE_BELOW, - 'MARK_THIS_SPOT': Blockly.Msg.MARK_THIS_SPOT, - 'MARK_SPOT_ABOVE': Blockly.Msg.MARK_SPOT_ABOVE, - 'MARK_SPOT_BELOW': Blockly.Msg.MARK_SPOT_BELOW, - 'CUT_BLOCK': Blockly.Msg.CUT_BLOCK, - 'COPY_BLOCK': Blockly.Msg.COPY_BLOCK, - 'MOVE_TO_MARKED_SPOT': Blockly.Msg.MOVE_TO_MARKED_SPOT, - 'DELETE': Blockly.Msg.DELETE - }; }], deleteBlock: function(block) { // If this is the top block, we should shift focus to the previous tree @@ -151,16 +141,17 @@ blocklyApp.WorkspaceTreeView = ng.core break; } } - // If this is not the top block, we should change the active descendant of the tree. + // If this is not the top block, we should change the active descendant + // of the tree. block.dispose(true); }, getMarkedBlockCompatibilityHTMLText: function(isCompatible) { return this.utilsService.getMarkedBlockCompatibilityHTMLText(isCompatible); }, generateAriaLabelledByAttr: function() { - return this.utilsService.generateAriaLabelledByAttr.apply(this, - arguments); + return this.utilsService.generateAriaLabelledByAttr.apply( + this, arguments); }, ngOnInit: function() { var elementsNeedingIds = ['blockSummary', 'listItem', 'label', diff --git a/accessible/workspaceview.component.js b/accessible/workspaceview.component.js index ae0fccfbe..281b87b55 100644 --- a/accessible/workspaceview.component.js +++ b/accessible/workspaceview.component.js @@ -26,30 +26,32 @@ blocklyApp.WorkspaceView = ng.core .Component({ selector: 'workspace-view', - viewInjector: [blocklyApp.ClipboardService], template: ` - -
    - - - - -
    -
    -
      - -
    -
    + +
    + + + + +
    +
    +
      + +
    +
    `, directives: [blocklyApp.WorkspaceTreeView], + pipes: [blocklyApp.TranslatePipe], providers: [blocklyApp.TreeService] }) .Class({ diff --git a/demos/accessible/index.html b/demos/accessible/index.html index d3f3a1b52..23d7a9724 100644 --- a/demos/accessible/index.html +++ b/demos/accessible/index.html @@ -20,6 +20,7 @@ + From ef417d7a6098df2c801d0c4f3aef5034f9246d83 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Thu, 9 Jun 2016 18:04:14 -0700 Subject: [PATCH 09/22] Fix precedence on isIndex blocks. --- generators/dart/lists.js | 8 ++++---- generators/dart/text.js | 2 +- generators/javascript/lists.js | 2 +- generators/javascript/text.js | 2 +- generators/php/lists.js | 8 ++++---- generators/php/text.js | 3 +-- generators/python/text.js | 2 +- 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/generators/dart/lists.js b/generators/dart/lists.js index 8b0f8553b..24fcd2243 100644 --- a/generators/dart/lists.js +++ b/generators/dart/lists.js @@ -80,7 +80,7 @@ Blockly.Dart['lists_indexOf'] = function(block) { var argument1 = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [code, Blockly.Dart.ORDER_ADDITIVE]; }; Blockly.Dart['lists_getIndex'] = function(block) { @@ -318,10 +318,10 @@ Blockly.Dart['lists_sort'] = function(block) { var type = block.getFieldValue('TYPE'); var sortFunctionName = Blockly.Dart.provideFunction_( 'lists_sort', - ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(list, type, direction) {', ' var compareFuncs = {', - ' "NUMERIC": (a, b) => direction * a.compareTo(b),', + ' "NUMERIC": (a, b) => direction * a.compareTo(b),', ' "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()),', ' "IGNORE_CASE": ', ' (a, b) => direction * ', @@ -332,7 +332,7 @@ Blockly.Dart['lists_sort'] = function(block) { ' list.sort(compare);', ' return list;', '}']); - return [sortFunctionName + '(' + listCode + ', ' + + return [sortFunctionName + '(' + listCode + ', ' + '"' + type + '", ' + direction + ')', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/text.js b/generators/dart/text.js index 6a06c8241..ba5a75cb0 100644 --- a/generators/dart/text.js +++ b/generators/dart/text.js @@ -90,7 +90,7 @@ Blockly.Dart['text_indexOf'] = function(block) { var argument1 = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [code, Blockly.Dart.ORDER_ADDITIVE]; }; Blockly.Dart['text_charAt'] = function(block) { diff --git a/generators/javascript/lists.js b/generators/javascript/lists.js index 2a06eab32..aa817f96f 100644 --- a/generators/javascript/lists.js +++ b/generators/javascript/lists.js @@ -88,7 +88,7 @@ Blockly.JavaScript['lists_indexOf'] = function(block) { var argument1 = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '[]'; var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; + return [code, Blockly.JavaScript.ORDER_ADDITION]; }; Blockly.JavaScript['lists_getIndex'] = function(block) { diff --git a/generators/javascript/text.js b/generators/javascript/text.js index 78e94ed16..86befb9ef 100644 --- a/generators/javascript/text.js +++ b/generators/javascript/text.js @@ -95,7 +95,7 @@ Blockly.JavaScript['text_indexOf'] = function(block) { var argument1 = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; + return [code, Blockly.JavaScript.ORDER_ADDITION]; }; Blockly.JavaScript['text_charAt'] = function(block) { diff --git a/generators/php/lists.js b/generators/php/lists.js index d8685ca0e..d7111b19e 100644 --- a/generators/php/lists.js +++ b/generators/php/lists.js @@ -104,7 +104,7 @@ Blockly.PHP['lists_indexOf'] = function(block) { [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($haystack, $needle) {', ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) return $index+1;', + ' if ($haystack[$index] == $needle) return $index + 1;', ' }', ' return 0;', '}']); @@ -116,7 +116,7 @@ Blockly.PHP['lists_indexOf'] = function(block) { '($haystack, $needle) {', ' $last = 0;', ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) $last = $index+1;', + ' if ($haystack[$index] == $needle) $last = $index + 1;', ' }', ' return $last;', '}']); @@ -364,7 +364,7 @@ Blockly.PHP['lists_sort'] = function(block) { var type = block.getFieldValue('TYPE'); var functionName = Blockly.PHP.provideFunction_( 'lists_sort', [ - 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list, $type, $direction) {', ' $sortCmpFuncs = array(', ' "NUMERIC" => "strnatcasecmp",', @@ -379,7 +379,7 @@ Blockly.PHP['lists_sort'] = function(block) { ' }', ' return $list2;', '}']); - var sortCode = functionName + + var sortCode = functionName + '(' + listCode + ', "' + type + '", ' + direction + ')'; return [sortCode, Blockly.PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/text.js b/generators/php/text.js index 561b4997c..3960ab011 100644 --- a/generators/php/text.js +++ b/generators/php/text.js @@ -103,7 +103,6 @@ Blockly.PHP['text_indexOf'] = function(block) { Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - var code = operator + '(' + argument1 + ', ' + argument0 + ') + 1'; var functionName = Blockly.PHP.provideFunction_( block.getFieldValue('END') == 'FIRST' ? @@ -113,7 +112,7 @@ Blockly.PHP['text_indexOf'] = function(block) { ' $pos = ' + operator + '($text, $search);', ' return $pos === false ? 0 : $pos + 1;', '}']); - code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/python/text.js b/generators/python/text.js index 7d3797b54..3ae0ffc8e 100644 --- a/generators/python/text.js +++ b/generators/python/text.js @@ -100,7 +100,7 @@ Blockly.Python['text_indexOf'] = function(block) { var argument1 = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_MEMBER) || '\'\''; var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Python.ORDER_MEMBER]; + return [code, Blockly.Python.ORDER_ADDITIVE]; }; Blockly.Python['text_charAt'] = function(block) { From c6517bc777debba5a18b4a6e80713781ccdf223b Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Thu, 9 Jun 2016 18:45:00 -0700 Subject: [PATCH 10/22] Remove unused functions and dependencies. --- accessible/fieldview.component.js | 4 +--- accessible/tree.service.js | 6 ------ accessible/workspace_treeview.component.js | 6 ------ 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/accessible/fieldview.component.js b/accessible/fieldview.component.js index d90ad1d4a..cdd52e117 100644 --- a/accessible/fieldview.component.js +++ b/accessible/fieldview.component.js @@ -66,12 +66,10 @@ blocklyApp.FieldView = ng.core providers: [blocklyApp.TreeService, blocklyApp.UtilsService] }) .Class({ - constructor: [blocklyApp.TreeService, blocklyApp.UtilsService, - function(_treeService, _utilsService) { + constructor: [blocklyApp.UtilsService, function(_utilsService) { this.optionText = { keys: [] }; - this.treeService = _treeService; this.utilsService = _utilsService; }], ngOnInit: function() { diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 27c38ce02..549df25db 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -36,12 +36,6 @@ blocklyApp.TreeService = ng.core // to shift focus back to the tree as a whole. this.previousKey_ = null; }, - createId: function(obj) { - if (obj && obj.id) { - return obj.id; - } - return 'blockly-' + Blockly.genUid(); - }, setActiveDesc: function(node, id) { blocklyApp.debug && console.log('setting active descendant for tree ' + id); this.activeDesc_[id] = node; diff --git a/accessible/workspace_treeview.component.js b/accessible/workspace_treeview.component.js index 937e666c1..e6f492cdb 100644 --- a/accessible/workspace_treeview.component.js +++ b/accessible/workspace_treeview.component.js @@ -203,12 +203,6 @@ blocklyApp.WorkspaceTreeView = ng.core this.treeService.updateSelectedNode(parentList, tree, false); } }, - setId: function(block) { - if (this.isTopBlock) { - return this.parentId + '-node0'; - } - return this.treeService.createId(block); - }, sendToSelected: function(block) { if (this.clipboardService) { this.clipboardService.pasteToMarkedConnection(block, false); From 11b49930f9837e7b461d5d49201b0c68aa6e02af Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Thu, 9 Jun 2016 19:10:34 -0700 Subject: [PATCH 11/22] Remove the unnecessary construction of new services. --- accessible/appview.component.js | 6 +++--- accessible/clipboard.service.js | 1 + accessible/fieldview.component.js | 3 +-- accessible/toolbox_treeview.component.js | 11 ++++++----- accessible/tree.service.js | 2 ++ accessible/utils.service.js | 7 +++++-- accessible/workspace_treeview.component.js | 7 ++++--- 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/accessible/appview.component.js b/accessible/appview.component.js index 792726654..6a3cc87bd 100644 --- a/accessible/appview.component.js +++ b/accessible/appview.component.js @@ -55,10 +55,10 @@ blocklyApp.AppView = ng.core `, directives: [blocklyApp.ToolboxView, blocklyApp.WorkspaceView], pipes: [blocklyApp.TranslatePipe], - // ClipboardService declared here so that all components are using the same - // instance of the clipboard. + // The clipboard and utils services are declared here, so that all + // components in the application use the same instance of the service. // https://www.sitepoint.com/angular-2-components-providers-classes-factories-values/ - providers: [blocklyApp.ClipboardService] + providers: [blocklyApp.ClipboardService, blocklyApp.UtilsService] }) .Class({ constructor: function() {} diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index 747baf0e2..25508fc6c 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -25,6 +25,7 @@ blocklyApp.ClipboardService = ng.core .Class({ constructor: function() { + blocklyApp.debug && console.log('Clipboard service constructed'); this.clipboardBlockXml_ = null; this.clipboardBlockSuperiorConnection_ = null; this.clipboardBlockNextConnection_ = null; diff --git a/accessible/fieldview.component.js b/accessible/fieldview.component.js index cdd52e117..114a256b6 100644 --- a/accessible/fieldview.component.js +++ b/accessible/fieldview.component.js @@ -62,8 +62,7 @@ blocklyApp.FieldView = ng.core `, inputs: ['field', 'level', 'index', 'parentId'], - pipes: [blocklyApp.TranslatePipe], - providers: [blocklyApp.TreeService, blocklyApp.UtilsService] + pipes: [blocklyApp.TranslatePipe] }) .Class({ constructor: [blocklyApp.UtilsService, function(_utilsService) { diff --git a/accessible/toolbox_treeview.component.js b/accessible/toolbox_treeview.component.js index 1e50ca196..0411c3fba 100644 --- a/accessible/toolbox_treeview.component.js +++ b/accessible/toolbox_treeview.component.js @@ -105,13 +105,14 @@ blocklyApp.ToolboxTreeView = ng.core }), blocklyApp.FieldView], inputs: [ 'block', 'displayBlockMenu', 'level', 'index', 'tree', 'noCategories'], - pipes: [blocklyApp.TranslatePipe], - providers: [blocklyApp.TreeService, blocklyApp.UtilsService] + pipes: [blocklyApp.TranslatePipe] }) .Class({ - constructor: [blocklyApp.ClipboardService, blocklyApp.TreeService, - blocklyApp.UtilsService, - function(_clipboardService, _treeService, _utilsService) { + constructor: [ + blocklyApp.ClipboardService, blocklyApp.TreeService, blocklyApp.UtilsService, + function(_clipboardService, _treeService, _utilsService) { + // ClipboardService and UtilsService are app-wide singleton services. + // TreeService is from the parent ToolboxView component. this.infoBlocks = Object.create(null); this.clipboardService = _clipboardService; this.treeService = _treeService; diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 549df25db..6682f66af 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -19,6 +19,8 @@ /** * @fileoverview Angular2 Service that handles all tree keyboard navigation. + * A separate TreeService is constructed for each tree in the application. + * * @author madeeha@google.com (Madeeha Ghori) */ diff --git a/accessible/utils.service.js b/accessible/utils.service.js index fddb47ded..569dc7aa1 100644 --- a/accessible/utils.service.js +++ b/accessible/utils.service.js @@ -18,8 +18,10 @@ */ /** - * @fileoverview Angular2 Service with functions required by multiple - * components. + * @fileoverview Angular2 utility service for multiple components. All + * functions in this service should be stateless, since this is a singleton + * service that is used for the entire application. + * * @author madeeha@google.com (Madeeha Ghori) */ @@ -28,6 +30,7 @@ var blocklyApp = {}; blocklyApp.UtilsService = ng.core .Class({ constructor: function() { + blocklyApp.debug && console.log('Utils service constructed'); }, generateUniqueId: function() { return 'blockly-' + Blockly.genUid(); diff --git a/accessible/workspace_treeview.component.js b/accessible/workspace_treeview.component.js index e6f492cdb..3740e4dec 100644 --- a/accessible/workspace_treeview.component.js +++ b/accessible/workspace_treeview.component.js @@ -117,11 +117,12 @@ blocklyApp.WorkspaceTreeView = ng.core [level]="level"> `, - directives: [ng.core.forwardRef( - function() { return blocklyApp.WorkspaceTreeView; }), blocklyApp.FieldView], + directives: [ng.core.forwardRef(function() { + return blocklyApp.WorkspaceTreeView; + }), blocklyApp.FieldView], inputs: ['block', 'isTopBlock', 'topBlockIndex', 'level', 'parentId'], pipes: [blocklyApp.TranslatePipe], - providers: [blocklyApp.TreeService, blocklyApp.UtilsService], + providers: [blocklyApp.TreeService], }) .Class({ constructor: [ From 0c92ce911ff2d5ebe7721fc55f96140ded34a9ec Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Thu, 9 Jun 2016 22:14:55 -0700 Subject: [PATCH 12/22] Fix sort block in JS to satisfy tests. --- generators/javascript/lists.js | 6 ++-- tests/generators/index.html | 11 +++++-- tests/generators/lists.xml | 56 ++++++++++++++-------------------- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/generators/javascript/lists.js b/generators/javascript/lists.js index aa817f96f..4bb6aac20 100644 --- a/generators/javascript/lists.js +++ b/generators/javascript/lists.js @@ -314,10 +314,10 @@ Blockly.JavaScript['lists_sort'] = function(block) { ' "NUMERIC": function(a, b) {', ' return parseFloat(a) - parseFloat(b); },', ' "TEXT": function(a, b) {', - ' return a.toString().localeCompare(b.toString(), "en"); },', + ' return a.toString() > b.toString() ? 1 : -1; },', ' "IGNORE_CASE": function(a, b) {', - ' return a.toString().localeCompare(b.toString(), "en",', - ' {"sensitivity": "base"}); },', + ' return a.toString().toLowerCase() > ' + + 'b.toString().toLowerCase() ? 1 : -1; },', ' };', ' var compare = compareFuncs[type];', ' return function(a, b) { return compare(a, b) * direction; }', diff --git a/tests/generators/index.html b/tests/generators/index.html index f10e5493a..5ae75e553 100644 --- a/tests/generators/index.html +++ b/tests/generators/index.html @@ -77,8 +77,15 @@ var workspace = null; function start() { workspace = Blockly.inject('blocklyDiv', - {media: '../../media/', - toolbox: document.getElementById('toolbox')}); + {grid: + {spacing: 25, + length: 3, + colour: '#ccc', + snap: true}, + media: '../../media/', + toolbox: document.getElementById('toolbox'), + zoom: {controls: true, wheel: true} + }); } function loadXml() { diff --git a/tests/generators/lists.xml b/tests/generators/lists.xml index 374185fa6..d803a14ad 100644 --- a/tests/generators/lists.xml +++ b/tests/generators/lists.xml @@ -2299,7 +2299,7 @@ - Klingon + klingon @@ -2329,12 +2329,12 @@ - Klingon + Vulcan - Vulcan + klingon @@ -2356,15 +2356,15 @@ 1 - + - borg + Vulcan - Klingon + klingon @@ -2372,11 +2372,6 @@ Borg - - - Vulcan - - @@ -2391,23 +2386,18 @@ - + - - borg - - - Borg - + - Klingon + klingon - + Vulcan @@ -2433,18 +2423,18 @@ - - 0-Borg + + 8 - - Vulcan + + 18 - - 1-Klingon + + -1 @@ -2463,18 +2453,18 @@ - - Vulcan + + 18 - - 1-Klingon + + 8 - - 0-Borg + + -1 @@ -2484,4 +2474,4 @@ - \ No newline at end of file + From f64f6f7d6f6065e7e271bbf77ec070d1e04a7af2 Mon Sep 17 00:00:00 2001 From: picklesrus Date: Fri, 10 Jun 2016 13:46:44 -0700 Subject: [PATCH 13/22] Trigger a contents resize in block's moveBy. (#422) This fixes #420 but and it also fixes some other similar problems with copy/paste and other users of moveBy. --- core/block_svg.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/block_svg.js b/core/block_svg.js index c84220606..094373973 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -343,6 +343,7 @@ Blockly.BlockSvg.prototype.moveBy = function(dx, dy) { 'translate(' + (xy.x + dx) + ',' + (xy.y + dy) + ')'); this.moveConnections_(dx, dy); event.recordNew(); + Blockly.resizeSvgContents(this.workspace); Blockly.Events.fire(event); }; From 11c00783f240a6377c5edb04735641256c296f2a Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Fri, 10 Jun 2016 17:36:39 -0700 Subject: [PATCH 14/22] Consolidate the usages of the 'blockly-disabled' label. --- accessible/clipboard.service.js | 15 ++-- accessible/fieldview.component.js | 21 +++-- accessible/toolbox_treeview.component.js | 14 ++-- accessible/toolboxview.component.js | 2 +- accessible/utils.service.js | 19 ++--- accessible/workspace_treeview.component.js | 89 +++++++++++----------- accessible/workspaceview.component.js | 16 ++-- 7 files changed, 78 insertions(+), 98 deletions(-) diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index 25508fc6c..29f0baaed 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -111,14 +111,11 @@ blocklyApp.ClipboardService = ng.core this.isCompatibleWithConnection_( blockConnection, this.markedConnection_); }, - getClipboardCompatibilityHTMLText: function(connection) { - if (this.isCompatibleWithConnection_(connection, - this.clipboardBlockSuperiorConnection_) || - this.isCompatibleWithConnection_(connection, - this.clipboardBlockNextConnection_)){ - return ''; - } else { - return 'blockly-disabled'; - } + isClipboardCompatibleWithConnection: function(connection) { + var superiorConnection = this.clipboardBlockSuperiorConnection_; + var nextConnection = this.clipboardBlockNextConnection_; + return + this.isCompatibleWithConnection_(connection, superiorConnection) || + this.isCompatibleWithConnection_(connection, nextConnection); } }); diff --git a/accessible/fieldview.component.js b/accessible/fieldview.component.js index 114a256b6..a53995556 100644 --- a/accessible/fieldview.component.js +++ b/accessible/fieldview.component.js @@ -30,12 +30,12 @@ blocklyApp.FieldView = ng.core template: `
  • + [attr.aria-level]="level" aria-selected=false>
  • -
  • + [attr.aria-level]="level" aria-selected=false>
  • -
  • - // Checkboxes not currently supported. +
  • + // Checkboxes are not currently supported.
  • -
  • @@ -135,13 +135,9 @@ blocklyApp.ToolboxTreeView = ng.core this.idMap['parentList'] = this.utilsService.generateUniqueId(); } }, - getMarkedBlockCompatibilityHTMLText: function(isCompatible) { - return this.utilsService.getMarkedBlockCompatibilityHTMLText( - isCompatible); - }, - generateAriaLabelledByAttr: function() { - return this.utilsService.generateAriaLabelledByAttr.apply( - this, arguments); + generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { + return this.utilsService.generateAriaLabelledByAttr( + mainLabel, secondLabel, isDisabled); }, setActiveDesc: function(parentList) { // If this is the first child of the toolbox and the diff --git a/accessible/toolboxview.component.js b/accessible/toolboxview.component.js index bb33692e3..523eeb331 100644 --- a/accessible/toolboxview.component.js +++ b/accessible/toolboxview.component.js @@ -67,7 +67,7 @@ blocklyApp.ToolboxView = ng.core `, directives: [blocklyApp.ToolboxTreeView], - providers: [blocklyApp.TreeService, blocklyApp.UtilsService], + providers: [blocklyApp.TreeService], }) .Class({ constructor: [ diff --git a/accessible/utils.service.js b/accessible/utils.service.js index 569dc7aa1..7a1e0d273 100644 --- a/accessible/utils.service.js +++ b/accessible/utils.service.js @@ -42,8 +42,12 @@ blocklyApp.UtilsService = ng.core } return idMap; }, - generateAriaLabelledByAttr: function() { - return Array.from(arguments).join(' ').trim(); + generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { + var attrValue = mainLabel + ' ' + secondLabel; + if (isDisabled) { + attrValue += ' blockly-disabled'; + } + return attrValue; }, getInputTypeLabel: function(connection) { // Returns an upper case string in the case of official input type names. @@ -63,16 +67,5 @@ blocklyApp.UtilsService = ng.core } else { return Blockly.Msg.VALUE; } - }, - getMarkedBlockCompatibilityHTMLText: function(isCompatible) { - if (isCompatible) { - // undefined will result in the - // 'copy to marked block' option being ENABLED. - return ''; - } else { - // Anything will result in the - // 'copy to marked block' option being DISABLED. - return 'blockly-disabled'; - } } }); diff --git a/accessible/workspace_treeview.component.js b/accessible/workspace_treeview.component.js index 3740e4dec..fc052be10 100644 --- a/accessible/workspace_treeview.component.js +++ b/accessible/workspace_treeview.component.js @@ -50,34 +50,40 @@ blocklyApp.WorkspaceTreeView = ng.core
  • + [disabled]="!hasNextConnection(block) || !isCompatibleWithClipboard(block.nextConnection)"> + {{'PASTE_BELOW'|translate}} +
  • + [disabled]="!hasPreviousConnection(block) || !isCompatibleWithClipboard(block.previousConnection)"> + {{'PASTE_ABOVE'|translate}} +
  • + [disabled]="!hasNextConnection(block)"> + {{'MARK_SPOT_BELOW'|translate}} +
  • + [disabled]="!hasPreviousConnection(block)">{{'MARK_SPOT_ABOVE'|translate}}
  • + [disabled]="!clipboardService.isBlockCompatibleWithMarkedConnection(block)">{{'MOVE_TO_MARKED_SPOT'|translate}}
  • {{'MARK_THIS_SPOT'|translate}}
  • + [disabled]="!isCompatibleWithClipboard(inputBlock.connection)"> + {{'PASTE'|translate}} +
  • @@ -133,27 +141,6 @@ blocklyApp.WorkspaceTreeView = ng.core this.treeService = _treeService; this.utilsService = _utilsService; }], - deleteBlock: function(block) { - // If this is the top block, we should shift focus to the previous tree - var topBlocks = blocklyApp.workspace.topBlocks_; - for (var i = 0; i < topBlocks.length; i++) { - if (topBlocks[i].id == block.id) { - this.treeService.goToPreviousTree(this.parentId); - break; - } - } - - // If this is not the top block, we should change the active descendant - // of the tree. - block.dispose(true); - }, - getMarkedBlockCompatibilityHTMLText: function(isCompatible) { - return this.utilsService.getMarkedBlockCompatibilityHTMLText(isCompatible); - }, - generateAriaLabelledByAttr: function() { - return this.utilsService.generateAriaLabelledByAttr.apply( - this, arguments); - }, ngOnInit: function() { var elementsNeedingIds = ['blockSummary', 'listItem', 'label', 'cutListItem', 'cutButton', 'copyListItem', 'copyButton', @@ -172,6 +159,26 @@ blocklyApp.WorkspaceTreeView = ng.core this.idMap = this.utilsService.generateIds(elementsNeedingIds); this.idMap['parentList'] = this.generateParentListId(); }, + isCompatibleWithClipboard: function(connection) { + return this.clipboardService.isClipboardCompatibleWithConnection( + connection); + }, + deleteBlock: function(block) { + // If this is the top block, shift focus to the previous tree. + var topBlocks = blocklyApp.workspace.topBlocks_; + for (var i = 0; i < topBlocks.length; i++) { + if (topBlocks[i].id == block.id) { + this.treeService.goToPreviousTree(this.parentId); + break; + } + } + // If this is not the top block, change the active descendant of the tree. + block.dispose(true); + }, + generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { + return this.utilsService.generateAriaLabelledByAttr( + mainLabel, secondLabel, isDisabled); + }, generateParentListId: function() { if (this.isTopBlock) { return this.parentId + '-node0' @@ -179,19 +186,11 @@ blocklyApp.WorkspaceTreeView = ng.core return this.utilsService.generateUniqueId(); } }, - getNoPreviousConnectionHTMLText: function(block) { - if (!block.previousConnection) { - return 'blockly-disabled'; - } else { - return ''; - } + hasPreviousConnection: function(block) { + return Boolean(block.previousConnection); }, - getNoNextConnectionHTMLText: function(block) { - if (!block.nextConnection) { - return 'blockly-disabled'; - } else { - return ''; - } + hasNextConnection: function(block) { + return Boolean(block.nextConnection); }, checkParentList: function(parentList) { blocklyApp.debug && console.log('setting parent list'); diff --git a/accessible/workspaceview.component.js b/accessible/workspaceview.component.js index 281b87b55..2c0d2c6f0 100644 --- a/accessible/workspaceview.component.js +++ b/accessible/workspaceview.component.js @@ -30,17 +30,19 @@ blocklyApp.WorkspaceView = ng.core +
    - -
    +
      Date: Fri, 10 Jun 2016 18:19:25 -0700 Subject: [PATCH 15/22] Fix error when undoing a shadow block replacement. Issue #415. --- core/xml.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/xml.js b/core/xml.js index f2664ac76..70fd915a5 100644 --- a/core/xml.js +++ b/core/xml.js @@ -294,7 +294,11 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { for (var i = 0; i < childCount; i++) { var xmlChild = xml.childNodes[i]; var name = xmlChild.nodeName.toLowerCase(); - if (name == 'block') { + if (name == 'block' || + (name == 'shadow' && !Blockly.Events.recordUndo)) { + // Allow top-level shadow blocks if recordUndo is disabled since + // that means an undo is in progress. Such a block is expected + // to be moved to a nested destination in the next operation. var block = Blockly.Xml.domToBlock(xmlChild, workspace); var blockX = parseInt(xmlChild.getAttribute('x'), 10); var blockY = parseInt(xmlChild.getAttribute('y'), 10); From cafd1802288aede12bc0a0121a12381345223894 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Fri, 10 Jun 2016 19:03:23 -0700 Subject: [PATCH 16/22] Unify setActiveDesc() and updateSelectedNode() in the TreeService. Move function calls made directly within the template to the correct hooks. --- accessible/clipboard.service.js | 32 +++++---------- accessible/toolbox_treeview.component.js | 21 +++++----- accessible/toolboxview.component.js | 3 +- accessible/tree.service.js | 48 +++++++++------------- accessible/workspace_treeview.component.js | 36 +++++++--------- accessible/workspaceview.component.js | 27 ++++-------- 6 files changed, 66 insertions(+), 101 deletions(-) diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index 29f0baaed..dee70dfc0 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -86,36 +86,26 @@ blocklyApp.ClipboardService = ng.core alert(Blockly.Msg.MARKED_SPOT_MSG); }, isCompatibleWithConnection_: function(blockConnection, connection) { - // Checking that the connection and blockConnection exist. - if (!connection || !blockConnection) { - return false; - } - - // Checking that the types match and it's the right kind of connection. - var isCompatible = Blockly.OPPOSITE_TYPE[blockConnection.type] == - connection.type && connection.checkType_(blockConnection); - - if (blocklyApp.debug) { - if (isCompatible) { - console.log('blocks should be connected'); - } else { - console.log('blocks should not be connected'); - } - } - return isCompatible; + // 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 this.markedConnection_ && + return Boolean( + this.markedConnection_ && this.markedConnection_.sourceBlock_.workspace && this.isCompatibleWithConnection_( - blockConnection, this.markedConnection_); + blockConnection, this.markedConnection_)); }, isClipboardCompatibleWithConnection: function(connection) { var superiorConnection = this.clipboardBlockSuperiorConnection_; var nextConnection = this.clipboardBlockNextConnection_; - return + return Boolean( this.isCompatibleWithConnection_(connection, superiorConnection) || - this.isCompatibleWithConnection_(connection, nextConnection); + this.isCompatibleWithConnection_(connection, nextConnection)); } }); diff --git a/accessible/toolbox_treeview.component.js b/accessible/toolbox_treeview.component.js index 9d02e4afa..f1b98d8d1 100644 --- a/accessible/toolbox_treeview.component.js +++ b/accessible/toolbox_treeview.component.js @@ -33,7 +33,6 @@ blocklyApp.ToolboxTreeView = ng.core [attr.aria-labelledBy]="generateAriaLabelledByAttr('blockly-block-summary', idMap['blockSummaryLabel'])" [attr.aria-selected]="index == 0 && tree.getAttribute('aria-activedescendant') == 'blockly-toolbox-tree-node0'" [attr.aria-level]="level"> - {{setActiveDesc(parentList)}}
        @@ -135,19 +134,21 @@ blocklyApp.ToolboxTreeView = ng.core this.idMap['parentList'] = this.utilsService.generateUniqueId(); } }, + ngAfterViewInit: function() { + // If this is a top-level tree in the toolbox, set its active + // descendant after the ids have been computed. + if (this.index == 0 && + this.tree.getAttribute('aria-activedescendant') == + 'blockly-toolbox-tree-node0') { + this.treeService.setActiveDesc( + document.getElementById(this.idMap['parentList']), + this.tree); + } + }, generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { return this.utilsService.generateAriaLabelledByAttr( mainLabel, secondLabel, isDisabled); }, - setActiveDesc: function(parentList) { - // If this is the first child of the toolbox and the - // current active descendant of the tree is this child, - // then set the active descendant stored in the treeService. - if (this.index == 0 && this.tree.getAttribute('aria-activedescendant') == - 'blockly-toolbox-tree-node0') { - this.treeService.setActiveDesc(parentList, this.tree.id); - } - }, copyToWorkspace: function(block) { var xml = Blockly.Xml.blockToDom(block); Blockly.Xml.domToBlock(blocklyApp.workspace, xml); diff --git a/accessible/toolboxview.component.js b/accessible/toolboxview.component.js index 523eeb331..e42e0e104 100644 --- a/accessible/toolboxview.component.js +++ b/accessible/toolboxview.component.js @@ -111,8 +111,7 @@ blocklyApp.ToolboxView = ng.core parent.id = 'blockly-toolbox-tree-node' + i; if (i == 0 && tree.getAttribute('aria-activedescendant') == 'blockly-toolbox-tree-node0') { - this.treeService.setActiveDesc(parent, tree.id); - parent.setAttribute('aria-selected', 'true'); + this.treeService.setActiveDesc(parent, tree); } }, getToolboxWorkspace: function(categoryNode) { diff --git a/accessible/tree.service.js b/accessible/tree.service.js index 6682f66af..27f5aebfe 100644 --- a/accessible/tree.service.js +++ b/accessible/tree.service.js @@ -28,8 +28,6 @@ blocklyApp.TreeService = ng.core .Class({ constructor: function() { blocklyApp.debug && console.log('making a new tree service'); - // Keeping track of the active descendants in each tree. - this.activeDesc_ = Object.create(null); this.trees = document.getElementsByClassName('blocklyTree'); // Keeping track of the last key pressed. If the user presses // enter (to edit a text input or press a button), the keyboard @@ -38,37 +36,31 @@ blocklyApp.TreeService = ng.core // to shift focus back to the tree as a whole. this.previousKey_ = null; }, - setActiveDesc: function(node, id) { - blocklyApp.debug && console.log('setting active descendant for tree ' + id); - this.activeDesc_[id] = node; - }, - getActiveDesc: function(id) { - return this.activeDesc_[id] || - document.getElementById((document.getElementById(id)).getAttribute('aria-activedescendant')); - }, - // Makes a given node the active descendant of a given tree. - updateSelectedNode: function(node, tree, keepFocus) { - blocklyApp.debug && console.log('updating node: ' + node.id); - var treeId = tree.id; - var activeDesc = this.getActiveDesc(treeId); + // Make a given node the active descendant of a given tree. + setActiveDesc: function(node, tree, keepFocus) { + blocklyApp.debug && console.log('setting activeDesc for tree ' + tree.id); + + var activeDesc = this.getActiveDesc(tree.id); if (activeDesc) { activeDesc.classList.remove('blocklyActiveDescendant'); activeDesc.setAttribute('aria-selected', 'false'); - } else { - blocklyApp.debug && console.log('updateSelectedNode: there is no active descendant'); } - node.classList.add('blocklyActiveDescendant'); - tree.setAttribute('aria-activedescendant', node.id); - this.setActiveDesc(node, treeId); - node.setAttribute('aria-selected', 'true'); - // Make sure keyboard focus is on tree as a whole - // in case focus was previously on a button or input - // element. + node.classList.add('blocklyActiveDescendant'); + node.setAttribute('aria-selected', 'true'); + tree.setAttribute('aria-activedescendant', node.id); + + // Make sure keyboard focus is on the entire tree in the case where the + // focus was previously on a button or input element. if (keepFocus) { tree.focus(); } }, + getActiveDesc: function(treeId) { + var activeDescendantId = document.getElementById( + treeId).getAttribute('aria-activedescendant'); + return document.getElementById(activeDescendantId); + }, onWorkspaceToolbarKeypress: function(e, treeId) { blocklyApp.debug && console.log(e.keyCode + 'inside TreeService onWorkspaceToolbarKeypress'); switch (e.keyCode) { @@ -151,7 +143,7 @@ blocklyApp.TreeService = ng.core if (!nextNode || nextNode.className == 'treeview') { return; } - this.updateSelectedNode(nextNode, tree, keepFocus); + this.setActiveDesc(nextNode, tree, keepFocus); this.previousKey_ = e.keyCode; e.preventDefault(); e.stopPropagation(); @@ -161,7 +153,7 @@ blocklyApp.TreeService = ng.core blocklyApp.debug && console.log('node passed in: ' + node.id); var prevSibling = this.getPreviousSibling(node); if (prevSibling && prevSibling.tagName != 'H1') { - this.updateSelectedNode(prevSibling, tree, keepFocus); + this.setActiveDesc(prevSibling, tree, keepFocus); } else { blocklyApp.debug && console.log('no previous sibling'); } @@ -173,7 +165,7 @@ blocklyApp.TreeService = ng.core blocklyApp.debug && console.log('in right arrow section'); var firstChild = this.getFirstChild(node); if (firstChild) { - this.updateSelectedNode(firstChild, tree, keepFocus); + this.setActiveDesc(firstChild, tree, keepFocus); } else { blocklyApp.debug && console.log('no valid child'); } @@ -187,7 +179,7 @@ blocklyApp.TreeService = ng.core blocklyApp.debug && console.log('preventing propogation'); var nextSibling = this.getNextSibling(node); if (nextSibling) { - this.updateSelectedNode(nextSibling, tree, keepFocus); + this.setActiveDesc(nextSibling, tree, keepFocus); } else { blocklyApp.debug && console.log('no next sibling'); } diff --git a/accessible/workspace_treeview.component.js b/accessible/workspace_treeview.component.js index fc052be10..4d35bcdde 100644 --- a/accessible/workspace_treeview.component.js +++ b/accessible/workspace_treeview.component.js @@ -31,7 +31,6 @@ blocklyApp.WorkspaceTreeView = ng.core
      1. - {{checkParentList(parentList)}}
        1. {{'WORKSPACE'|translate}} -
          +
          -
            - +
          `, @@ -58,10 +58,6 @@ blocklyApp.WorkspaceView = ng.core }) .Class({ constructor: [blocklyApp.TreeService, function(_treeService) { - if (blocklyApp.workspace) { - this.workspace = blocklyApp.workspace; - this.treeService = _treeService; - } // ACCESSIBLE_GLOBALS is a global variable defined by the containing // page. It should contain a key, toolbarButtonConfig, whose // corresponding value is an Array with two keys: 'text' and 'action'. @@ -70,23 +66,16 @@ blocklyApp.WorkspaceView = ng.core this.toolbarButtonConfig = ACCESSIBLE_GLOBALS && ACCESSIBLE_GLOBALS.toolbarButtonConfig ? ACCESSIBLE_GLOBALS.toolbarButtonConfig : []; - this.stringMap = { - 'WORKSPACE': Blockly.Msg.WORKSPACE, - 'CLEAR_WORKSPACE': Blockly.Msg.CLEAR_WORKSPACE - }; + this.workspace = blocklyApp.workspace; + this.treeService = _treeService; }], - onWorkspaceToolbarKeypress: function(event, id) { - this.treeService.onWorkspaceToolbarKeypress(event, id); + onWorkspaceToolbarKeypress: function(event) { + var activeElementId = document.activeElement.id; + this.treeService.onWorkspaceToolbarKeypress(event, activeElementId); }, onKeypress: function(event, tree){ this.treeService.onKeypress(event, tree); }, - getActiveElementId: function() { - return document.activeElement.id; - }, - makeId: function(index) { - return 'blockly-workspace-tree' + index; - }, isWorkspaceEmpty: function() { return !blocklyApp.workspace.topBlocks_.length; } From b75d3f7327fe0f8be8ba72c26c7d02674664af33 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Fri, 10 Jun 2016 19:22:14 -0700 Subject: [PATCH 17/22] Standardize naming of components. --- ...{appview.component.js => app.component.js} | 6 ++-- ...ldview.component.js => field.component.js} | 4 +-- ...component.js => toolbox-tree.component.js} | 36 +++++++++---------- ...view.component.js => toolbox.component.js} | 30 ++++++++-------- ...mponent.js => workspace-tree.component.js} | 28 ++++++++------- ...ew.component.js => workspace.component.js} | 11 +++--- demos/accessible/index.html | 14 ++++---- 7 files changed, 69 insertions(+), 60 deletions(-) rename accessible/{appview.component.js => app.component.js} (91%) rename accessible/{fieldview.component.js => field.component.js} (98%) rename accessible/{toolbox_treeview.component.js => toolbox-tree.component.js} (87%) rename accessible/{toolboxview.component.js => toolbox.component.js} (84%) rename accessible/{workspace_treeview.component.js => workspace-tree.component.js} (92%) rename accessible/{workspaceview.component.js => workspace.component.js} (89%) diff --git a/accessible/appview.component.js b/accessible/app.component.js similarity index 91% rename from accessible/appview.component.js rename to accessible/app.component.js index 6a3cc87bd..2a8b75218 100644 --- a/accessible/appview.component.js +++ b/accessible/app.component.js @@ -35,10 +35,10 @@ blocklyApp.AppView = ng.core
          - {{'TOOLBOX_LOAD'|translate}} + {{'TOOLBOX_LOAD'|translate}} - {{'WORKSPACE_LOAD'|translate}} + {{'WORKSPACE_LOAD'|translate}}
          @@ -53,7 +53,7 @@ blocklyApp.AppView = ng.core `, - directives: [blocklyApp.ToolboxView, blocklyApp.WorkspaceView], + directives: [blocklyApp.ToolboxComponent, blocklyApp.WorkspaceComponent], pipes: [blocklyApp.TranslatePipe], // The clipboard and utils services are declared here, so that all // components in the application use the same instance of the service. diff --git a/accessible/fieldview.component.js b/accessible/field.component.js similarity index 98% rename from accessible/fieldview.component.js rename to accessible/field.component.js index a53995556..6f5f06284 100644 --- a/accessible/fieldview.component.js +++ b/accessible/field.component.js @@ -24,9 +24,9 @@ * @author madeeha@google.com (Madeeha Ghori) */ -blocklyApp.FieldView = ng.core +blocklyApp.FieldComponent = ng.core .Component({ - selector: 'field-view', + selector: 'blockly-field', template: `
        2. - - - - + + +
      2. - - + + `, - directives: [ng.core.forwardRef(function() { - return blocklyApp.ToolboxTreeView; - }), blocklyApp.FieldView], + directives: [blocklyApp.FieldComponent, ng.core.forwardRef(function() { + return blocklyApp.ToolboxTreeComponent; + })], inputs: [ 'block', 'displayBlockMenu', 'level', 'index', 'tree', 'noCategories'], pipes: [blocklyApp.TranslatePipe] @@ -111,7 +111,7 @@ blocklyApp.ToolboxTreeView = ng.core blocklyApp.ClipboardService, blocklyApp.TreeService, blocklyApp.UtilsService, function(_clipboardService, _treeService, _utilsService) { // ClipboardService and UtilsService are app-wide singleton services. - // TreeService is from the parent ToolboxView component. + // TreeService is from the parent ToolboxComponent. this.infoBlocks = Object.create(null); this.clipboardService = _clipboardService; this.treeService = _treeService; diff --git a/accessible/toolboxview.component.js b/accessible/toolbox.component.js similarity index 84% rename from accessible/toolboxview.component.js rename to accessible/toolbox.component.js index e42e0e104..f6e8009d1 100644 --- a/accessible/toolboxview.component.js +++ b/accessible/toolbox.component.js @@ -23,9 +23,9 @@ * @author madeeha@google.com (Madeeha Ghori) */ -blocklyApp.ToolboxView = ng.core +blocklyApp.ToolboxComponent = ng.core .Component({ - selector: 'toolbox-view', + selector: 'blockly-toolbox', template: `

        Toolbox

          {{labelCategory(name, i, tree)}}
            - - + +
    - - + +
    `, - directives: [blocklyApp.ToolboxTreeView], + directives: [blocklyApp.ToolboxTreeComponent], providers: [blocklyApp.TreeService], }) .Class({ diff --git a/accessible/workspace_treeview.component.js b/accessible/workspace-tree.component.js similarity index 92% rename from accessible/workspace_treeview.component.js rename to accessible/workspace-tree.component.js index 4d35bcdde..25cccc9b1 100644 --- a/accessible/workspace_treeview.component.js +++ b/accessible/workspace-tree.component.js @@ -24,9 +24,9 @@ * @author madeeha@google.com (Madeeha Ghori) */ -blocklyApp.WorkspaceTreeView = ng.core +blocklyApp.WorkspaceTreeComponent = ng.core .Component({ - selector: 'tree-view', + selector: 'blockly-workspace-tree', template: `
  • - - + + +
  • @@ -118,15 +121,16 @@ blocklyApp.WorkspaceTreeView = ng.core
  • - - + + + `, - directives: [ng.core.forwardRef(function() { - return blocklyApp.WorkspaceTreeView; - }), blocklyApp.FieldView], + directives: [blocklyApp.FieldComponent, ng.core.forwardRef(function() { + return blocklyApp.WorkspaceTreeComponent; + })], inputs: ['block', 'isTopBlock', 'topBlockIndex', 'level', 'parentId', 'tree'], pipes: [blocklyApp.TranslatePipe], providers: [blocklyApp.TreeService], diff --git a/accessible/workspaceview.component.js b/accessible/workspace.component.js similarity index 89% rename from accessible/workspaceview.component.js rename to accessible/workspace.component.js index a7c42facd..c00cf7e5f 100644 --- a/accessible/workspaceview.component.js +++ b/accessible/workspace.component.js @@ -23,9 +23,9 @@ * @author madeeha@google.com (Madeeha Ghori) */ -blocklyApp.WorkspaceView = ng.core +blocklyApp.WorkspaceComponent = ng.core .Component({ - selector: 'workspace-view', + selector: 'blockly-workspace', template: `