From 306eb80216939faa65b5205a477cadeffdb48587 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Tue, 13 Jun 2023 21:30:37 +0100 Subject: [PATCH] refactor(generators): Introduce JavascriptGenerator class, Order enum (#7153) * refactor(generators): Introduce class JavascriptGenerator Also fix an import ordering error. * refactor(generators): Use Order.* instead of .ORDER_* * refactor(generators): Don't rename javascriptGenerator --- generators/javascript.js | 560 +++++++++++---------- generators/javascript/colour.js | 44 +- generators/javascript/lists.js | 148 +++--- generators/javascript/logic.js | 83 +-- generators/javascript/loops.js | 87 ++-- generators/javascript/math.js | 227 +++++---- generators/javascript/procedures.js | 82 +-- generators/javascript/text.js | 227 +++++---- generators/javascript/variables.js | 16 +- generators/javascript/variables_dynamic.js | 9 +- 10 files changed, 770 insertions(+), 713 deletions(-) diff --git a/generators/javascript.js b/generators/javascript.js index ce259657f..c1af07c51 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -16,305 +16,319 @@ import * as Variables from '../core/variables.js'; import * as stringUtils from '../core/utils/string.js'; // import type {Block} from '../core/block.js'; import {CodeGenerator} from '../core/generator.js'; -import {inputTypes} from '../core/inputs/input_types.js'; import {Names, NameType} from '../core/names.js'; // import type {Workspace} from '../core/workspace.js'; +import {inputTypes} from '../core/inputs/input_types.js'; -/** - * JavaScript code generator. - * @type {!CodeGenerator} - */ -const JavaScript = new CodeGenerator('JavaScript'); - -/** - * List of illegal variable names. - * This is not intended to be a security feature. Blockly is 100% client-side, - * so bypassing this list is trivial. This is intended to prevent users from - * accidentally clobbering a built-in object or function. - */ -JavaScript.addReservedWords( - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords - 'break,case,catch,class,const,continue,debugger,default,delete,do,else,export,extends,finally,for,function,if,import,in,instanceof,new,return,super,switch,this,throw,try,typeof,var,void,while,with,yield,' + - 'enum,' + - 'implements,interface,let,package,private,protected,public,static,' + - 'await,' + - 'null,true,false,' + - // Magic variable. - 'arguments,' + - // Everything in the current environment (835 items in Chrome, 104 in Node). - Object.getOwnPropertyNames(globalThis).join(',')); - /** * Order of operation ENUMs. * https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence + * @enum {number} */ -JavaScript.ORDER_ATOMIC = 0; // 0 "" ... -JavaScript.ORDER_NEW = 1.1; // new -JavaScript.ORDER_MEMBER = 1.2; // . [] -JavaScript.ORDER_FUNCTION_CALL = 2; // () -JavaScript.ORDER_INCREMENT = 3; // ++ -JavaScript.ORDER_DECREMENT = 3; // -- -JavaScript.ORDER_BITWISE_NOT = 4.1; // ~ -JavaScript.ORDER_UNARY_PLUS = 4.2; // + -JavaScript.ORDER_UNARY_NEGATION = 4.3; // - -JavaScript.ORDER_LOGICAL_NOT = 4.4; // ! -JavaScript.ORDER_TYPEOF = 4.5; // typeof -JavaScript.ORDER_VOID = 4.6; // void -JavaScript.ORDER_DELETE = 4.7; // delete -JavaScript.ORDER_AWAIT = 4.8; // await -JavaScript.ORDER_EXPONENTIATION = 5.0; // ** -JavaScript.ORDER_MULTIPLICATION = 5.1; // * -JavaScript.ORDER_DIVISION = 5.2; // / -JavaScript.ORDER_MODULUS = 5.3; // % -JavaScript.ORDER_SUBTRACTION = 6.1; // - -JavaScript.ORDER_ADDITION = 6.2; // + -JavaScript.ORDER_BITWISE_SHIFT = 7; // << >> >>> -JavaScript.ORDER_RELATIONAL = 8; // < <= > >= -JavaScript.ORDER_IN = 8; // in -JavaScript.ORDER_INSTANCEOF = 8; // instanceof -JavaScript.ORDER_EQUALITY = 9; // == != === !== -JavaScript.ORDER_BITWISE_AND = 10; // & -JavaScript.ORDER_BITWISE_XOR = 11; // ^ -JavaScript.ORDER_BITWISE_OR = 12; // | -JavaScript.ORDER_LOGICAL_AND = 13; // && -JavaScript.ORDER_LOGICAL_OR = 14; // || -JavaScript.ORDER_CONDITIONAL = 15; // ?: -JavaScript.ORDER_ASSIGNMENT = 16; // = += -= **= *= /= %= <<= >>= ... -JavaScript.ORDER_YIELD = 17; // yield -JavaScript.ORDER_COMMA = 18; // , -JavaScript.ORDER_NONE = 99; // (...) - -/** - * List of outer-inner pairings that do NOT require parentheses. - * @type {!Array>} - */ -JavaScript.ORDER_OVERRIDES = [ - // (foo()).bar -> foo().bar - // (foo())[0] -> foo()[0] - [JavaScript.ORDER_FUNCTION_CALL, JavaScript.ORDER_MEMBER], - // (foo())() -> foo()() - [JavaScript.ORDER_FUNCTION_CALL, 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] - [JavaScript.ORDER_MEMBER, JavaScript.ORDER_MEMBER], - // (foo.bar)() -> foo.bar() - // (foo[0])() -> foo[0]() - [JavaScript.ORDER_MEMBER, JavaScript.ORDER_FUNCTION_CALL], - - // !(!foo) -> !!foo - [JavaScript.ORDER_LOGICAL_NOT, JavaScript.ORDER_LOGICAL_NOT], - // a * (b * c) -> a * b * c - [JavaScript.ORDER_MULTIPLICATION, JavaScript.ORDER_MULTIPLICATION], - // a + (b + c) -> a + b + c - [JavaScript.ORDER_ADDITION, JavaScript.ORDER_ADDITION], - // a && (b && c) -> a && b && c - [JavaScript.ORDER_LOGICAL_AND, JavaScript.ORDER_LOGICAL_AND], - // a || (b || c) -> a || b || c - [JavaScript.ORDER_LOGICAL_OR, JavaScript.ORDER_LOGICAL_OR] -]; - -/** - * Whether the init method has been called. - * @type {?boolean} - */ -JavaScript.isInitialized = false; - -/** - * Initialise the database of variable names. - * @param {!Workspace} workspace Workspace to generate code from. - */ -JavaScript.init = function(workspace) { - // Call Blockly.CodeGenerator's init. - Object.getPrototypeOf(this).init.call(this); - - if (!this.nameDB_) { - this.nameDB_ = new Names(this.RESERVED_WORDS_); - } else { - this.nameDB_.reset(); - } - - this.nameDB_.setVariableMap(workspace.getVariableMap()); - this.nameDB_.populateVariables(workspace); - this.nameDB_.populateProcedures(workspace); - - const defvars = []; - // Add developer variables (not created or named by the user). - const devVarList = Variables.allDeveloperVariables(workspace); - for (let i = 0; i < devVarList.length; i++) { - defvars.push( - this.nameDB_.getName(devVarList[i], NameType.DEVELOPER_VARIABLE)); - } - - // Add user variables, but only ones that are being used. - const variables = Variables.allUsedVarModels(workspace); - for (let i = 0; i < variables.length; i++) { - defvars.push(this.nameDB_.getName(variables[i].getId(), NameType.VARIABLE)); - } - - // Declare all of the variables. - if (defvars.length) { - this.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; - } - this.isInitialized = true; +export const Order = { + ATOMIC: 0, // 0 "" ... + NEW: 1.1, // new + MEMBER: 1.2, // . [] + FUNCTION_CALL: 2, // () + INCREMENT: 3, // ++ + DECREMENT: 3, // -- + BITWISE_NOT: 4.1, // ~ + UNARY_PLUS: 4.2, // + + UNARY_NEGATION: 4.3, // - + LOGICAL_NOT: 4.4, // ! + TYPEOF: 4.5, // typeof + VOID: 4.6, // void + DELETE: 4.7, // delete + AWAIT: 4.8, // await + EXPONENTIATION: 5.0, // ** + MULTIPLICATION: 5.1, // * + DIVISION: 5.2, // / + MODULUS: 5.3, // % + SUBTRACTION: 6.1, // - + ADDITION: 6.2, // + + BITWISE_SHIFT: 7, // << >> >>> + RELATIONAL: 8, // < <= > >= + IN: 8, // in + INSTANCEOF: 8, // instanceof + EQUALITY: 9, // == != === !== + BITWISE_AND: 10, // & + BITWISE_XOR: 11, // ^ + BITWISE_OR: 12, // | + LOGICAL_AND: 13, // && + LOGICAL_OR: 14, // || + CONDITIONAL: 15, // ?: + ASSIGNMENT: 16, //: += -= **= *= /= %= <<= >>= ... + YIELD: 17, // yield + COMMA: 18, // , + NONE: 99, // (...) }; /** - * Prepend the generated code with the variable definitions. - * @param {string} code Generated code. - * @return {string} Completed code. + * JavaScript code generator class. */ -JavaScript.finish = function(code) { - // Convert the definitions dictionary into a list. - const definitions = Object.values(this.definitions_); - // Call Blockly.CodeGenerator's finish. - code = Object.getPrototypeOf(this).finish.call(this, code); - this.isInitialized = false; +export class JavascriptGenerator extends CodeGenerator { + /** + * List of outer-inner pairings that do NOT require parentheses. + * @type {!Array>} + */ + ORDER_OVERRIDES = [ + // (foo()).bar -> foo().bar + // (foo())[0] -> foo()[0] + [Order.FUNCTION_CALL, Order.MEMBER], + // (foo())() -> foo()() + [Order.FUNCTION_CALL, 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] + [Order.MEMBER, Order.MEMBER], + // (foo.bar)() -> foo.bar() + // (foo[0])() -> foo[0]() + [Order.MEMBER, Order.FUNCTION_CALL], - this.nameDB_.reset(); - return definitions.join('\n\n') + '\n\n\n' + code; -}; + // !(!foo) -> !!foo + [Order.LOGICAL_NOT, Order.LOGICAL_NOT], + // a * (b * c) -> a * b * c + [Order.MULTIPLICATION, Order.MULTIPLICATION], + // a + (b + c) -> a + b + c + [Order.ADDITION, Order.ADDITION], + // a && (b && c) -> a && b && c + [Order.LOGICAL_AND, Order.LOGICAL_AND], + // a || (b || c) -> a || b || c + [Order.LOGICAL_OR, Order.LOGICAL_OR] + ]; -/** - * Naked values are top-level blocks with outputs that aren't plugged into - * anything. A trailing semicolon is needed to make this legal. - * @param {string} line Line of generated code. - * @return {string} Legal line of code. - */ -JavaScript.scrubNakedValue = function(line) { - return line + ';\n'; -}; + constructor(name) { + super(name ?? 'JavaScript'); + this.isInitialized = false; -/** - * Encode a string as a properly escaped JavaScript string, complete with - * quotes. - * @param {string} string Text to encode. - * @return {string} JavaScript string. - * @protected - */ -JavaScript.quote_ = function(string) { - // Can't use goog.string.quote since Google's style guide recommends - // JS string literals use single quotes. - string = string.replace(/\\/g, '\\\\') - .replace(/\n/g, '\\\n') - .replace(/'/g, '\\\''); - return '\'' + string + '\''; -}; - -/** - * Encode a string as a properly escaped multiline JavaScript string, complete - * with quotes. - * @param {string} string Text to encode. - * @return {string} JavaScript string. - * @protected - */ -JavaScript.multiline_quote_ = function(string) { - // Can't use goog.string.quote since Google's style guide recommends - // JS string literals use single quotes. - const lines = string.split(/\n/g).map(this.quote_); - return lines.join(' + \'\\n\' +\n'); -}; - -/** - * Common tasks for generating JavaScript from blocks. - * Handles comments for the specified block and any connected value blocks. - * Calls any statements following this block. - * @param {!Block} block The current block. - * @param {string} code The JavaScript code created for this block. - * @param {boolean=} opt_thisOnly True to generate code for only this statement. - * @return {string} JavaScript code with comments and subsequent blocks added. - * @protected - */ -JavaScript.scrub_ = function(block, code, opt_thisOnly) { - let commentCode = ''; - // Only collect comments for blocks that aren't inline. - if (!block.outputConnection || !block.outputConnection.targetConnection) { - // Collect comment for this block. - let comment = block.getCommentText(); - if (comment) { - comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); - commentCode += this.prefixLines(comment + '\n', '// '); + // Copy Order values onto instance for backwards compatibility + // while ensuring they are not part of the publically- -advertised + // API. + for (const key in Order) { + this['ORDER_' + key] = Order[key]; } - // Collect comments for all value arguments. - // Don't collect comments for nested statements. - for (let i = 0; i < block.inputList.length; i++) { - if (block.inputList[i].type === inputTypes.VALUE) { - const childBlock = block.inputList[i].connection.targetBlock(); - if (childBlock) { - comment = this.allNestedComments(childBlock); - if (comment) { - commentCode += this.prefixLines(comment, '// '); + + // List of illegal variable names. This is not intended to be a + // security feature. Blockly is 100% client-side, so bypassing + // this list is trivial. This is intended to prevent users from + // accidentally clobbering a built-in object or function. + this.addReservedWords( + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords + 'break,case,catch,class,const,continue,debugger,default,delete,do,' + + 'else,export,extends,finally,for,function,if,import,in,instanceof,' + + 'new,return,super,switch,this,throw,try,typeof,var,void,' + + 'while,with,yield,' + + 'enum,' + + 'implements,interface,let,package,private,protected,public,static,' + + 'await,' + + 'null,true,false,' + + // Magic variable. + 'arguments,' + + // Everything in the current environment (835 items in Chrome, + // 104 in Node). + Object.getOwnPropertyNames(globalThis).join(',') + ); + } + + /** + * Initialise the database of variable names. + * @param {!Workspace} workspace Workspace to generate code from. + */ + init(workspace) { + super.init(workspace); + + if (!this.nameDB_) { + this.nameDB_ = new Names(this.RESERVED_WORDS_); + } else { + this.nameDB_.reset(); + } + + this.nameDB_.setVariableMap(workspace.getVariableMap()); + this.nameDB_.populateVariables(workspace); + this.nameDB_.populateProcedures(workspace); + + const defvars = []; + // Add developer variables (not created or named by the user). + const devVarList = Variables.allDeveloperVariables(workspace); + for (let i = 0; i < devVarList.length; i++) { + defvars.push( + this.nameDB_.getName(devVarList[i], NameType.DEVELOPER_VARIABLE)); + } + + // Add user variables, but only ones that are being used. + const variables = Variables.allUsedVarModels(workspace); + for (let i = 0; i < variables.length; i++) { + defvars.push(this.nameDB_.getName(variables[i].getId(), NameType.VARIABLE)); + } + + // Declare all of the variables. + if (defvars.length) { + this.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; + } + this.isInitialized = true; + } + + /** + * Prepend the generated code with the variable definitions. + * @param {string} code Generated code. + * @return {string} Completed code. + */ + finish(code) { + // Convert the definitions dictionary into a list. + const definitions = Object.values(this.definitions_); + // Call Blockly.CodeGenerator's finish. + super.finish(code); + this.isInitialized = false; + + this.nameDB_.reset(); + return definitions.join('\n\n') + '\n\n\n' + code; + } + + /** + * Naked values are top-level blocks with outputs that aren't plugged into + * anything. A trailing semicolon is needed to make this legal. + * @param {string} line Line of generated code. + * @return {string} Legal line of code. + */ + scrubNakedValue(line) { + return line + ';\n'; + } + + /** + * Encode a string as a properly escaped JavaScript string, complete with + * quotes. + * @param {string} string Text to encode. + * @return {string} JavaScript string. + * @protected + */ + quote_(string) { + // Can't use goog.string.quote since Google's style guide recommends + // JS string literals use single quotes. + string = string.replace(/\\/g, '\\\\') + .replace(/\n/g, '\\\n') + .replace(/'/g, '\\\''); + return '\'' + string + '\''; + } + + /** + * Encode a string as a properly escaped multiline JavaScript string, complete + * with quotes. + * @param {string} string Text to encode. + * @return {string} JavaScript string. + * @protected + */ + multiline_quote_(string) { + // Can't use goog.string.quote since Google's style guide recommends + // JS string literals use single quotes. + const lines = string.split(/\n/g).map(this.quote_); + return lines.join(' + \'\\n\' +\n'); + } + + /** + * Common tasks for generating JavaScript from blocks. + * Handles comments for the specified block and any connected value blocks. + * Calls any statements following this block. + * @param {!Block} block The current block. + * @param {string} code The JavaScript code created for this block. + * @param {boolean=} opt_thisOnly True to generate code for only this statement. + * @return {string} JavaScript code with comments and subsequent blocks added. + * @protected + */ + scrub_(block, code, opt_thisOnly) { + let commentCode = ''; + // Only collect comments for blocks that aren't inline. + if (!block.outputConnection || !block.outputConnection.targetConnection) { + // Collect comment for this block. + let comment = block.getCommentText(); + if (comment) { + comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); + commentCode += this.prefixLines(comment + '\n', '// '); + } + // Collect comments for all value arguments. + // Don't collect comments for nested statements. + for (let i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type === inputTypes.VALUE) { + const childBlock = block.inputList[i].connection.targetBlock(); + if (childBlock) { + comment = this.allNestedComments(childBlock); + if (comment) { + commentCode += this.prefixLines(comment, '// '); + } } } } } - } - const nextBlock = block.nextConnection && block.nextConnection.targetBlock(); - const nextCode = opt_thisOnly ? '' : this.blockToCode(nextBlock); - return commentCode + code + nextCode; -}; - -/** - * Gets a property and adjusts the value while taking into account indexing. - * @param {!Block} block The block. - * @param {string} atId The property ID of the element to get. - * @param {number=} opt_delta Value to add. - * @param {boolean=} opt_negate Whether to negate the value. - * @param {number=} opt_order The highest order acting on this value. - * @return {string|number} - */ -JavaScript.getAdjusted = function( - block, atId, opt_delta, opt_negate, opt_order) { - let delta = opt_delta || 0; - let order = opt_order || this.ORDER_NONE; - if (block.workspace.options.oneBasedIndex) { - delta--; - } - const defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; - - let innerOrder; - let outerOrder = order; - if (delta > 0) { - outerOrder = this.ORDER_ADDITION; - innerOrder = this.ORDER_ADDITION; - } else if (delta < 0) { - outerOrder = this.ORDER_SUBTRACTION; - innerOrder = this.ORDER_SUBTRACTION; - } else if (opt_negate) { - outerOrder = this.ORDER_UNARY_NEGATION; - innerOrder = this.ORDER_UNARY_NEGATION; + const nextBlock = block.nextConnection && block.nextConnection.targetBlock(); + const nextCode = opt_thisOnly ? '' : this.blockToCode(nextBlock); + return commentCode + code + nextCode; } - let at = this.valueToCode(block, atId, outerOrder) || defaultAtIndex; - - if (stringUtils.isNumber(at)) { - // If the index is a naked number, adjust it right now. - at = Number(at) + delta; - if (opt_negate) { - at = -at; + /** + * Gets a property and adjusts the value while taking into account indexing. + * @param {!Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @param {number=} opt_order The highest order acting on this value. + * @return {string|number} + */ + getAdjusted(block, atId, opt_delta, opt_negate, opt_order) { + let delta = opt_delta || 0; + let order = opt_order || this.ORDER_NONE; + if (block.workspace.options.oneBasedIndex) { + delta--; } - } else { - // If the index is dynamic, adjust it in code. + const defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; + + let innerOrder; + let outerOrder = order; if (delta > 0) { - at = at + ' + ' + delta; + outerOrder = this.ORDER_ADDITION; + innerOrder = this.ORDER_ADDITION; } else if (delta < 0) { - at = at + ' - ' + -delta; + outerOrder = this.ORDER_SUBTRACTION; + innerOrder = this.ORDER_SUBTRACTION; + } else if (opt_negate) { + outerOrder = this.ORDER_UNARY_NEGATION; + innerOrder = this.ORDER_UNARY_NEGATION; } - if (opt_negate) { - if (delta) { - at = '-(' + at + ')'; - } else { - at = '-' + at; + + let at = this.valueToCode(block, atId, outerOrder) || defaultAtIndex; + + if (stringUtils.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = Number(at) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = at + ' + ' + delta; + } else if (delta < 0) { + at = at + ' - ' + -delta; + } + if (opt_negate) { + if (delta) { + at = '-(' + at + ')'; + } else { + at = '-' + at; + } + } + innerOrder = Math.floor(innerOrder); + order = Math.floor(order); + if (innerOrder && order >= innerOrder) { + at = '(' + at + ')'; } } - innerOrder = Math.floor(innerOrder); - order = Math.floor(order); - if (innerOrder && order >= innerOrder) { - at = '(' + at + ')'; - } + return at; } - return at; -}; -export {JavaScript as javascriptGenerator}; +} + +/** + * JavaScript code generator instance. + * @type {!JavascriptGenerator} + */ +export const javascriptGenerator = new JavascriptGenerator(); diff --git a/generators/javascript/colour.js b/generators/javascript/colour.js index dce2d22f4..9ffd901d9 100644 --- a/generators/javascript/colour.js +++ b/generators/javascript/colour.js @@ -11,36 +11,36 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.JavaScript.colour'); -import {javascriptGenerator as JavaScript} from '../javascript.js'; +import {Order, javascriptGenerator} from '../javascript.js'; -JavaScript.forBlock['colour_picker'] = function(block) { +javascriptGenerator.forBlock['colour_picker'] = function(block) { // Colour picker. - const code = JavaScript.quote_(block.getFieldValue('COLOUR')); - return [code, JavaScript.ORDER_ATOMIC]; + const code = javascriptGenerator.quote_(block.getFieldValue('COLOUR')); + return [code, Order.ATOMIC]; }; -JavaScript.forBlock['colour_random'] = function(block) { +javascriptGenerator.forBlock['colour_random'] = function(block) { // Generate a random colour. - const functionName = JavaScript.provideFunction_('colourRandom', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}() { + const functionName = javascriptGenerator.provideFunction_('colourRandom', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}() { var num = Math.floor(Math.random() * Math.pow(2, 24)); return '#' + ('00000' + num.toString(16)).substr(-6); } `); const code = functionName + '()'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['colour_rgb'] = function(block) { +javascriptGenerator.forBlock['colour_rgb'] = function(block) { // Compose a colour from RGB components expressed as percentages. - const red = JavaScript.valueToCode(block, 'RED', JavaScript.ORDER_NONE) || 0; + const red = javascriptGenerator.valueToCode(block, 'RED', Order.NONE) || 0; const green = - JavaScript.valueToCode(block, 'GREEN', JavaScript.ORDER_NONE) || 0; + javascriptGenerator.valueToCode(block, 'GREEN', Order.NONE) || 0; const blue = - JavaScript.valueToCode(block, 'BLUE', JavaScript.ORDER_NONE) || 0; - const functionName = JavaScript.provideFunction_('colourRgb', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) { + javascriptGenerator.valueToCode(block, 'BLUE', Order.NONE) || 0; + const functionName = javascriptGenerator.provideFunction_('colourRgb', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) { r = Math.max(Math.min(Number(r), 100), 0) * 2.55; g = Math.max(Math.min(Number(g), 100), 0) * 2.55; b = Math.max(Math.min(Number(b), 100), 0) * 2.55; @@ -51,19 +51,19 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) { } `); const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['colour_blend'] = function(block) { +javascriptGenerator.forBlock['colour_blend'] = function(block) { // Blend two colours together. - const c1 = JavaScript.valueToCode(block, 'COLOUR1', JavaScript.ORDER_NONE) || + const c1 = javascriptGenerator.valueToCode(block, 'COLOUR1', Order.NONE) || "'#000000'"; - const c2 = JavaScript.valueToCode(block, 'COLOUR2', JavaScript.ORDER_NONE) || + const c2 = javascriptGenerator.valueToCode(block, 'COLOUR2', Order.NONE) || "'#000000'"; const ratio = - JavaScript.valueToCode(block, 'RATIO', JavaScript.ORDER_NONE) || 0.5; - const functionName = JavaScript.provideFunction_('colourBlend', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) { + javascriptGenerator.valueToCode(block, 'RATIO', Order.NONE) || 0.5; + const functionName = javascriptGenerator.provideFunction_('colourBlend', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) { ratio = Math.max(Math.min(Number(ratio), 1), 0); var r1 = parseInt(c1.substring(1, 3), 16); var g1 = parseInt(c1.substring(3, 5), 16); @@ -81,5 +81,5 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) { } `); const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; diff --git a/generators/javascript/lists.js b/generators/javascript/lists.js index 317588e55..f2a3f531e 100644 --- a/generators/javascript/lists.js +++ b/generators/javascript/lists.js @@ -13,30 +13,30 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.JavaScript.lists'); import {NameType} from '../../core/names.js'; -import {javascriptGenerator as JavaScript} from '../javascript.js'; +import {Order, javascriptGenerator} from '../javascript.js'; -JavaScript.forBlock['lists_create_empty'] = function(block) { +javascriptGenerator.forBlock['lists_create_empty'] = function(block) { // Create an empty list. - return ['[]', JavaScript.ORDER_ATOMIC]; + return ['[]', Order.ATOMIC]; }; -JavaScript.forBlock['lists_create_with'] = function(block) { +javascriptGenerator.forBlock['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. const elements = new Array(block.itemCount_); for (let i = 0; i < block.itemCount_; i++) { elements[i] = - JavaScript.valueToCode(block, 'ADD' + i, JavaScript.ORDER_NONE) || + javascriptGenerator.valueToCode(block, 'ADD' + i, Order.NONE) || 'null'; } const code = '[' + elements.join(', ') + ']'; - return [code, JavaScript.ORDER_ATOMIC]; + return [code, Order.ATOMIC]; }; -JavaScript.forBlock['lists_repeat'] = function(block) { +javascriptGenerator.forBlock['lists_repeat'] = function(block) { // Create a list with one element repeated. - const functionName = JavaScript.provideFunction_('listsRepeat', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(value, n) { + const functionName = javascriptGenerator.provideFunction_('listsRepeat', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(value, n) { var array = []; for (var i = 0; i < n; i++) { array[i] = value; @@ -45,59 +45,60 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(value, n) { } `); const element = - JavaScript.valueToCode(block, 'ITEM', JavaScript.ORDER_NONE) || 'null'; + javascriptGenerator.valueToCode(block, 'ITEM', Order.NONE) || 'null'; const repeatCount = - JavaScript.valueToCode(block, 'NUM', JavaScript.ORDER_NONE) || '0'; + javascriptGenerator.valueToCode(block, 'NUM', Order.NONE) || '0'; const code = functionName + '(' + element + ', ' + repeatCount + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['lists_length'] = function(block) { +javascriptGenerator.forBlock['lists_length'] = function(block) { // String or array length. const list = - JavaScript.valueToCode(block, 'VALUE', JavaScript.ORDER_MEMBER) || '[]'; - return [list + '.length', JavaScript.ORDER_MEMBER]; + javascriptGenerator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; + return [list + '.length', Order.MEMBER]; }; -JavaScript.forBlock['lists_isEmpty'] = function(block) { +javascriptGenerator.forBlock['lists_isEmpty'] = function(block) { // Is the string null or array empty? const list = - JavaScript.valueToCode(block, 'VALUE', JavaScript.ORDER_MEMBER) || '[]'; - return ['!' + list + '.length', JavaScript.ORDER_LOGICAL_NOT]; + javascriptGenerator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; + return ['!' + list + '.length', Order.LOGICAL_NOT]; }; -JavaScript.forBlock['lists_indexOf'] = function(block) { +javascriptGenerator.forBlock['lists_indexOf'] = function(block) { // Find an item in the list. const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; const item = - JavaScript.valueToCode(block, 'FIND', JavaScript.ORDER_NONE) || "''"; + javascriptGenerator.valueToCode(block, 'FIND', Order.NONE) || "''"; const list = - JavaScript.valueToCode(block, 'VALUE', JavaScript.ORDER_MEMBER) || '[]'; + javascriptGenerator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; const code = list + '.' + operator + '(' + item + ')'; if (block.workspace.options.oneBasedIndex) { - return [code + ' + 1', JavaScript.ORDER_ADDITION]; + return [code + ' + 1', Order.ADDITION]; } - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['lists_getIndex'] = function(block) { +javascriptGenerator.forBlock['lists_getIndex'] = function(block) { // Get element at index. // Note: Until January 2013 this block did not have MODE or WHERE inputs. const mode = block.getFieldValue('MODE') || 'GET'; const where = block.getFieldValue('WHERE') || 'FROM_START'; const listOrder = - (where === 'RANDOM') ? JavaScript.ORDER_NONE : JavaScript.ORDER_MEMBER; - const list = JavaScript.valueToCode(block, 'VALUE', listOrder) || '[]'; + (where === 'RANDOM') ? Order.NONE : Order.MEMBER; + const list = + javascriptGenerator.valueToCode(block, 'VALUE', listOrder) || '[]'; switch (where) { case ('FIRST'): if (mode === 'GET') { const code = list + '[0]'; - return [code, JavaScript.ORDER_MEMBER]; + return [code, Order.MEMBER]; } else if (mode === 'GET_REMOVE') { const code = list + '.shift()'; - return [code, JavaScript.ORDER_MEMBER]; + return [code, Order.MEMBER]; } else if (mode === 'REMOVE') { return list + '.shift();\n'; } @@ -105,43 +106,44 @@ JavaScript.forBlock['lists_getIndex'] = function(block) { case ('LAST'): if (mode === 'GET') { const code = list + '.slice(-1)[0]'; - return [code, JavaScript.ORDER_MEMBER]; + return [code, Order.MEMBER]; } else if (mode === 'GET_REMOVE') { const code = list + '.pop()'; - return [code, JavaScript.ORDER_MEMBER]; + return [code, Order.MEMBER]; } else if (mode === 'REMOVE') { return list + '.pop();\n'; } break; case ('FROM_START'): { - const at = JavaScript.getAdjusted(block, 'AT'); + const at = javascriptGenerator.getAdjusted(block, 'AT'); if (mode === 'GET') { const code = list + '[' + at + ']'; - return [code, JavaScript.ORDER_MEMBER]; + return [code, Order.MEMBER]; } else if (mode === 'GET_REMOVE') { const code = list + '.splice(' + at + ', 1)[0]'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { return list + '.splice(' + at + ', 1);\n'; } break; } case ('FROM_END'): { - const at = JavaScript.getAdjusted(block, 'AT', 1, true); + const at = javascriptGenerator.getAdjusted(block, 'AT', 1, true); if (mode === 'GET') { const code = list + '.slice(' + at + ')[0]'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } else if (mode === 'GET_REMOVE') { const code = list + '.splice(' + at + ', 1)[0]'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { return list + '.splice(' + at + ', 1);'; } break; } case ('RANDOM'): { - const functionName = JavaScript.provideFunction_('listsGetRandomItem', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { + const functionName = + javascriptGenerator.provideFunction_('listsGetRandomItem', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { var x = Math.floor(Math.random() * list.length); if (remove) { return list.splice(x, 1)[0]; @@ -152,7 +154,7 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { `); const code = functionName + '(' + list + ', ' + (mode !== 'GET') + ')'; if (mode === 'GET' || mode === 'GET_REMOVE') { - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { return code + ';\n'; } @@ -162,15 +164,15 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { throw Error('Unhandled combination (lists_getIndex).'); }; -JavaScript.forBlock['lists_setIndex'] = function(block) { +javascriptGenerator.forBlock['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. let list = - JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_MEMBER) || '[]'; + javascriptGenerator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; const mode = block.getFieldValue('MODE') || 'GET'; const where = block.getFieldValue('WHERE') || 'FROM_START'; const value = - JavaScript.valueToCode(block, 'TO', JavaScript.ORDER_ASSIGNMENT) || + javascriptGenerator.valueToCode(block, 'TO', Order.ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. // Closure, which accesses and modifies 'list'. @@ -179,7 +181,8 @@ JavaScript.forBlock['lists_setIndex'] = function(block) { return ''; } const listVar = - JavaScript.nameDB_.getDistinctName('tmpList', NameType.VARIABLE); + javascriptGenerator.nameDB_.getDistinctName( + 'tmpList', NameType.VARIABLE); const code = 'var ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; @@ -202,7 +205,7 @@ JavaScript.forBlock['lists_setIndex'] = function(block) { } break; case ('FROM_START'): { - const at = JavaScript.getAdjusted(block, 'AT'); + const at = javascriptGenerator.getAdjusted(block, 'AT'); if (mode === 'SET') { return list + '[' + at + '] = ' + value + ';\n'; } else if (mode === 'INSERT') { @@ -211,8 +214,8 @@ JavaScript.forBlock['lists_setIndex'] = function(block) { break; } case ('FROM_END'): { - const at = JavaScript.getAdjusted( - block, 'AT', 1, false, JavaScript.ORDER_SUBTRACTION); + const at = javascriptGenerator.getAdjusted( + block, 'AT', 1, false, Order.SUBTRACTION); let code = cacheList(); if (mode === 'SET') { code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; @@ -227,7 +230,8 @@ JavaScript.forBlock['lists_setIndex'] = function(block) { case ('RANDOM'): { let code = cacheList(); const xVar = - JavaScript.nameDB_.getDistinctName('tmpX', NameType.VARIABLE); + javascriptGenerator.nameDB_.getDistinctName( + 'tmpX', NameType.VARIABLE); code += 'var ' + xVar + ' = Math.floor(Math.random() * ' + list + '.length);\n'; if (mode === 'SET') { @@ -262,10 +266,10 @@ const getSubstringIndex = function(listName, where, opt_at) { } }; -JavaScript.forBlock['lists_getSublist'] = function(block) { +javascriptGenerator.forBlock['lists_getSublist'] = function(block) { // Get sublist. const list = - JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_MEMBER) || '[]'; + javascriptGenerator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); let code; @@ -279,11 +283,11 @@ JavaScript.forBlock['lists_getSublist'] = function(block) { let at1; switch (where1) { case 'FROM_START': - at1 = JavaScript.getAdjusted(block, 'AT1'); + at1 = javascriptGenerator.getAdjusted(block, 'AT1'); break; case 'FROM_END': - at1 = JavaScript.getAdjusted( - block, 'AT1', 1, false, JavaScript.ORDER_SUBTRACTION); + at1 = javascriptGenerator.getAdjusted( + block, 'AT1', 1, false, Order.SUBTRACTION); at1 = list + '.length - ' + at1; break; case 'FIRST': @@ -295,11 +299,11 @@ JavaScript.forBlock['lists_getSublist'] = function(block) { let at2; switch (where2) { case 'FROM_START': - at2 = JavaScript.getAdjusted(block, 'AT2', 1); + at2 = javascriptGenerator.getAdjusted(block, 'AT2', 1); break; case 'FROM_END': - at2 = JavaScript.getAdjusted( - block, 'AT2', 0, false, JavaScript.ORDER_SUBTRACTION); + at2 = javascriptGenerator.getAdjusted( + block, 'AT2', 0, false, Order.SUBTRACTION); at2 = list + '.length - ' + at2; break; case 'LAST': @@ -310,8 +314,8 @@ JavaScript.forBlock['lists_getSublist'] = function(block) { } code = list + '.slice(' + at1 + ', ' + at2 + ')'; } else { - const at1 = JavaScript.getAdjusted(block, 'AT1'); - const at2 = JavaScript.getAdjusted(block, 'AT2'); + const at1 = javascriptGenerator.getAdjusted(block, 'AT1'); + const at2 = javascriptGenerator.getAdjusted(block, 'AT2'); const wherePascalCase = { 'FIRST': 'First', 'LAST': 'Last', @@ -324,9 +328,9 @@ JavaScript.forBlock['lists_getSublist'] = function(block) { (where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : ''; const at2Param = (where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : ''; - const functionName = JavaScript.provideFunction_( + const functionName = javascriptGenerator.provideFunction_( 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { var start = ${getSubstringIndex('sequence', where1, 'at1')}; var end = ${getSubstringIndex('sequence', where2, 'at2')} + 1; return sequence.slice(start, end); @@ -339,19 +343,19 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param} ((where2 === 'FROM_END' || where2 === 'FROM_START') ? ', ' + at2 : '') + ')'; } - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['lists_sort'] = function(block) { +javascriptGenerator.forBlock['lists_sort'] = function(block) { // Block for sorting a list. const list = - JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_FUNCTION_CALL) || + javascriptGenerator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || '[]'; const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; const type = block.getFieldValue('TYPE'); const getCompareFunctionName = - JavaScript.provideFunction_('listsGetSortCompare', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { + javascriptGenerator.provideFunction_('listsGetSortCompare', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { var compareFuncs = { 'NUMERIC': function(a, b) { return Number(a) - Number(b); }, @@ -367,15 +371,15 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { return [ list + '.slice().sort(' + getCompareFunctionName + '("' + type + '", ' + direction + '))', - JavaScript.ORDER_FUNCTION_CALL + Order.FUNCTION_CALL ]; }; -JavaScript.forBlock['lists_split'] = function(block) { +javascriptGenerator.forBlock['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. - let input = JavaScript.valueToCode(block, 'INPUT', JavaScript.ORDER_MEMBER); + let input = javascriptGenerator.valueToCode(block, 'INPUT', Order.MEMBER); const delimiter = - JavaScript.valueToCode(block, 'DELIM', JavaScript.ORDER_NONE) || "''"; + javascriptGenerator.valueToCode(block, 'DELIM', Order.NONE) || "''"; const mode = block.getFieldValue('MODE'); let functionName; if (mode === 'SPLIT') { @@ -392,14 +396,14 @@ JavaScript.forBlock['lists_split'] = function(block) { throw Error('Unknown mode: ' + mode); } const code = input + '.' + functionName + '(' + delimiter + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['lists_reverse'] = function(block) { +javascriptGenerator.forBlock['lists_reverse'] = function(block) { // Block for reversing a list. const list = - JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_FUNCTION_CALL) || + javascriptGenerator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || '[]'; const code = list + '.slice().reverse()'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; diff --git a/generators/javascript/logic.js b/generators/javascript/logic.js index 93420fabf..7206b63a6 100644 --- a/generators/javascript/logic.js +++ b/generators/javascript/logic.js @@ -11,26 +11,28 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.JavaScript.logic'); -import {javascriptGenerator as JavaScript} from '../javascript.js'; +import {Order, javascriptGenerator} from '../javascript.js'; -JavaScript.forBlock['controls_if'] = function(block) { +javascriptGenerator.forBlock['controls_if'] = function(block) { // If/elseif/else condition. let n = 0; let code = ''; - if (JavaScript.STATEMENT_PREFIX) { + if (javascriptGenerator.STATEMENT_PREFIX) { // Automatic prefix insertion is switched off for this block. Add manually. - code += JavaScript.injectId(JavaScript.STATEMENT_PREFIX, block); + code += javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_PREFIX, block); } do { const conditionCode = - JavaScript.valueToCode(block, 'IF' + n, JavaScript.ORDER_NONE) || + javascriptGenerator.valueToCode(block, 'IF' + n, Order.NONE) || 'false'; - let branchCode = JavaScript.statementToCode(block, 'DO' + n); - if (JavaScript.STATEMENT_SUFFIX) { - branchCode = JavaScript.prefixLines( - JavaScript.injectId(JavaScript.STATEMENT_SUFFIX, block), - JavaScript.INDENT) + + let branchCode = javascriptGenerator.statementToCode(block, 'DO' + n); + if (javascriptGenerator.STATEMENT_SUFFIX) { + branchCode = javascriptGenerator.prefixLines( + javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_SUFFIX, block), + javascriptGenerator.INDENT) + branchCode; } code += (n > 0 ? ' else ' : '') + 'if (' + conditionCode + ') {\n' + @@ -38,12 +40,13 @@ JavaScript.forBlock['controls_if'] = function(block) { n++; } while (block.getInput('IF' + n)); - if (block.getInput('ELSE') || JavaScript.STATEMENT_SUFFIX) { - let branchCode = JavaScript.statementToCode(block, 'ELSE'); - if (JavaScript.STATEMENT_SUFFIX) { - branchCode = JavaScript.prefixLines( - JavaScript.injectId(JavaScript.STATEMENT_SUFFIX, block), - JavaScript.INDENT) + + if (block.getInput('ELSE') || javascriptGenerator.STATEMENT_SUFFIX) { + let branchCode = javascriptGenerator.statementToCode(block, 'ELSE'); + if (javascriptGenerator.STATEMENT_SUFFIX) { + branchCode = javascriptGenerator.prefixLines( + javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_SUFFIX, block), + javascriptGenerator.INDENT) + branchCode; } code += ' else {\n' + branchCode + '}'; @@ -51,29 +54,30 @@ JavaScript.forBlock['controls_if'] = function(block) { return code + '\n'; }; -JavaScript.forBlock['controls_ifelse'] = JavaScript.forBlock['controls_if']; +javascriptGenerator.forBlock['controls_ifelse'] = + javascriptGenerator.forBlock['controls_if']; -JavaScript.forBlock['logic_compare'] = function(block) { +javascriptGenerator.forBlock['logic_compare'] = function(block) { // Comparison operator. const OPERATORS = {'EQ': '==', 'NEQ': '!=', 'LT': '<', 'LTE': '<=', 'GT': '>', 'GTE': '>='}; const operator = OPERATORS[block.getFieldValue('OP')]; const order = (operator === '==' || operator === '!=') ? - JavaScript.ORDER_EQUALITY : - JavaScript.ORDER_RELATIONAL; - const argument0 = JavaScript.valueToCode(block, 'A', order) || '0'; - const argument1 = JavaScript.valueToCode(block, 'B', order) || '0'; + Order.EQUALITY : + Order.RELATIONAL; + const argument0 = javascriptGenerator.valueToCode(block, 'A', order) || '0'; + const argument1 = javascriptGenerator.valueToCode(block, 'B', order) || '0'; const code = argument0 + ' ' + operator + ' ' + argument1; return [code, order]; }; -JavaScript.forBlock['logic_operation'] = function(block) { +javascriptGenerator.forBlock['logic_operation'] = function(block) { // Operations 'and', 'or'. const operator = (block.getFieldValue('OP') === 'AND') ? '&&' : '||'; - const order = (operator === '&&') ? JavaScript.ORDER_LOGICAL_AND : - JavaScript.ORDER_LOGICAL_OR; - let argument0 = JavaScript.valueToCode(block, 'A', order); - let argument1 = JavaScript.valueToCode(block, 'B', order); + const order = (operator === '&&') ? Order.LOGICAL_AND : + Order.LOGICAL_OR; + let argument0 = javascriptGenerator.valueToCode(block, 'A', order); + let argument1 = javascriptGenerator.valueToCode(block, 'B', order); if (!argument0 && !argument1) { // If there are no arguments, then the return value is false. argument0 = 'false'; @@ -92,36 +96,37 @@ JavaScript.forBlock['logic_operation'] = function(block) { return [code, order]; }; -JavaScript.forBlock['logic_negate'] = function(block) { +javascriptGenerator.forBlock['logic_negate'] = function(block) { // Negation. - const order = JavaScript.ORDER_LOGICAL_NOT; - const argument0 = JavaScript.valueToCode(block, 'BOOL', order) || 'true'; + const order = Order.LOGICAL_NOT; + const argument0 = + javascriptGenerator.valueToCode(block, 'BOOL', order) || 'true'; const code = '!' + argument0; return [code, order]; }; -JavaScript.forBlock['logic_boolean'] = function(block) { +javascriptGenerator.forBlock['logic_boolean'] = function(block) { // Boolean values true and false. const code = (block.getFieldValue('BOOL') === 'TRUE') ? 'true' : 'false'; - return [code, JavaScript.ORDER_ATOMIC]; + return [code, Order.ATOMIC]; }; -JavaScript.forBlock['logic_null'] = function(block) { +javascriptGenerator.forBlock['logic_null'] = function(block) { // Null data type. - return ['null', JavaScript.ORDER_ATOMIC]; + return ['null', Order.ATOMIC]; }; -JavaScript.forBlock['logic_ternary'] = function(block) { +javascriptGenerator.forBlock['logic_ternary'] = function(block) { // Ternary operator. const value_if = - JavaScript.valueToCode(block, 'IF', JavaScript.ORDER_CONDITIONAL) || + javascriptGenerator.valueToCode(block, 'IF', Order.CONDITIONAL) || 'false'; const value_then = - JavaScript.valueToCode(block, 'THEN', JavaScript.ORDER_CONDITIONAL) || + javascriptGenerator.valueToCode(block, 'THEN', Order.CONDITIONAL) || 'null'; const value_else = - JavaScript.valueToCode(block, 'ELSE', JavaScript.ORDER_CONDITIONAL) || + javascriptGenerator.valueToCode(block, 'ELSE', Order.CONDITIONAL) || 'null'; const code = value_if + ' ? ' + value_then + ' : ' + value_else; - return [code, JavaScript.ORDER_CONDITIONAL]; + return [code, Order.CONDITIONAL]; }; diff --git a/generators/javascript/loops.js b/generators/javascript/loops.js index 7e2a90cf0..d11728a1d 100644 --- a/generators/javascript/loops.js +++ b/generators/javascript/loops.js @@ -13,10 +13,10 @@ goog.declareModuleId('Blockly.JavaScript.loops'); import * as stringUtils from '../../core/utils/string.js'; import {NameType} from '../../core/names.js'; -import {javascriptGenerator as JavaScript} from '../javascript.js'; +import {Order, javascriptGenerator} from '../javascript.js'; -JavaScript.forBlock['controls_repeat_ext'] = function(block) { +javascriptGenerator.forBlock['controls_repeat_ext'] = function(block) { // Repeat n times. let repeats; if (block.getField('TIMES')) { @@ -25,18 +25,19 @@ JavaScript.forBlock['controls_repeat_ext'] = function(block) { } else { // External number. repeats = - JavaScript.valueToCode(block, 'TIMES', JavaScript.ORDER_ASSIGNMENT) || + javascriptGenerator.valueToCode(block, 'TIMES', Order.ASSIGNMENT) || '0'; } - let branch = JavaScript.statementToCode(block, 'DO'); - branch = JavaScript.addLoopTrap(branch, block); + let branch = javascriptGenerator.statementToCode(block, 'DO'); + branch = javascriptGenerator.addLoopTrap(branch, block); let code = ''; const loopVar = - JavaScript.nameDB_.getDistinctName('count', NameType.VARIABLE); + javascriptGenerator.nameDB_.getDistinctName('count', NameType.VARIABLE); let endVar = repeats; if (!repeats.match(/^\w+$/) && !stringUtils.isNumber(repeats)) { endVar = - JavaScript.nameDB_.getDistinctName('repeat_end', NameType.VARIABLE); + javascriptGenerator.nameDB_.getDistinctName( + 'repeat_end', NameType.VARIABLE); code += 'var ' + endVar + ' = ' + repeats + ';\n'; } code += 'for (var ' + loopVar + ' = 0; ' + loopVar + ' < ' + endVar + '; ' + @@ -44,36 +45,38 @@ JavaScript.forBlock['controls_repeat_ext'] = function(block) { return code; }; -JavaScript.forBlock['controls_repeat'] = JavaScript.forBlock['controls_repeat_ext']; +javascriptGenerator.forBlock['controls_repeat'] = + javascriptGenerator.forBlock['controls_repeat_ext']; -JavaScript.forBlock['controls_whileUntil'] = function(block) { +javascriptGenerator.forBlock['controls_whileUntil'] = function(block) { // Do while/until loop. const until = block.getFieldValue('MODE') === 'UNTIL'; let argument0 = - JavaScript.valueToCode( + javascriptGenerator.valueToCode( block, 'BOOL', - until ? JavaScript.ORDER_LOGICAL_NOT : JavaScript.ORDER_NONE) || + until ? Order.LOGICAL_NOT : Order.NONE) || 'false'; - let branch = JavaScript.statementToCode(block, 'DO'); - branch = JavaScript.addLoopTrap(branch, block); + let branch = javascriptGenerator.statementToCode(block, 'DO'); + branch = javascriptGenerator.addLoopTrap(branch, block); if (until) { argument0 = '!' + argument0; } return 'while (' + argument0 + ') {\n' + branch + '}\n'; }; -JavaScript.forBlock['controls_for'] = function(block) { +javascriptGenerator.forBlock['controls_for'] = function(block) { // For loop. const variable0 = - JavaScript.nameDB_.getName(block.getFieldValue('VAR'), NameType.VARIABLE); + javascriptGenerator.nameDB_.getName( + block.getFieldValue('VAR'), NameType.VARIABLE); const argument0 = - JavaScript.valueToCode(block, 'FROM', JavaScript.ORDER_ASSIGNMENT) || '0'; + javascriptGenerator.valueToCode(block, 'FROM', Order.ASSIGNMENT) || '0'; const argument1 = - JavaScript.valueToCode(block, 'TO', JavaScript.ORDER_ASSIGNMENT) || '0'; + javascriptGenerator.valueToCode(block, 'TO', Order.ASSIGNMENT) || '0'; const increment = - JavaScript.valueToCode(block, 'BY', JavaScript.ORDER_ASSIGNMENT) || '1'; - let branch = JavaScript.statementToCode(block, 'DO'); - branch = JavaScript.addLoopTrap(branch, block); + javascriptGenerator.valueToCode(block, 'BY', Order.ASSIGNMENT) || '1'; + let branch = javascriptGenerator.statementToCode(block, 'DO'); + branch = javascriptGenerator.addLoopTrap(branch, block); let code; if (stringUtils.isNumber(argument0) && stringUtils.isNumber(argument1) && stringUtils.isNumber(increment)) { @@ -93,19 +96,19 @@ JavaScript.forBlock['controls_for'] = function(block) { // Cache non-trivial values to variables to prevent repeated look-ups. let startVar = argument0; if (!argument0.match(/^\w+$/) && !stringUtils.isNumber(argument0)) { - startVar = JavaScript.nameDB_.getDistinctName( + startVar = javascriptGenerator.nameDB_.getDistinctName( variable0 + '_start', NameType.VARIABLE); code += 'var ' + startVar + ' = ' + argument0 + ';\n'; } let endVar = argument1; if (!argument1.match(/^\w+$/) && !stringUtils.isNumber(argument1)) { - endVar = JavaScript.nameDB_.getDistinctName( + endVar = javascriptGenerator.nameDB_.getDistinctName( variable0 + '_end', NameType.VARIABLE); code += 'var ' + endVar + ' = ' + argument1 + ';\n'; } // Determine loop direction at start, in case one of the bounds // changes during loop execution. - const incVar = JavaScript.nameDB_.getDistinctName( + const incVar = javascriptGenerator.nameDB_.getDistinctName( variable0 + '_inc', NameType.VARIABLE); code += 'var ' + incVar + ' = '; if (stringUtils.isNumber(increment)) { @@ -114,7 +117,7 @@ JavaScript.forBlock['controls_for'] = function(block) { code += 'Math.abs(' + increment + ');\n'; } code += 'if (' + startVar + ' > ' + endVar + ') {\n'; - code += JavaScript.INDENT + incVar + ' = -' + incVar + ';\n'; + code += javascriptGenerator.INDENT + incVar + ' = -' + incVar + ';\n'; code += '}\n'; code += 'for (' + variable0 + ' = ' + startVar + '; ' + incVar + ' >= 0 ? ' + variable0 + ' <= ' + endVar + ' : ' + variable0 + @@ -124,50 +127,54 @@ JavaScript.forBlock['controls_for'] = function(block) { return code; }; -JavaScript.forBlock['controls_forEach'] = function(block) { +javascriptGenerator.forBlock['controls_forEach'] = function(block) { // For each loop. const variable0 = - JavaScript.nameDB_.getName(block.getFieldValue('VAR'), NameType.VARIABLE); + javascriptGenerator.nameDB_.getName( + block.getFieldValue('VAR'), NameType.VARIABLE); const argument0 = - JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_ASSIGNMENT) || + javascriptGenerator.valueToCode(block, 'LIST', Order.ASSIGNMENT) || '[]'; - let branch = JavaScript.statementToCode(block, 'DO'); - branch = JavaScript.addLoopTrap(branch, block); + let branch = javascriptGenerator.statementToCode(block, 'DO'); + branch = javascriptGenerator.addLoopTrap(branch, block); let code = ''; // Cache non-trivial values to variables to prevent repeated look-ups. let listVar = argument0; if (!argument0.match(/^\w+$/)) { - listVar = JavaScript.nameDB_.getDistinctName( + listVar = javascriptGenerator.nameDB_.getDistinctName( variable0 + '_list', NameType.VARIABLE); code += 'var ' + listVar + ' = ' + argument0 + ';\n'; } - const indexVar = JavaScript.nameDB_.getDistinctName( + const indexVar = javascriptGenerator.nameDB_.getDistinctName( variable0 + '_index', NameType.VARIABLE); - branch = JavaScript.INDENT + variable0 + ' = ' + listVar + '[' + indexVar + - '];\n' + branch; + branch = javascriptGenerator.INDENT + variable0 + ' = ' + listVar + + '[' + indexVar + '];\n' + branch; code += 'for (var ' + indexVar + ' in ' + listVar + ') {\n' + branch + '}\n'; return code; }; -JavaScript.forBlock['controls_flow_statements'] = function(block) { +javascriptGenerator.forBlock['controls_flow_statements'] = function(block) { // Flow statements: continue, break. let xfix = ''; - if (JavaScript.STATEMENT_PREFIX) { + if (javascriptGenerator.STATEMENT_PREFIX) { // Automatic prefix insertion is switched off for this block. Add manually. - xfix += JavaScript.injectId(JavaScript.STATEMENT_PREFIX, block); + xfix += javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_PREFIX, block); } - if (JavaScript.STATEMENT_SUFFIX) { + if (javascriptGenerator.STATEMENT_SUFFIX) { // Inject any statement suffix here since the regular one at the end // will not get executed if the break/continue is triggered. - xfix += JavaScript.injectId(JavaScript.STATEMENT_SUFFIX, block); + xfix += javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_SUFFIX, block); } - if (JavaScript.STATEMENT_PREFIX) { + if (javascriptGenerator.STATEMENT_PREFIX) { const loop = block.getSurroundLoop(); if (loop && !loop.suppressPrefixSuffix) { // Inject loop's statement prefix here since the regular one at the end // of the loop will not get executed if 'continue' is triggered. // In the case of 'break', a prefix is needed due to the loop's suffix. - xfix += JavaScript.injectId(JavaScript.STATEMENT_PREFIX, loop); + xfix += javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_PREFIX, loop); } } switch (block.getFieldValue('FLOW')) { diff --git a/generators/javascript/math.js b/generators/javascript/math.js index 5d62aae2b..dff0ba824 100644 --- a/generators/javascript/math.js +++ b/generators/javascript/math.js @@ -13,63 +13,63 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.JavaScript.math'); import {NameType} from '../../core/names.js'; -import {javascriptGenerator as JavaScript} from '../javascript.js'; +import {Order, javascriptGenerator} from '../javascript.js'; -JavaScript.forBlock['math_number'] = function(block) { +javascriptGenerator.forBlock['math_number'] = function(block) { // Numeric value. const code = Number(block.getFieldValue('NUM')); - const order = code >= 0 ? JavaScript.ORDER_ATOMIC : - JavaScript.ORDER_UNARY_NEGATION; + const order = code >= 0 ? Order.ATOMIC : + Order.UNARY_NEGATION; return [code, order]; }; -JavaScript.forBlock['math_arithmetic'] = function(block) { +javascriptGenerator.forBlock['math_arithmetic'] = function(block) { // Basic arithmetic operators, and power. const OPERATORS = { - 'ADD': [' + ', JavaScript.ORDER_ADDITION], - 'MINUS': [' - ', JavaScript.ORDER_SUBTRACTION], - 'MULTIPLY': [' * ', JavaScript.ORDER_MULTIPLICATION], - 'DIVIDE': [' / ', JavaScript.ORDER_DIVISION], - 'POWER': [null, JavaScript.ORDER_NONE], // Handle power separately. + 'ADD': [' + ', Order.ADDITION], + 'MINUS': [' - ', Order.SUBTRACTION], + 'MULTIPLY': [' * ', Order.MULTIPLICATION], + 'DIVIDE': [' / ', Order.DIVISION], + 'POWER': [null, Order.NONE], // Handle power separately. }; const tuple = OPERATORS[block.getFieldValue('OP')]; const operator = tuple[0]; const order = tuple[1]; - const argument0 = JavaScript.valueToCode(block, 'A', order) || '0'; - const argument1 = JavaScript.valueToCode(block, 'B', order) || '0'; + const argument0 = javascriptGenerator.valueToCode(block, 'A', order) || '0'; + const argument1 = javascriptGenerator.valueToCode(block, 'B', order) || '0'; let code; // Power in JavaScript requires a special case since it has no operator. if (!operator) { code = 'Math.pow(' + argument0 + ', ' + argument1 + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } code = argument0 + operator + argument1; return [code, order]; }; -JavaScript.forBlock['math_single'] = function(block) { +javascriptGenerator.forBlock['math_single'] = function(block) { // Math operators with single operand. const operator = block.getFieldValue('OP'); let code; let arg; if (operator === 'NEG') { // Negation is a special case given its different operator precedence. - arg = JavaScript.valueToCode(block, 'NUM', - JavaScript.ORDER_UNARY_NEGATION) || '0'; + arg = javascriptGenerator.valueToCode(block, 'NUM', + Order.UNARY_NEGATION) || '0'; if (arg[0] === '-') { // --3 is not legal in JS. arg = ' ' + arg; } code = '-' + arg; - return [code, JavaScript.ORDER_UNARY_NEGATION]; + return [code, Order.UNARY_NEGATION]; } if (operator === 'SIN' || operator === 'COS' || operator === 'TAN') { - arg = JavaScript.valueToCode(block, 'NUM', - JavaScript.ORDER_DIVISION) || '0'; + arg = javascriptGenerator.valueToCode(block, 'NUM', + Order.DIVISION) || '0'; } else { - arg = JavaScript.valueToCode(block, 'NUM', - JavaScript.ORDER_NONE) || '0'; + arg = javascriptGenerator.valueToCode(block, 'NUM', + Order.NONE) || '0'; } // First, handle cases which generate values that don't need parentheses // wrapping the code. @@ -109,7 +109,7 @@ JavaScript.forBlock['math_single'] = function(block) { break; } if (code) { - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } // Second, handle cases which generate values that may need parentheses // wrapping the code. @@ -129,46 +129,47 @@ JavaScript.forBlock['math_single'] = function(block) { default: throw Error('Unknown math operator: ' + operator); } - return [code, JavaScript.ORDER_DIVISION]; + return [code, Order.DIVISION]; }; -JavaScript.forBlock['math_constant'] = function(block) { +javascriptGenerator.forBlock['math_constant'] = function(block) { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. const CONSTANTS = { - 'PI': ['Math.PI', JavaScript.ORDER_MEMBER], - 'E': ['Math.E', JavaScript.ORDER_MEMBER], - 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', JavaScript.ORDER_DIVISION], - 'SQRT2': ['Math.SQRT2', JavaScript.ORDER_MEMBER], - 'SQRT1_2': ['Math.SQRT1_2', JavaScript.ORDER_MEMBER], - 'INFINITY': ['Infinity', JavaScript.ORDER_ATOMIC], + 'PI': ['Math.PI', Order.MEMBER], + 'E': ['Math.E', Order.MEMBER], + 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', Order.DIVISION], + 'SQRT2': ['Math.SQRT2', Order.MEMBER], + 'SQRT1_2': ['Math.SQRT1_2', Order.MEMBER], + 'INFINITY': ['Infinity', Order.ATOMIC], }; return CONSTANTS[block.getFieldValue('CONSTANT')]; }; -JavaScript.forBlock['math_number_property'] = function(block) { +javascriptGenerator.forBlock['math_number_property'] = function(block) { // Check if a number is even, odd, prime, whole, positive, or negative // or if it is divisible by certain number. Returns true or false. const PROPERTIES = { - 'EVEN': [' % 2 === 0', JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY], - 'ODD': [' % 2 === 1', JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY], - 'WHOLE': [' % 1 === 0', JavaScript.ORDER_MODULUS, - JavaScript.ORDER_EQUALITY], - 'POSITIVE': [' > 0', JavaScript.ORDER_RELATIONAL, - JavaScript.ORDER_RELATIONAL], - 'NEGATIVE': [' < 0', JavaScript.ORDER_RELATIONAL, - JavaScript.ORDER_RELATIONAL], - 'DIVISIBLE_BY': [null, JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY], - 'PRIME': [null, JavaScript.ORDER_NONE, JavaScript.ORDER_FUNCTION_CALL], + 'EVEN': [' % 2 === 0', Order.MODULUS, Order.EQUALITY], + 'ODD': [' % 2 === 1', Order.MODULUS, Order.EQUALITY], + 'WHOLE': [' % 1 === 0', Order.MODULUS, + Order.EQUALITY], + 'POSITIVE': [' > 0', Order.RELATIONAL, + Order.RELATIONAL], + 'NEGATIVE': [' < 0', Order.RELATIONAL, + Order.RELATIONAL], + 'DIVISIBLE_BY': [null, Order.MODULUS, Order.EQUALITY], + 'PRIME': [null, Order.NONE, Order.FUNCTION_CALL], }; const dropdownProperty = block.getFieldValue('PROPERTY'); const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; - const numberToCheck = JavaScript.valueToCode(block, 'NUMBER_TO_CHECK', - inputOrder) || '0'; + const numberToCheck = + javascriptGenerator.valueToCode(block, 'NUMBER_TO_CHECK', inputOrder) || + '0'; let code; if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - const functionName = JavaScript.provideFunction_('mathIsPrime', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(n) { + const functionName = javascriptGenerator.provideFunction_('mathIsPrime', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(n) { // https://en.wikipedia.org/wiki/Primality_test#Naive_methods if (n == 2 || n == 3) { return true; @@ -189,8 +190,8 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(n) { `); code = functionName + '(' + numberToCheck + ')'; } else if (dropdownProperty === 'DIVISIBLE_BY') { - const divisor = JavaScript.valueToCode(block, 'DIVISOR', - JavaScript.ORDER_MODULUS) || '0'; + const divisor = javascriptGenerator.valueToCode(block, 'DIVISOR', + Order.MODULUS) || '0'; code = numberToCheck + ' % ' + divisor + ' === 0'; } else { code = numberToCheck + suffix; @@ -198,58 +199,60 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(n) { return [code, outputOrder]; }; -JavaScript.forBlock['math_change'] = function(block) { +javascriptGenerator.forBlock['math_change'] = function(block) { // Add to a variable in place. - const argument0 = JavaScript.valueToCode(block, 'DELTA', - JavaScript.ORDER_ADDITION) || '0'; - const varName = JavaScript.nameDB_.getName( + const argument0 = javascriptGenerator.valueToCode(block, 'DELTA', + Order.ADDITION) || '0'; + const varName = javascriptGenerator.nameDB_.getName( block.getFieldValue('VAR'), NameType.VARIABLE); return varName + ' = (typeof ' + varName + ' === \'number\' ? ' + varName + ' : 0) + ' + argument0 + ';\n'; }; // Rounding functions have a single operand. -JavaScript.forBlock['math_round'] = JavaScript.forBlock['math_single']; +javascriptGenerator.forBlock['math_round'] = + javascriptGenerator.forBlock['math_single']; // Trigonometry functions have a single operand. -JavaScript.forBlock['math_trig'] = JavaScript.forBlock['math_single']; +javascriptGenerator.forBlock['math_trig'] = + javascriptGenerator.forBlock['math_single']; -JavaScript.forBlock['math_on_list'] = function(block) { +javascriptGenerator.forBlock['math_on_list'] = function(block) { // Math functions for lists. const func = block.getFieldValue('OP'); let list; let code; switch (func) { case 'SUM': - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_MEMBER) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.MEMBER) || '[]'; code = list + '.reduce(function(x, y) {return x + y;}, 0)'; break; case 'MIN': - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_NONE) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.NONE) || '[]'; code = 'Math.min.apply(null, ' + list + ')'; break; case 'MAX': - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_NONE) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.NONE) || '[]'; code = 'Math.max.apply(null, ' + list + ')'; break; case 'AVERAGE': { // mathMean([null,null,1,3]) === 2.0. - const functionName = JavaScript.provideFunction_('mathMean', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { + const functionName = javascriptGenerator.provideFunction_('mathMean', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(myList) { return myList.reduce(function(x, y) {return x + y;}, 0) / myList.length; } `); - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_NONE) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'MEDIAN': { // mathMedian([null,null,1,3]) === 2.0. - const functionName = JavaScript.provideFunction_('mathMedian', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { + const functionName = javascriptGenerator.provideFunction_('mathMedian', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(myList) { var localList = myList.filter(function (x) {return typeof x === 'number';}); if (!localList.length) return null; localList.sort(function(a, b) {return b - a;}); @@ -260,8 +263,8 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { } } `); - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_NONE) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } @@ -269,8 +272,8 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { // As a list of numbers can contain more than one mode, // the returned result is provided as an array. // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. - const functionName = JavaScript.provideFunction_('mathModes', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(values) { + const functionName = javascriptGenerator.provideFunction_('mathModes', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(values) { var modes = []; var counts = []; var maxCount = 0; @@ -299,14 +302,15 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(values) { return modes; } `); - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_NONE) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { - const functionName = JavaScript.provideFunction_('mathStandardDeviation', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(numbers) { + const functionName = + javascriptGenerator.provideFunction_('mathStandardDeviation', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(numbers) { var n = numbers.length; if (!n) return null; var mean = numbers.reduce(function(x, y) {return x + y;}) / n; @@ -318,60 +322,61 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(numbers) { return Math.sqrt(variance); } `); - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_NONE) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'RANDOM': { - const functionName = JavaScript.provideFunction_('mathRandomList', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list) { + const functionName = + javascriptGenerator.provideFunction_('mathRandomList', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(list) { var x = Math.floor(Math.random() * list.length); return list[x]; } `); - list = JavaScript.valueToCode(block, 'LIST', - JavaScript.ORDER_NONE) || '[]'; + list = javascriptGenerator.valueToCode(block, 'LIST', + Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } default: throw Error('Unknown operator: ' + func); } - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['math_modulo'] = function(block) { +javascriptGenerator.forBlock['math_modulo'] = function(block) { // Remainder computation. - const argument0 = JavaScript.valueToCode(block, 'DIVIDEND', - JavaScript.ORDER_MODULUS) || '0'; - const argument1 = JavaScript.valueToCode(block, 'DIVISOR', - JavaScript.ORDER_MODULUS) || '0'; + const argument0 = javascriptGenerator.valueToCode(block, 'DIVIDEND', + Order.MODULUS) || '0'; + const argument1 = javascriptGenerator.valueToCode(block, 'DIVISOR', + Order.MODULUS) || '0'; const code = argument0 + ' % ' + argument1; - return [code, JavaScript.ORDER_MODULUS]; + return [code, Order.MODULUS]; }; -JavaScript.forBlock['math_constrain'] = function(block) { +javascriptGenerator.forBlock['math_constrain'] = function(block) { // Constrain a number between two limits. - const argument0 = JavaScript.valueToCode(block, 'VALUE', - JavaScript.ORDER_NONE) || '0'; - const argument1 = JavaScript.valueToCode(block, 'LOW', - JavaScript.ORDER_NONE) || '0'; - const argument2 = JavaScript.valueToCode(block, 'HIGH', - JavaScript.ORDER_NONE) || 'Infinity'; + const argument0 = javascriptGenerator.valueToCode(block, 'VALUE', + Order.NONE) || '0'; + const argument1 = javascriptGenerator.valueToCode(block, 'LOW', + Order.NONE) || '0'; + const argument2 = javascriptGenerator.valueToCode(block, 'HIGH', + Order.NONE) || 'Infinity'; const code = 'Math.min(Math.max(' + argument0 + ', ' + argument1 + '), ' + argument2 + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['math_random_int'] = function(block) { +javascriptGenerator.forBlock['math_random_int'] = function(block) { // Random integer between [X] and [Y]. - const argument0 = JavaScript.valueToCode(block, 'FROM', - JavaScript.ORDER_NONE) || '0'; - const argument1 = JavaScript.valueToCode(block, 'TO', - JavaScript.ORDER_NONE) || '0'; - const functionName = JavaScript.provideFunction_('mathRandomInt', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(a, b) { + const argument0 = javascriptGenerator.valueToCode(block, 'FROM', + Order.NONE) || '0'; + const argument1 = javascriptGenerator.valueToCode(block, 'TO', + Order.NONE) || '0'; + const functionName = javascriptGenerator.provideFunction_('mathRandomInt', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(a, b) { if (a > b) { // Swap a and b to ensure a is smaller. var c = a; @@ -382,20 +387,20 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(a, b) { } `); const code = functionName + '(' + argument0 + ', ' + argument1 + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['math_random_float'] = function(block) { +javascriptGenerator.forBlock['math_random_float'] = function(block) { // Random fraction between 0 and 1. - return ['Math.random()', JavaScript.ORDER_FUNCTION_CALL]; + return ['Math.random()', Order.FUNCTION_CALL]; }; -JavaScript.forBlock['math_atan2'] = function(block) { +javascriptGenerator.forBlock['math_atan2'] = function(block) { // Arctangent of point (X, Y) in degrees from -180 to 180. - const argument0 = JavaScript.valueToCode(block, 'X', - JavaScript.ORDER_NONE) || '0'; - const argument1 = JavaScript.valueToCode(block, 'Y', - JavaScript.ORDER_NONE) || '0'; + const argument0 = javascriptGenerator.valueToCode(block, 'X', + Order.NONE) || '0'; + const argument1 = javascriptGenerator.valueToCode(block, 'Y', + Order.NONE) || '0'; return ['Math.atan2(' + argument1 + ', ' + argument0 + ') / Math.PI * 180', - JavaScript.ORDER_DIVISION]; + Order.DIVISION]; }; diff --git a/generators/javascript/procedures.js b/generators/javascript/procedures.js index dbb59bc18..fd4d896ce 100644 --- a/generators/javascript/procedures.js +++ b/generators/javascript/procedures.js @@ -12,98 +12,112 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.JavaScript.procedures'); import {NameType} from '../../core/names.js'; -import {javascriptGenerator as JavaScript} from '../javascript.js'; +import {Order, javascriptGenerator} from '../javascript.js'; -JavaScript.forBlock['procedures_defreturn'] = function(block) { +javascriptGenerator.forBlock['procedures_defreturn'] = function(block) { // Define a procedure with a return value. - const funcName = JavaScript.nameDB_.getName( + const funcName = javascriptGenerator.nameDB_.getName( block.getFieldValue('NAME'), NameType.PROCEDURE); let xfix1 = ''; - if (JavaScript.STATEMENT_PREFIX) { - xfix1 += JavaScript.injectId(JavaScript.STATEMENT_PREFIX, block); + if (javascriptGenerator.STATEMENT_PREFIX) { + xfix1 += javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_PREFIX, block); } - if (JavaScript.STATEMENT_SUFFIX) { - xfix1 += JavaScript.injectId(JavaScript.STATEMENT_SUFFIX, block); + if (javascriptGenerator.STATEMENT_SUFFIX) { + xfix1 += javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_SUFFIX, block); } if (xfix1) { - xfix1 = JavaScript.prefixLines(xfix1, JavaScript.INDENT); + xfix1 = javascriptGenerator.prefixLines(xfix1, javascriptGenerator.INDENT); } let loopTrap = ''; - if (JavaScript.INFINITE_LOOP_TRAP) { - loopTrap = JavaScript.prefixLines( - JavaScript.injectId(JavaScript.INFINITE_LOOP_TRAP, block), - JavaScript.INDENT); + if (javascriptGenerator.INFINITE_LOOP_TRAP) { + loopTrap = javascriptGenerator.prefixLines( + javascriptGenerator.injectId( + javascriptGenerator.INFINITE_LOOP_TRAP, block), + javascriptGenerator.INDENT); } - const branch = JavaScript.statementToCode(block, 'STACK'); + const branch = javascriptGenerator.statementToCode(block, 'STACK'); let returnValue = - JavaScript.valueToCode(block, 'RETURN', JavaScript.ORDER_NONE) || ''; + javascriptGenerator.valueToCode(block, 'RETURN', Order.NONE) || ''; let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return. xfix2 = xfix1; } if (returnValue) { - returnValue = JavaScript.INDENT + 'return ' + returnValue + ';\n'; + returnValue = javascriptGenerator.INDENT + 'return ' + returnValue + ';\n'; } const args = []; const variables = block.getVars(); for (let i = 0; i < variables.length; i++) { - args[i] = JavaScript.nameDB_.getName(variables[i], NameType.VARIABLE); + args[i] = + javascriptGenerator.nameDB_.getName(variables[i], NameType.VARIABLE); } let code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + xfix1 + loopTrap + branch + xfix2 + returnValue + '}'; - code = JavaScript.scrub_(block, code); + code = javascriptGenerator.scrub_(block, code); // Add % so as not to collide with helper functions in definitions list. - JavaScript.definitions_['%' + funcName] = code; + javascriptGenerator.definitions_['%' + funcName] = code; return null; }; // Defining a procedure without a return value uses the same generator as // a procedure with a return value. -JavaScript.forBlock['procedures_defnoreturn'] = JavaScript.forBlock['procedures_defreturn']; +javascriptGenerator.forBlock['procedures_defnoreturn'] = + javascriptGenerator.forBlock['procedures_defreturn']; -JavaScript.forBlock['procedures_callreturn'] = function(block) { +javascriptGenerator.forBlock['procedures_callreturn'] = function(block) { // Call a procedure with a return value. - const funcName = JavaScript.nameDB_.getName( + const funcName = javascriptGenerator.nameDB_.getName( block.getFieldValue('NAME'), NameType.PROCEDURE); const args = []; const variables = block.getVars(); for (let i = 0; i < variables.length; i++) { - args[i] = JavaScript.valueToCode(block, 'ARG' + i, JavaScript.ORDER_NONE) || + args[i] = javascriptGenerator.valueToCode(block, 'ARG' + i, Order.NONE) || 'null'; } const code = funcName + '(' + args.join(', ') + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['procedures_callnoreturn'] = function(block) { +javascriptGenerator.forBlock['procedures_callnoreturn'] = function(block) { // Call a procedure with no return value. // Generated code is for a function call as a statement is the same as a // function call as a value, with the addition of line ending. - const tuple = JavaScript.forBlock['procedures_callreturn'](block); + const tuple = javascriptGenerator.forBlock['procedures_callreturn'](block); return tuple[0] + ';\n'; }; -JavaScript.forBlock['procedures_ifreturn'] = function(block) { +javascriptGenerator.forBlock['procedures_callnoreturn'] = function(block) { + // Call a procedure with no return value. + // Generated code is for a function call as a statement is the same as a + // function call as a value, with the addition of line ending. + const tuple = javascriptGenerator.forBlock['procedures_callreturn'](block); + return tuple[0] + ';\n'; +}; + +javascriptGenerator.forBlock['procedures_ifreturn'] = function(block) { // Conditionally return value from a procedure. const condition = - JavaScript.valueToCode(block, 'CONDITION', JavaScript.ORDER_NONE) || + javascriptGenerator.valueToCode(block, 'CONDITION', Order.NONE) || 'false'; let code = 'if (' + condition + ') {\n'; - if (JavaScript.STATEMENT_SUFFIX) { + if (javascriptGenerator.STATEMENT_SUFFIX) { // Inject any statement suffix here since the regular one at the end // will not get executed if the return is triggered. - code += JavaScript.prefixLines( - JavaScript.injectId(JavaScript.STATEMENT_SUFFIX, block), - JavaScript.INDENT); + code += javascriptGenerator.prefixLines( + javascriptGenerator.injectId( + javascriptGenerator.STATEMENT_SUFFIX, block), + javascriptGenerator.INDENT); } if (block.hasReturnValue_) { const value = - JavaScript.valueToCode(block, 'VALUE', JavaScript.ORDER_NONE) || 'null'; - code += JavaScript.INDENT + 'return ' + value + ';\n'; + javascriptGenerator.valueToCode(block, 'VALUE', Order.NONE) || 'null'; + code += javascriptGenerator.INDENT + 'return ' + value + ';\n'; } else { - code += JavaScript.INDENT + 'return;\n'; + code += javascriptGenerator.INDENT + 'return;\n'; } code += '}\n'; return code; diff --git a/generators/javascript/text.js b/generators/javascript/text.js index 149f823ee..ea3edb4bb 100644 --- a/generators/javascript/text.js +++ b/generators/javascript/text.js @@ -12,7 +12,7 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.JavaScript.texts'); import {NameType} from '../../core/names.js'; -import {javascriptGenerator as JavaScript} from '../javascript.js'; +import {Order, javascriptGenerator} from '../javascript.js'; /** @@ -29,9 +29,9 @@ const strRegExp = /^\s*'([^']|\\')*'\s*$/; */ const forceString = function(value) { if (strRegExp.test(value)) { - return [value, JavaScript.ORDER_ATOMIC]; + return [value, Order.ATOMIC]; } - return ['String(' + value + ')', JavaScript.ORDER_FUNCTION_CALL]; + return ['String(' + value + ')', Order.FUNCTION_CALL]; }; /** @@ -53,158 +53,162 @@ const getSubstringIndex = function(stringName, where, opt_at) { } }; -JavaScript.forBlock['text'] = function(block) { +javascriptGenerator.forBlock['text'] = function(block) { // Text value. - const code = JavaScript.quote_(block.getFieldValue('TEXT')); - return [code, JavaScript.ORDER_ATOMIC]; + const code = javascriptGenerator.quote_(block.getFieldValue('TEXT')); + return [code, Order.ATOMIC]; }; -JavaScript.forBlock['text_multiline'] = function(block) { +javascriptGenerator.forBlock['text_multiline'] = function(block) { // Text value. - const code = JavaScript.multiline_quote_(block.getFieldValue('TEXT')); - const order = code.indexOf('+') !== -1 ? JavaScript.ORDER_ADDITION : - JavaScript.ORDER_ATOMIC; + const code = + javascriptGenerator.multiline_quote_(block.getFieldValue('TEXT')); + const order = code.indexOf('+') !== -1 ? Order.ADDITION : + Order.ATOMIC; return [code, order]; }; -JavaScript.forBlock['text_join'] = function(block) { +javascriptGenerator.forBlock['text_join'] = function(block) { // Create a string made up of any number of elements of any type. switch (block.itemCount_) { case 0: - return ["''", JavaScript.ORDER_ATOMIC]; + return ["''", Order.ATOMIC]; case 1: { - const element = JavaScript.valueToCode(block, 'ADD0', - JavaScript.ORDER_NONE) || "''"; + const element = javascriptGenerator.valueToCode(block, 'ADD0', + Order.NONE) || "''"; const codeAndOrder = forceString(element); return codeAndOrder; } case 2: { - const element0 = JavaScript.valueToCode(block, 'ADD0', - JavaScript.ORDER_NONE) || "''"; - const element1 = JavaScript.valueToCode(block, 'ADD1', - JavaScript.ORDER_NONE) || "''"; + const element0 = javascriptGenerator.valueToCode(block, 'ADD0', + Order.NONE) || "''"; + const element1 = javascriptGenerator.valueToCode(block, 'ADD1', + Order.NONE) || "''"; const code = forceString(element0)[0] + ' + ' + forceString(element1)[0]; - return [code, JavaScript.ORDER_ADDITION]; + return [code, Order.ADDITION]; } default: { const elements = new Array(block.itemCount_); for (let i = 0; i < block.itemCount_; i++) { - elements[i] = JavaScript.valueToCode(block, 'ADD' + i, - JavaScript.ORDER_NONE) || "''"; + elements[i] = javascriptGenerator.valueToCode(block, 'ADD' + i, + Order.NONE) || "''"; } const code = '[' + elements.join(',') + '].join(\'\')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } } }; -JavaScript.forBlock['text_append'] = function(block) { +javascriptGenerator.forBlock['text_append'] = function(block) { // Append to a variable in place. - const varName = JavaScript.nameDB_.getName( + const varName = javascriptGenerator.nameDB_.getName( block.getFieldValue('VAR'), NameType.VARIABLE); - const value = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || "''"; + const value = javascriptGenerator.valueToCode(block, 'TEXT', + Order.NONE) || "''"; const code = varName + ' += ' + forceString(value)[0] + ';\n'; return code; }; -JavaScript.forBlock['text_length'] = function(block) { +javascriptGenerator.forBlock['text_length'] = function(block) { // String or array length. - const text = JavaScript.valueToCode(block, 'VALUE', - JavaScript.ORDER_MEMBER) || "''"; - return [text + '.length', JavaScript.ORDER_MEMBER]; + const text = javascriptGenerator.valueToCode(block, 'VALUE', + Order.MEMBER) || "''"; + return [text + '.length', Order.MEMBER]; }; -JavaScript.forBlock['text_isEmpty'] = function(block) { +javascriptGenerator.forBlock['text_isEmpty'] = function(block) { // Is the string null or array empty? - const text = JavaScript.valueToCode(block, 'VALUE', - JavaScript.ORDER_MEMBER) || "''"; - return ['!' + text + '.length', JavaScript.ORDER_LOGICAL_NOT]; + const text = javascriptGenerator.valueToCode(block, 'VALUE', + Order.MEMBER) || "''"; + return ['!' + text + '.length', Order.LOGICAL_NOT]; }; -JavaScript.forBlock['text_indexOf'] = function(block) { +javascriptGenerator.forBlock['text_indexOf'] = function(block) { // Search the text for a substring. const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; - const substring = JavaScript.valueToCode(block, 'FIND', - JavaScript.ORDER_NONE) || "''"; - const text = JavaScript.valueToCode(block, 'VALUE', - JavaScript.ORDER_MEMBER) || "''"; + const substring = javascriptGenerator.valueToCode(block, 'FIND', + Order.NONE) || "''"; + const text = javascriptGenerator.valueToCode(block, 'VALUE', + Order.MEMBER) || "''"; const code = text + '.' + operator + '(' + substring + ')'; // Adjust index if using one-based indices. if (block.workspace.options.oneBasedIndex) { - return [code + ' + 1', JavaScript.ORDER_ADDITION]; + return [code + ' + 1', Order.ADDITION]; } - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['text_charAt'] = function(block) { +javascriptGenerator.forBlock['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. const where = block.getFieldValue('WHERE') || 'FROM_START'; - const textOrder = (where === 'RANDOM') ? JavaScript.ORDER_NONE : - JavaScript.ORDER_MEMBER; - const text = JavaScript.valueToCode(block, 'VALUE', textOrder) || "''"; + const textOrder = (where === 'RANDOM') ? Order.NONE : + Order.MEMBER; + const text = + javascriptGenerator.valueToCode(block, 'VALUE', textOrder) || "''"; switch (where) { case 'FIRST': { const code = text + '.charAt(0)'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } case 'LAST': { const code = text + '.slice(-1)'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } case 'FROM_START': { - const at = JavaScript.getAdjusted(block, 'AT'); + const at = javascriptGenerator.getAdjusted(block, 'AT'); // Adjust index if using one-based indices. const code = text + '.charAt(' + at + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } case 'FROM_END': { - const at = JavaScript.getAdjusted(block, 'AT', 1, true); + const at = javascriptGenerator.getAdjusted(block, 'AT', 1, true); const code = text + '.slice(' + at + ').charAt(0)'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } case 'RANDOM': { - const functionName = JavaScript.provideFunction_('textRandomLetter', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(text) { + const functionName = + javascriptGenerator.provideFunction_('textRandomLetter', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(text) { var x = Math.floor(Math.random() * text.length); return text[x]; } `); const code = functionName + '(' + text + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; } } throw Error('Unhandled option (text_charAt).'); }; -JavaScript.forBlock['text_getSubstring'] = function(block) { +javascriptGenerator.forBlock['text_getSubstring'] = function(block) { // Get substring. const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); const requiresLengthCall = (where1 !== 'FROM_END' && where1 !== 'LAST' && where2 !== 'FROM_END' && where2 !== 'LAST'); - const textOrder = requiresLengthCall ? JavaScript.ORDER_MEMBER : - JavaScript.ORDER_NONE; - const text = JavaScript.valueToCode(block, 'STRING', textOrder) || "''"; + const textOrder = requiresLengthCall ? Order.MEMBER : + Order.NONE; + const text = + javascriptGenerator.valueToCode(block, 'STRING', textOrder) || "''"; let code; if (where1 === 'FIRST' && where2 === 'LAST') { code = text; - return [code, JavaScript.ORDER_NONE]; + return [code, Order.NONE]; } else if (text.match(/^'?\w+'?$/) || requiresLengthCall) { // If the text is a variable or literal or doesn't require a call for // length, don't generate a helper function. let at1; switch (where1) { case 'FROM_START': - at1 = JavaScript.getAdjusted(block, 'AT1'); + at1 = javascriptGenerator.getAdjusted(block, 'AT1'); break; case 'FROM_END': - at1 = JavaScript.getAdjusted(block, 'AT1', 1, false, - JavaScript.ORDER_SUBTRACTION); + at1 = javascriptGenerator.getAdjusted(block, 'AT1', 1, false, + Order.SUBTRACTION); at1 = text + '.length - ' + at1; break; case 'FIRST': @@ -216,11 +220,11 @@ JavaScript.forBlock['text_getSubstring'] = function(block) { let at2; switch (where2) { case 'FROM_START': - at2 = JavaScript.getAdjusted(block, 'AT2', 1); + at2 = javascriptGenerator.getAdjusted(block, 'AT2', 1); break; case 'FROM_END': - at2 = JavaScript.getAdjusted(block, 'AT2', 0, false, - JavaScript.ORDER_SUBTRACTION); + at2 = javascriptGenerator.getAdjusted(block, 'AT2', 0, false, + Order.SUBTRACTION); at2 = text + '.length - ' + at2; break; case 'LAST': @@ -231,8 +235,8 @@ JavaScript.forBlock['text_getSubstring'] = function(block) { } code = text + '.slice(' + at1 + ', ' + at2 + ')'; } else { - const at1 = JavaScript.getAdjusted(block, 'AT1'); - const at2 = JavaScript.getAdjusted(block, 'AT2'); + const at1 = javascriptGenerator.getAdjusted(block, 'AT1'); + const at2 = javascriptGenerator.getAdjusted(block, 'AT2'); const wherePascalCase = {'FIRST': 'First', 'LAST': 'Last', 'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'}; // The value for 'FROM_END' and'FROM_START' depends on `at` so @@ -241,9 +245,9 @@ JavaScript.forBlock['text_getSubstring'] = function(block) { (where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : ''; const at2Param = (where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : ''; - const functionName = JavaScript.provideFunction_( + const functionName = javascriptGenerator.provideFunction_( 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { var start = ${getSubstringIndex('sequence', where1, 'at1')}; var end = ${getSubstringIndex('sequence', where2, 'at2')} + 1; return sequence.slice(start, end); @@ -256,10 +260,10 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param} ((where2 === 'FROM_END' || where2 === 'FROM_START') ? ', ' + at2 : '') + ')'; } - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['text_changeCase'] = function(block) { +javascriptGenerator.forBlock['text_changeCase'] = function(block) { // Change capitalization. const OPERATORS = { 'UPPERCASE': '.toUpperCase()', @@ -267,26 +271,28 @@ JavaScript.forBlock['text_changeCase'] = function(block) { 'TITLECASE': null, }; const operator = OPERATORS[block.getFieldValue('CASE')]; - const textOrder = operator ? JavaScript.ORDER_MEMBER : JavaScript.ORDER_NONE; - const text = JavaScript.valueToCode(block, 'TEXT', textOrder) || "''"; + const textOrder = operator ? Order.MEMBER : Order.NONE; + const text = + javascriptGenerator.valueToCode(block, 'TEXT', textOrder) || "''"; let code; if (operator) { - // Upper and lower case are functions built into JavaScript. + // Upper and lower case are functions built into javascriptGenerator. code = text + operator; } else { // Title case is not a native JavaScript function. Define one. - const functionName = JavaScript.provideFunction_('textToTitleCase', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(str) { + const functionName = + javascriptGenerator.provideFunction_('textToTitleCase', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(str) { return str.replace(/\\S+/g, function(txt) {return txt[0].toUpperCase() + txt.substring(1).toLowerCase();}); } `); code = functionName + '(' + text + ')'; } - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['text_trim'] = function(block) { +javascriptGenerator.forBlock['text_trim'] = function(block) { // Trim spaces. const OPERATORS = { 'LEFT': ".replace(/^[\\s\\xa0]+/, '')", @@ -294,45 +300,46 @@ JavaScript.forBlock['text_trim'] = function(block) { 'BOTH': '.trim()', }; const operator = OPERATORS[block.getFieldValue('MODE')]; - const text = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_MEMBER) || "''"; - return [text + operator, JavaScript.ORDER_FUNCTION_CALL]; + const text = javascriptGenerator.valueToCode(block, 'TEXT', + Order.MEMBER) || "''"; + return [text + operator, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['text_print'] = function(block) { +javascriptGenerator.forBlock['text_print'] = function(block) { // Print statement. - const msg = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || "''"; + const msg = javascriptGenerator.valueToCode(block, 'TEXT', + Order.NONE) || "''"; return 'window.alert(' + msg + ');\n'; }; -JavaScript.forBlock['text_prompt_ext'] = function(block) { +javascriptGenerator.forBlock['text_prompt_ext'] = function(block) { // Prompt function. let msg; if (block.getField('TEXT')) { // Internal message. - msg = JavaScript.quote_(block.getFieldValue('TEXT')); + msg = javascriptGenerator.quote_(block.getFieldValue('TEXT')); } else { // External message. - msg = JavaScript.valueToCode(block, 'TEXT', JavaScript.ORDER_NONE) || "''"; + msg = javascriptGenerator.valueToCode(block, 'TEXT', Order.NONE) || "''"; } let code = 'window.prompt(' + msg + ')'; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; if (toNumber) { code = 'Number(' + code + ')'; } - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['text_prompt'] = JavaScript.forBlock['text_prompt_ext']; +javascriptGenerator.forBlock['text_prompt'] = + javascriptGenerator.forBlock['text_prompt_ext']; -JavaScript.forBlock['text_count'] = function(block) { - const text = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || "''"; - const sub = JavaScript.valueToCode(block, 'SUB', - JavaScript.ORDER_NONE) || "''"; - const functionName = JavaScript.provideFunction_('textCount', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) { +javascriptGenerator.forBlock['text_count'] = function(block) { + const text = javascriptGenerator.valueToCode(block, 'TEXT', + Order.NONE) || "''"; + const sub = javascriptGenerator.valueToCode(block, 'SUB', + Order.NONE) || "''"; + const functionName = javascriptGenerator.provideFunction_('textCount', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) { if (needle.length === 0) { return haystack.length + 1; } else { @@ -341,31 +348,31 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) { } `); const code = functionName + '(' + text + ', ' + sub + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; + return [code, Order.FUNCTION_CALL]; }; -JavaScript.forBlock['text_replace'] = function(block) { - const text = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || "''"; - const from = JavaScript.valueToCode(block, 'FROM', - JavaScript.ORDER_NONE) || "''"; - const to = JavaScript.valueToCode(block, 'TO', JavaScript.ORDER_NONE) || "''"; +javascriptGenerator.forBlock['text_replace'] = function(block) { + const text = javascriptGenerator.valueToCode(block, 'TEXT', + Order.NONE) || "''"; + const from = javascriptGenerator.valueToCode(block, 'FROM', + Order.NONE) || "''"; + const to = javascriptGenerator.valueToCode(block, 'TO', Order.NONE) || "''"; // The regex escaping code below is taken from the implementation of // goog.string.regExpEscape. - const functionName = JavaScript.provideFunction_('textReplace', ` -function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle, replacement) { + const functionName = javascriptGenerator.provideFunction_('textReplace', ` +function ${javascriptGenerator.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle, replacement) { needle = needle.replace(/([-()\\[\\]{}+?*.$\\^|,:#