diff --git a/blockly_compressed.js b/blockly_compressed.js index 726aa9ec4..3bb16756e 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -1060,11 +1060,10 @@ Blockly.Block.prototype.toString=function(a){var b=[];if(this.collapsed_)b.push( Blockly.Block.prototype.appendStatementInput=function(a){return this.appendInput_(Blockly.NEXT_STATEMENT,a)};Blockly.Block.prototype.appendDummyInput=function(a){return this.appendInput_(Blockly.DUMMY_INPUT,a||"")}; Blockly.Block.prototype.jsonInit=function(a){goog.asserts.assert(void 0==a.output||void 0==a.previousStatement,"Must not have both an output and a previousStatement.");this.setColour(a.colour);for(var b=0;void 0!==a["message"+b];)this.interpolate_(a["message"+b],a["args"+b]||[],a["lastDummyAlign"+b]),b++;void 0!==a.inputsInline&&this.setInputsInline(a.inputsInline);void 0!==a.output&&this.setOutput(!0,a.output);void 0!==a.previousStatement&&this.setPreviousStatement(!0,a.previousStatement);void 0!== a.nextStatement&&this.setNextStatement(!0,a.nextStatement);void 0!==a.tooltip&&this.setTooltip(a.tooltip);void 0!==a.helpUrl&&this.setHelpUrl(a.helpUrl)}; -Blockly.Block.prototype.interpolate_=function(a,b,c){var d=a.split(/(%\d+)/),e=[],f=0;a=[];for(var g=0;g=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}; // Copyright 2011 Google Inc. Apache License 2.0 Blockly.SVG_NS="http://www.w3.org/2000/svg";Blockly.HTML_NS="http://www.w3.org/1999/xhtml";Blockly.HSV_SATURATION=.45;Blockly.HSV_VALUE=.65;Blockly.SPRITE={width:64,height:92,url:"sprites.png"};Blockly.makeColour=function(a){return goog.color.hsvToHex(a,Blockly.HSV_SATURATION,255*Blockly.HSV_VALUE)};Blockly.INPUT_VALUE=1;Blockly.OUTPUT_VALUE=2;Blockly.NEXT_STATEMENT=3;Blockly.PREVIOUS_STATEMENT=4;Blockly.DUMMY_INPUT=5;Blockly.ALIGN_LEFT=-1;Blockly.ALIGN_CENTRE=0;Blockly.ALIGN_RIGHT=1; Blockly.OPPOSITE_TYPE=[];Blockly.OPPOSITE_TYPE[Blockly.INPUT_VALUE]=Blockly.OUTPUT_VALUE;Blockly.OPPOSITE_TYPE[Blockly.OUTPUT_VALUE]=Blockly.INPUT_VALUE;Blockly.OPPOSITE_TYPE[Blockly.NEXT_STATEMENT]=Blockly.PREVIOUS_STATEMENT;Blockly.OPPOSITE_TYPE[Blockly.PREVIOUS_STATEMENT]=Blockly.NEXT_STATEMENT;Blockly.selected=null;Blockly.highlightedConnection_=null;Blockly.localConnection_=null;Blockly.DRAG_RADIUS=5;Blockly.SNAP_RADIUS=20;Blockly.BUMP_DELAY=250;Blockly.COLLAPSE_CHARS=30;Blockly.LONGPRESS=750; diff --git a/core/block.js b/core/block.js index cb0cdd148..59fc8502e 100644 --- a/core/block.js +++ b/core/block.js @@ -948,25 +948,23 @@ Blockly.Block.prototype.jsonInit = function(json) { * @private */ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { - // Parse the message and interpolate the arguments. - // Build a list of elements. - var tokens = message.split(/(%\d+)/); + var tokens = Blockly.tokenizeInterpolation(message); + // Interpolate the arguments. Build a list of elements. var indexDup = []; var indexCount = 0; var elements = []; for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; - if (token.match(/^%\d+$/)) { - var index = parseInt(token.substring(1), 10); - goog.asserts.assert(index > 0 && index <= args.length, + if (typeof token == 'number') { + goog.asserts.assert(token > 0 && token <= args.length, 'Message index "%s" out of range.', token); - goog.asserts.assert(!indexDup[index], + goog.asserts.assert(!indexDup[token], 'Message index "%s" duplicated.', token); - indexDup[index] = true; + indexDup[token] = true; indexCount++; - elements.push(args[index - 1]); + elements.push(args[token - 1]); } else { - token = token.replace(/%%/g, '%').trim(); + token = token.trim(); if (token) { elements.push(token); } diff --git a/core/utils.js b/core/utils.js index 326bf44de..77e1d30f1 100644 --- a/core/utils.js +++ b/core/utils.js @@ -469,3 +469,61 @@ Blockly.commonWordSuffix = function(array, opt_shortest) { Blockly.isNumber = function(str) { return !!str.match(/^\s*-?\d+(\.\d+)?\s*$/); }; + +/** + * Parse a string with any number of interpolation tokens (%1, %2, ...). + * '%' characters may be self-escaped (%%). + * @param {string} message Text containing interpolation tokens. + * @return {!Array.} Array of strings and numbers. + */ +Blockly.tokenizeInterpolation = function(message) { + var tokens = []; + var chars = message.split(''); + chars.push(''); // End marker. + // Parse the message with a finite state machine. + // 0 - Base case. + // 1 - % found. + // 2 - Digit found. + var state = 0; + var buffer = []; + var number = null; + for (var i = 0; i < chars.length; i++) { + var c = chars[i]; + if (state == 0) { + if (c == '%') { + state = 1; // Start escape. + } else { + buffer.push(c); // Regular char. + } + } else if (state == 1) { + if (c == '%') { + buffer.push(c); // Escaped %: %% + state = 0; + } else if ('0' <= c && c <= '9') { + state = 2; + number = c; + var text = buffer.join(''); + if (text) { + tokens.push(text); + } + buffer.length = 0; + } else { + buffer.push('%', c); // Not an escape: %a + state = 0; + } + } else if (state == 2) { + if ('0' <= c && c <= '9') { + number += c; // Multi-digit number. + } else { + tokens.push(parseInt(number, 10)); + i--; // Parse this char again. + state = 0; + } + } + } + var text = buffer.join(''); + if (text) { + tokens.push(text); + } + return tokens; +}; diff --git a/tests/jsunit/blockly_test.js b/tests/jsunit/blockly_test.js index 8dffb9dd1..1318149f2 100644 --- a/tests/jsunit/blockly_test.js +++ b/tests/jsunit/blockly_test.js @@ -187,3 +187,20 @@ function test_commonWordSuffix() { len = Blockly.commonWordSuffix([]); assertEquals('Empty list', 0, len); } + +function test_tokenizeInterpolation() { + var tokens = Blockly.tokenizeInterpolation(''); + assertArrayEquals('Null interpolation', [], tokens); + tokens = Blockly.tokenizeInterpolation('Hello'); + assertArrayEquals('No interpolation', ['Hello'], tokens); + tokens = Blockly.tokenizeInterpolation('Hello%World'); + assertArrayEquals('Unescaped %.', ['Hello%World'], tokens); + tokens = Blockly.tokenizeInterpolation('Hello%%World'); + assertArrayEquals('Escaped %.', ['Hello%World'], tokens); + tokens = Blockly.tokenizeInterpolation('Hello %1 World'); + assertArrayEquals('Interpolation.', ['Hello ', 1, ' World'], tokens); + tokens = Blockly.tokenizeInterpolation('%123Hello%456World%789'); + assertArrayEquals('Interpolations.', [123, 'Hello', 456, 'World', 789], tokens); + tokens = Blockly.tokenizeInterpolation('%%%x%%0%00%01%'); + assertArrayEquals('Torture interpolations.', ['%%x%0', 0, 1, '%'], tokens); +}