From 0e318e42121b45394336114109b77248df4c8afa Mon Sep 17 00:00:00 2001
From: toebes-extreme
Date: Wed, 27 May 2015 15:07:08 -0400
Subject: [PATCH 01/84] Upgrade FieldTextArea to latest Blockly version
Refactored FieldTextArea to be based on FieldTextInput instead of Field
in order to allow it to utilize the editing from FieldTextInput
Updated code to latest version of Blockly including RTL usage
---
blocks/text.js | 22 ++++
core/blockly.js | 1 +
core/field_textarea.js | 228 ++++++++++++++++++++++++++++++++++
demos/code/index.html | 3 +-
generators/dart/text.js | 9 ++
generators/javascript/text.js | 10 ++
generators/python/text.js | 10 ++
7 files changed, 282 insertions(+), 1 deletion(-)
create mode 100644 core/field_textarea.js
diff --git a/blocks/text.js b/blocks/text.js
index 9457258a6e6..6cbcc62a09f 100644
--- a/blocks/text.js
+++ b/blocks/text.js
@@ -639,3 +639,25 @@ Blockly.Blocks['text_prompt_ext'] = {
});
}
};
+
+
+Blockly.Blocks['text_comment'] = {
+ /**
+ * Block for adding in comments.
+ * @this Blockly.Block
+ */
+ init: function() {
+
+ this.setColour(160);
+ //this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);
+ this.appendDummyInput()
+ .appendField('Comment:');
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextArea(''), 'COMMENT')
+ ;
+ this.setPreviousStatement(true);
+ this.setNextStatement(true);
+ // this.setTooltip(Blockly.Msg.TEXT_TEXT_TOOLTIP);
+ }
+};
+
diff --git a/core/blockly.js b/core/blockly.js
index 7ef8e789cbf..f62726e3319 100644
--- a/core/blockly.js
+++ b/core/blockly.js
@@ -38,6 +38,7 @@ goog.require('Blockly.FieldColour');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.FieldImage');
goog.require('Blockly.FieldTextInput');
+goog.require('Blockly.FieldTextArea');
goog.require('Blockly.FieldVariable');
goog.require('Blockly.Generator');
goog.require('Blockly.Msg');
diff --git a/core/field_textarea.js b/core/field_textarea.js
new file mode 100644
index 00000000000..3f6080be281
--- /dev/null
+++ b/core/field_textarea.js
@@ -0,0 +1,228 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Text input field.
+ * @author primary.edw@gmail.com (Andrew Mee)
+ * based on work in field_textinput by fraser@google.com (Neil Fraser)
+ * refactored by toebes@extremenetworks.com (John Toebes)
+ */
+'use strict';
+
+goog.provide('Blockly.FieldTextArea');
+
+goog.require('Blockly.FieldTextInput');
+goog.require('Blockly.Msg');
+goog.require('goog.asserts');
+goog.require('goog.dom');
+goog.require('goog.userAgent');
+
+
+/**
+ * Class for an editable text field.
+ * @param {string} text The initial content of the field.
+ * @param {Function} opt_changeHandler An optional function that is called
+ * to validate any constraints on what the user entered. Takes the new
+ * text as an argument and returns either the accepted text, a replacement
+ * text, or null to abort the change.
+ * @extends {Blockly.Field}
+ * @constructor
+ */
+Blockly.FieldTextArea = function(text, opt_changeHandler) {
+ Blockly.FieldTextArea.superClass_.constructor.call(this,
+ text, opt_changeHandler);
+};
+goog.inherits(Blockly.FieldTextArea, Blockly.FieldTextInput);
+
+/**
+ * Update the text node of this field to display the current text.
+ * @private
+ */
+Blockly.FieldTextArea.prototype.updateTextNode_ = function() {
+ if (!this.textElement_) {
+ // Not rendered yet.
+ return;
+ }
+ var text = this.text_;
+
+ // Empty the text element.
+ goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
+
+ // Replace whitespace with non-breaking spaces so the text doesn't collapse.
+ text = text.replace(/ /g, Blockly.Field.NBSP);
+ if (this.sourceBlock_.RTL && text) {
+ // The SVG is LTR, force text to be RTL.
+ text += '\u200F';
+ }
+ if (!text) {
+ // Prevent the field from disappearing if empty.
+ text = Blockly.Field.NBSP;
+ }
+
+ var lines = text.split('\n');
+ var dy = '0em';
+ for (var i = 0; i < lines.length; i++) {
+ var tspanElement = Blockly.createSvgElement('tspan',
+ {'dy': dy, 'x': 0}, this.textElement_);
+ dy = '1em';
+ var textNode = document.createTextNode(lines[i]);
+ tspanElement.appendChild(textNode);
+ }
+
+ // Cached width is obsolete. Clear it.
+ this.size_.width = 0;
+};
+
+/**
+ * Draws the border with the correct width.
+ * Saves the computed width in a property.
+ * @private
+ */
+Blockly.FieldTextArea.prototype.render_ = function() {
+ this.size_.width = this.textElement_.getBBox().width + 5;
+ this.size_.height = (this.text_.split(/\n/).length ||1)*20 +
+ (Blockly.BlockSvg.SEP_SPACE_Y+5) ;
+
+ if (this.borderRect_) {
+ this.borderRect_.setAttribute('width',
+ this.size_.width + Blockly.BlockSvg.SEP_SPACE_X);
+ this.borderRect_.setAttribute('height',
+ this.size_.height - (Blockly.BlockSvg.SEP_SPACE_Y+5));
+ }
+
+};
+
+/**
+ * Show the inline free-text editor on top of the text.
+ * @param {boolean=} opt_quietInput True if editor should be created without
+ * focus. Defaults to false.
+ * @private
+ */
+Blockly.FieldTextArea.prototype.showEditor_ = function(opt_quietInput) {
+ var quietInput = opt_quietInput || false;
+ if (!quietInput && (goog.userAgent.MOBILE || goog.userAgent.ANDROID ||
+ goog.userAgent.IPAD)) {
+ // Mobile browsers have issues with in-line textareas (focus & keyboards).
+ var newValue = window.prompt(Blockly.Msg.CHANGE_VALUE_TITLE, this.text_);
+ if (this.changeHandler_) {
+ var override = this.changeHandler_(newValue);
+ if (override !== undefined) {
+ newValue = override;
+ }
+ }
+ if (newValue !== null) {
+ this.setText(newValue);
+ }
+ return;
+ }
+
+ Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_());
+ var div = Blockly.WidgetDiv.DIV;
+ // Create the input.
+ var htmlInput = goog.dom.createDom('textarea', 'blocklyHtmlInput');
+ Blockly.FieldTextInput.htmlInput_ = htmlInput;
+ htmlInput.style.resize = 'none';
+ htmlInput.style['line-height'] = '20px';
+ htmlInput.style.height = '100%';
+ div.appendChild(htmlInput);
+
+ htmlInput.value = htmlInput.defaultValue = this.text_;
+ htmlInput.oldValue_ = null;
+ this.validate_();
+ this.resizeEditor_();
+ if (!quietInput) {
+ htmlInput.focus();
+ htmlInput.select();
+ }
+
+ // Bind to keydown -- trap Enter without IME and Esc to hide.
+ htmlInput.onKeyDownWrapper_ =
+ Blockly.bindEvent_(htmlInput, 'keydown', this, this.onHtmlInputKeyDown_);
+ // Bind to keyup -- trap Enter; resize after every keystroke.
+ htmlInput.onKeyUpWrapper_ =
+ Blockly.bindEvent_(htmlInput, 'keyup', this, this.onHtmlInputChange_);
+ // Bind to keyPress -- repeatedly resize when holding down a key.
+ htmlInput.onKeyPressWrapper_ =
+ Blockly.bindEvent_(htmlInput, 'keypress', this, this.onHtmlInputChange_);
+ var workspaceSvg = this.sourceBlock_.workspace.getCanvas();
+ htmlInput.onWorkspaceChangeWrapper_ =
+ Blockly.bindEvent_(workspaceSvg, 'blocklyWorkspaceChange', this,
+ this.resizeEditor_);
+};
+
+/**
+ * Handle key down to the editor.
+ * @param {!Event} e Keyboard event.
+ * @private
+ */
+Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
+ var htmlInput = Blockly.FieldTextInput.htmlInput_;
+ var escKey = 27;
+ if (e.keyCode == escKey) {
+ this.setText(htmlInput.defaultValue);
+ Blockly.WidgetDiv.hide();
+ }
+};
+
+/**
+ * Handle a change to the editor.
+ * @param {!Event} e Keyboard event.
+ * @private
+ */
+Blockly.FieldTextArea.prototype.onHtmlInputChange_ = function(e) {
+ Blockly.FieldTextInput.prototype.onHtmlInputChange_.call(this, e);
+
+ var htmlInput = Blockly.FieldTextInput.htmlInput_;
+ if (e.keyCode == 27) {
+ // Esc
+ this.setText(htmlInput.defaultValue);
+ Blockly.WidgetDiv.hide();
+ } else {
+ Blockly.FieldTextInput.prototype.onHtmlInputChange_.call(this, e);
+ this.resizeEditor_();
+ }
+};
+
+/**
+ * Resize the editor and the underlying block to fit the text.
+ * @private
+ */
+Blockly.FieldTextArea.prototype.resizeEditor_ = function() {
+ var div = Blockly.WidgetDiv.DIV;
+ var bBox = this.fieldGroup_.getBBox();
+ div.style.width = bBox.width + 'px';
+ div.style.height = bBox.height + 'px';
+ var xy = this.getAbsoluteXY_();
+ // In RTL mode block fields and LTR input fields the left edge moves,
+ // whereas the right edge is fixed. Reposition the editor.
+ if (this.RTL) {
+ var borderBBox = this.borderRect_.getBBox();
+ xy.x += borderBBox.width;
+ xy.x -= div.offsetWidth;
+ }
+ // Shift by a few pixels to line up exactly.
+ xy.y += 1;
+ if (goog.userAgent.WEBKIT) {
+ xy.y -= 3;
+ }
+ div.style.left = xy.x + 'px';
+ div.style.top = xy.y + 'px';
+};
+
diff --git a/demos/code/index.html b/demos/code/index.html
index 9b3b22c56f2..dbea8d6d22c 100644
--- a/demos/code/index.html
+++ b/demos/code/index.html
@@ -186,7 +186,8 @@ Blockly >
-
+
+
diff --git a/generators/dart/text.js b/generators/dart/text.js
index 41c90058c71..3b1b5f6b121 100644
--- a/generators/dart/text.js
+++ b/generators/dart/text.js
@@ -275,3 +275,12 @@ Blockly.Dart['text_prompt_ext'] = function(block) {
}
return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
};
+
+Blockly.Dart['text_comment'] = function(block) {
+ // Display comment
+
+ var comment = block.getFieldValue('COMMENT') || '';
+ var code = '/*\n' + comment + '\n*/\n';
+
+ return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
+};
diff --git a/generators/javascript/text.js b/generators/javascript/text.js
index 20bbaacb29c..7504f534fe2 100644
--- a/generators/javascript/text.js
+++ b/generators/javascript/text.js
@@ -255,3 +255,13 @@ Blockly.JavaScript['text_prompt_ext'] = function(block) {
}
return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
};
+
+
+Blockly.JavaScript['text_comment'] = function(block) {
+ // Display comment
+
+ var comment = block.getFieldValue('COMMENT') || '';
+ var code = '/*\n' + comment + '\n*/\n';
+
+ return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
+};
diff --git a/generators/python/text.js b/generators/python/text.js
index 3ab807ff184..3bae80526ed 100644
--- a/generators/python/text.js
+++ b/generators/python/text.js
@@ -270,3 +270,13 @@ Blockly.Python['text_prompt_ext'] = function(block) {
}
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
};
+
+
+Blockly.Python['text_comment'] = function(block) {
+ // Display comment
+
+ var comment = block.getFieldValue('COMMENT') || '';
+ var code = '/*\n' + comment + '\n*/\n';
+
+ return [code, Blockly.Python.ORDER_FUNCTION_CALL];
+};
From c9eedf6796fdda662419823d996800747ad8eb43 Mon Sep 17 00:00:00 2001
From: toebes-extreme
Date: Tue, 30 Jun 2015 11:08:36 -0400
Subject: [PATCH 02/84] Updated to latest code Integrated Typeblock capability
from AppInventor Added support for Java generators Eliminated mutators for
procedures and lists
---
core/drawer.js | 460 ++++++++++++++++++++
core/field_clickimage.js | 125 ++++++
core/field_scope_variable.js | 266 ++++++++++++
core/scopevariables.js | 191 ++++++++
core/typeblock.js | 695 ++++++++++++++++++++++++++++++
generators/java.js | 334 ++++++++++++++
generators/java/colour.js | 86 ++++
generators/java/lists.js | 334 ++++++++++++++
generators/java/logic.js | 128 ++++++
generators/java/loops.js | 165 +++++++
generators/java/math.js | 384 +++++++++++++++++
generators/java/procedures.js | 109 +++++
generators/java/text.js | 304 +++++++++++++
generators/java/variables.js | 119 +++++
java_compressed.js | 98 +++++
tests/generators/unittest_java.js | 160 +++++++
16 files changed, 3958 insertions(+)
create mode 100644 core/drawer.js
create mode 100644 core/field_clickimage.js
create mode 100644 core/field_scope_variable.js
create mode 100644 core/scopevariables.js
create mode 100644 core/typeblock.js
create mode 100644 generators/java.js
create mode 100644 generators/java/colour.js
create mode 100644 generators/java/lists.js
create mode 100644 generators/java/logic.js
create mode 100644 generators/java/loops.js
create mode 100644 generators/java/math.js
create mode 100644 generators/java/procedures.js
create mode 100644 generators/java/text.js
create mode 100644 generators/java/variables.js
create mode 100644 java_compressed.js
create mode 100644 tests/generators/unittest_java.js
diff --git a/core/drawer.js b/core/drawer.js
new file mode 100644
index 00000000000..52208a752af
--- /dev/null
+++ b/core/drawer.js
@@ -0,0 +1,460 @@
+// -*- mode: java; c-basic-offset: 2; -*-
+// Copyright 2013-2014 MIT, All rights reserved
+// Released under the Apache License, Version 2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+/**
+ * @license
+ * @fileoverview Visual blocks editor for App Inventor
+ * Set of drawers for holding factory blocks (blocks that create
+ * other blocks when dragged onto the workspace). The set of drawers
+ * includes the built-in drawers that we get from the blocks language, as
+ * well as a drawer per component instance that was added to this workspace.
+ *
+ * @author mckinney@mit.edu (Andrew F. McKinney)
+ * @author Sharon Perl (sharon@google.com)
+ */
+
+'use strict';
+
+goog.provide('Blockly.Drawer');
+
+goog.require('Blockly.Flyout');
+
+// Some block drawers need to be initialized after all the javascript source is loaded because they
+// use utility functions that may not yet be defined at the time their source is read in. They
+// can do this by adding a field to Blockly.DrawerInit whose value is their initialization function.
+// For example, see language/common/math.js.
+
+/**
+ * Create the dom for the drawer. Creates a flyout Blockly.Drawer.flyout_,
+ * and initializes its dom.
+ */
+Blockly.Drawer.createDom = function() {
+ Blockly.Drawer.flyout_ = new Blockly.Flyout();
+ // insert the flyout after the main workspace (except, there's no
+ // svg.insertAfter method, so we need to insert before the thing following
+ // the main workspace. Neil Fraser says: this is "less hacky than it looks".
+ var flyoutGroup = Blockly.Drawer.flyout_.createDom();
+ Blockly.svg.insertBefore(flyoutGroup, Blockly.mainWorkspace.svgGroup_.nextSibling);
+};
+
+/**
+ * Initializes the drawer by initializing the flyout and creating the
+ * language tree. Call after calling createDom.
+ */
+Blockly.Drawer.init = function() {
+ Blockly.Drawer.flyout_.init(Blockly.mainWorkspace, true);
+ for (var name in Blockly.DrawerInit) {
+ Blockly.DrawerInit[name]();
+ }
+
+ Blockly.Drawer.languageTree = Blockly.Drawer.buildTree_();
+};
+
+/**
+ * String to prefix on categories of each potential block in the drawer.
+ * Used to prevent collisions with built-in properties like 'toString'.
+ * @private
+ */
+Blockly.Drawer.PREFIX_ = 'cat_';
+
+/**
+ * Build the hierarchical tree of block types.
+ * Note: taken from Blockly's toolbox.js
+ * @return {!Object} Tree object.
+ * @private
+ */
+Blockly.Drawer.buildTree_ = function() {
+ var tree = {};
+ // Populate the tree structure.
+ for (var name in Blockly.Blocks) {
+ var block = Blockly.Blocks[name];
+ // Blocks without a category are fragments used by the mutator dialog.
+ if (block.category) {
+ var cat = Blockly.Drawer.PREFIX_ + window.encodeURI(block.category);
+ if (cat in tree) {
+ tree[cat].push(name);
+ } else {
+ tree[cat] = [name];
+ }
+ }
+ }
+ return tree;
+};
+
+/**
+ * Show the contents of the built-in drawer named drawerName. drawerName
+ * should be one of Blockly.MSG_VARIABLE_CATEGORY,
+ * Blockly.MSG_PROCEDURE_CATEGORY, or one of the built-in block categories.
+ * @param drawerName
+ */
+Blockly.Drawer.showBuiltin = function(drawerName) {
+ drawerName = Blockly.Drawer.PREFIX_ + drawerName;
+ var blockSet = Blockly.Drawer.languageTree[drawerName];
+ if(drawerName == "cat_Procedures") {
+ var newBlockSet = [];
+ for(var i=0;i';
+ if(mutatorAttributes) {
+ xmlString += Blockly.Drawer.mutatorAttributesToXMLString(mutatorAttributes);
+ }
+ xmlString += '';
+ }
+ }
+ var xmlBlockArray = [];
+ var xmlFromString = Blockly.Xml.textToDom(xmlString);
+ // [lyn, 11/10/13] Use goog.dom.getChildren rather than .children or .childNodes
+ // to make this code work across browsers.
+ var children = goog.dom.getChildren(xmlFromString);
+ for(var i=0;i';
+ return xmlString;
+}
+
+// [lyn, 10/22/13] return an XML string including one procedure caller for each procedure declaration
+// in main workspace.
+Blockly.Drawer.procedureCallersXMLString = function(returnsValue) {
+ var xmlString = '' // Used to accumulate xml for each caller
+ var decls = Blockly.AIProcedure.getProcedureDeclarationBlocks(returnsValue);
+ decls.sort(Blockly.Drawer.compareDeclarationsByName); // sort decls lexicographically by procedure name
+ for (var i = 0; i < decls.length; i++) {
+ xmlString += Blockly.Drawer.procedureCallerBlockString(decls[i]);
+ }
+ xmlString += '';
+ return xmlString;
+}
+
+Blockly.Drawer.compareDeclarationsByName = function (decl1, decl2) {
+ var name1 = decl1.getFieldValue('NAME').toLocaleLowerCase();
+ var name2 = decl2.getFieldValue('NAME').toLocaleLowerCase();
+ return name1.localeCompare(name2);
+}
+
+// [lyn, 10/22/13] return an XML string for a caller block for the give procedure declaration block
+// Here's an example:
+//
+// p2
+//
+//
+//
+//
+//
+Blockly.Drawer.procedureCallerBlockString = function(procDeclBlock) {
+ var declType = procDeclBlock.type;
+ var callerType = (declType == 'procedures_defreturn') ? 'procedures_callreturn' : 'procedures_callnoreturn';
+ var blockString = ''
+ var procName = procDeclBlock.getFieldValue('NAME');
+ blockString += '' + procName + '';
+ var mutationDom = procDeclBlock.mutationToDom();
+ mutationDom.setAttribute('name', procName); // Decl doesn't have name attribute, but caller does
+ var mutationXmlString = Blockly.Xml.domToText(mutationDom);
+ blockString += mutationXmlString;
+ blockString += '';
+ return blockString;
+}
+
+/**
+ * Given the blockType and a dictionary of the mutator attributes
+ * either return the xml string associated with the default block
+ * or return null, since there are no default blocks associated with the blockType.
+ */
+Blockly.Drawer.getDefaultXMLString = function(blockType,mutatorAttributes) {
+ //return null if the
+ if(Blockly.Drawer.defaultBlockXMLStrings[blockType] == null) {
+ return null;
+ }
+
+ if(Blockly.Drawer.defaultBlockXMLStrings[blockType].xmlString != null) {
+ //return xml string associated with block type
+ return Blockly.Drawer.defaultBlockXMLStrings[blockType].xmlString;
+ } else if(Blockly.Drawer.defaultBlockXMLStrings[blockType].length != null){
+ var possibleMutatorDefaults = Blockly.Drawer.defaultBlockXMLStrings[blockType];
+ var matchingAttributes;
+ var allMatch;
+ //go through each of the possible matching cases
+ for(var i=0;i' +
+ '' +
+ '1' +
+ '5' +
+ '1' +
+ '' +
+ '' },
+
+ math_random_int: {xmlString:
+ '' +
+ '' +
+ '1' +
+ '100' +
+ '' +
+ ''},
+ color_make_color: {xmlString:
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '255' +
+ '0' +
+ '0' +
+ '' +
+ '' +
+ '' +
+ ''},
+ lists_create_with: {xmlString:
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ ''},
+ lists_lookup_in_pairs: {xmlString:
+ '' +
+ '' +
+ 'not found' +
+ '' +
+ ''},
+
+ component_method: [
+ {matchingMutatorAttributes:{component_type:"TinyDB", method_name:"GetValue"},
+ mutatorXMLStringFunction: function(mutatorAttributes) {
+ return '' +
+ '' +
+ '' +
+ //mutator generator
+ Blockly.Drawer.mutatorAttributesToXMLString(mutatorAttributes) +
+ '' +
+ '' +
+ '';}},
+
+ // Notifer.ShowTextDialog has cancelable default to TRUE
+ {matchingMutatorAttributes:{component_type:"Notifier", method_name:"ShowTextDialog"},
+ mutatorXMLStringFunction: function(mutatorAttributes) {
+ return '' +
+ '' +
+ '' +
+ //mutator generator
+ Blockly.Drawer.mutatorAttributesToXMLString(mutatorAttributes) +
+ 'TRUE' +
+ '' +
+ '';}},
+
+ // Notifer.ShowChooseDialog has cancelable default to TRUE
+ {matchingMutatorAttributes:{component_type:"Notifier", method_name:"ShowChooseDialog"},
+ mutatorXMLStringFunction: function(mutatorAttributes) {
+ return '' +
+ '' +
+ '' +
+ //mutator generator
+ Blockly.Drawer.mutatorAttributesToXMLString(mutatorAttributes) +
+ 'TRUE' +
+ '' +
+ '';}},
+
+ // Canvas.DrawCircle has fill default to TRUE
+ {matchingMutatorAttributes:{component_type:"Canvas", method_name:"DrawCircle"},
+ mutatorXMLStringFunction: function(mutatorAttributes) {
+ return '' +
+ '' +
+ '' +
+ //mutator generator
+ Blockly.Drawer.mutatorAttributesToXMLString(mutatorAttributes) +
+ 'TRUE' +
+ '' +
+ '';}}
+ ]
+};
diff --git a/core/field_clickimage.js b/core/field_clickimage.js
new file mode 100644
index 00000000000..14c67d03fdb
--- /dev/null
+++ b/core/field_clickimage.js
@@ -0,0 +1,125 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview clickable image field.
+ * @author toebes@extremenetworks.com (John Toebes)
+ */
+'use strict';
+
+goog.provide('Blockly.FieldClickImage');
+
+goog.require('Blockly.FieldImage');
+
+/**
+ * Class for a clickable image.
+ * @param {string} src The URL of the image.
+ * @param {number} width Width of the image.
+ * @param {number} height Height of the image.
+ * @param {?string} opt_alt Optional alt text for when block is collapsed.
+ * @param {?Function} handler A function that is executed when the
+ * image is selected.
+ * @extends {Blockly.FieldImage}
+ * @constructor
+ */
+Blockly.FieldClickImage = function(src, width, height, opt_alt, handler) {
+ Blockly.FieldClickImage.superClass_.constructor.call(this,
+ src, width, height, '');
+
+ this.handler_ = handler;
+};
+
+goog.inherits(Blockly.FieldClickImage, Blockly.FieldImage);
+
+/**
+ * Editable fields are saved by the XML renderer, non-editable fields are not.
+ * However we don't want to serialize it even if it is present
+ */
+Blockly.FieldClickImage.prototype.EDITABLE = true;
+Blockly.FieldLabel.prototype.SERIALIZABLE = false;
+
+/**
+ * Mouse cursor style when over the hotspot that initiates the editor.
+ */
+Blockly.FieldClickImage.prototype.CURSOR = 'default';
+
+/**
+ * Add or remove the UI indicating if this image may be clicked or not.
+ */
+Blockly.FieldClickImage.prototype.updateEditable = function() {
+ if (this.sourceBlock_.isInFlyout || !this.EDITABLE) {
+ Blockly.addClass_(/** @type {!Element} */ (this.fieldGroup_),
+ 'blocklyIconGroupReadonly');
+ } else {
+ Blockly.removeClass_(/** @type {!Element} */ (this.fieldGroup_),
+ 'blocklyIconGroupReadonly');
+ }
+};
+
+/**
+ * Install this field on a block.
+ * @param {!Blockly.Block} block The block containing this field.
+ */
+Blockly.FieldClickImage.prototype.init = function(block) {
+ if (this.sourceBlock_) {
+ // Image has already been initialized once.
+ return;
+ }
+ Blockly.FieldClickImage.superClass_.init.call(this, block);
+
+ // We want to use the styling of an Icon to indicate clickability
+ Blockly.addClass_(/** @type {!Element} */ (this.fieldGroup_),
+ 'blocklyIconGroup');
+ //
+ // Update the classes for this to appear editable
+ this.updateEditable();
+ // And bind to the mouseup so that we can get called for a click
+ this.mouseUpWrapper_ =
+ Blockly.bindEvent_(this.fieldGroup_, 'mouseup', this, this.onMouseUp_);
+ // Force a render.
+ this.updateTextNode_();
+}
+
+/**
+ * Clone this FieldClickImage.
+ * @return {!Blockly.FieldClickImage} The result of calling the constructor again
+ * with the current values of the arguments used during construction.
+ */
+Blockly.FieldClickImage.prototype.clone = function() {
+ return new Blockly.FieldClickImage(this.handler_,
+ this.rootBlock_, this.name_, this.pos_);
+};
+
+/**
+ * Take the action of the block
+ * Note that this does swap out the dragMode_ variable because we know that
+ * We only get invoked when we aren't actually dragging (otherwise the click
+ * would be consumed by the drag code). Once we return, there is a small amount
+ * of cleanup which needs to complete
+ * @private
+ */
+Blockly.FieldClickImage.prototype.showEditor_ = function() {
+ if (this.handler_) {
+ var saveDragMode = Blockly.dragMode_;
+ Blockly.dragMode_ = 0;
+ this.handler_(this, this.sourceBlock_);
+ Blockly.dragMode_ = saveDragMode;
+ }
+};
diff --git a/core/field_scope_variable.js b/core/field_scope_variable.js
new file mode 100644
index 00000000000..1d3ebf3cc5d
--- /dev/null
+++ b/core/field_scope_variable.js
@@ -0,0 +1,266 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Variable input field.
+ * @author toebes@extremenetworks.com
+ * based on field_variable.js by fraser@google.com (Neil Fraser)
+ */
+'use strict';
+
+goog.provide('Blockly.FieldScopeVariable');
+
+goog.require('Blockly.FieldDropdown');
+goog.require('Blockly.Msg');
+goog.require('Blockly.ScopeVariables');
+goog.require('goog.string');
+
+
+/**
+ * Class for a variable's dropdown field.
+ * @param {?string} varclass The default name for the variable. If null,
+ * a unique variable name will be generated.
+ * @param {Function} opt_changeHandler A function that is executed when a new
+ * option is selected. Its sole argument is the new option value. Its
+ * return value is ignored.
+ * @extends {Blockly.FieldDropdown}
+ * @constructor
+ */
+Blockly.FieldScopeVariable = function(varclass, opt_changeHandler) {
+ var changeHandler;
+ if (opt_changeHandler) {
+ // Wrap the user's change handler together with the variable rename handler.
+ var thisObj = this;
+ changeHandler = function(value) {
+ var retVal = Blockly.FieldScopeVariable.dropdownChange.call(thisObj, value);
+ var newVal;
+ if (retVal === undefined) {
+ newVal = value; // Existing variable selected.
+ } else if (retVal === null) {
+ newVal = thisObj.getValue(); // Abort, no change.
+ } else {
+ newVal = retVal; // Variable name entered.
+ }
+ opt_changeHandler.call(thisObj, newVal);
+ return retVal;
+ };
+ } else {
+ changeHandler = Blockly.FieldScopeVariable.dropdownChange;
+ }
+ this.msgRename_ = Blockly.Msg.RENAME_SCOPE_VARIABLE;
+ this.msgRenameTitle_ = Blockly.Msg.RENAME_SCOPE_VARIABLE_TITLE;
+ this.msgNew_ = Blockly.Msg.NEW_SCOPE_VARIABLE;
+ this.msgNewTitle_ = Blockly.Msg.NEW_SCOPE_VARIABLE_TITLE;
+ this.msgEmpty_ = null;
+
+ this.setVarClass(varclass);
+ this.setValue(this.getVarClass()[0]);
+
+ Blockly.FieldScopeVariable.superClass_.constructor.call(this,
+ Blockly.FieldScopeVariable.dropdownCreate, changeHandler);
+
+};
+goog.inherits(Blockly.FieldScopeVariable, Blockly.FieldDropdown);
+
+Blockly.FieldScopeVariable.prototype.init
+/**
+ * Install this dropdown on a block.
+ * @param {!Blockly.Block} block The block containing this text.
+ */
+Blockly.FieldScopeVariable.prototype.init = function(block) {
+ if (this.sourceBlock_) {
+ // Dropdown has already been initialized once.
+ return;
+ }
+
+ if (!this.getValue()) {
+ // Variables without names get uniquely named for this workspace.
+ if (block.isInFlyout) {
+ var workspace = block.workspace.targetWorkspace;
+ } else {
+ var workspace = block.workspace;
+ }
+ this.setValue(Blockly.ScopeVariables.generateUniqueName(workspace,
+ this.getVarClass()));
+ }
+ Blockly.FieldScopeVariable.superClass_.init.call(this, block);
+};
+
+/**
+ * Clone this FieldScopeVariable.
+ * @return {!Blockly.FieldScopeVariable} The result of calling the constructor again
+ * with the current values of the arguments used during construction.
+ */
+Blockly.FieldScopeVariable.prototype.clone = function() {
+ return new Blockly.FieldScopeVariable(this.getValue(), this.changeHandler_);
+};
+
+/**
+ * Get the variable's name (use a variableDB to convert into a real name).
+ * Unline a regular dropdown, variables are literal and have no neutral value.
+ * @return {string} Current text.
+ */
+Blockly.FieldScopeVariable.prototype.getValue = function() {
+ var result = this.getText();
+ if (result === this.msgEmpty_) {
+ result = '';
+ }
+ return result;
+};
+
+/**
+ * Set the variable name.
+ * @param {string} text New text.
+ */
+Blockly.FieldScopeVariable.prototype.setValue = function(text) {
+ this.value_ = text;
+ this.setText(text);
+};
+
+/**
+ * Get the variable's name (use a variableDB to convert into a real name).
+ * Unline a regular dropdown, variables are literal and have no neutral value.
+ * @return {array} Array of classes
+ */
+Blockly.FieldScopeVariable.prototype.getVarClass = function() {
+ return this.varClass_;
+};
+
+/**
+ * Set the variable name.
+ * @param {string} text New text.
+ */
+Blockly.FieldScopeVariable.prototype.setVarClass = function(varclass) {
+ if (typeof varclass === "string") {
+ this.varClass_ = [ varclass ];
+ } else {
+ this.varClass_ = varclass;
+ }
+};
+
+/**
+ * Set the variable prompt titles.
+ * @param {string} text New text.
+ */
+Blockly.FieldScopeVariable.prototype.setMsgStrings = function(
+ msgRename, msgRenameTitle, msgNew, msgNewTitle) {
+ this.msgRename_ = msgRename;
+ this.msgRenameTitle_ = msgRenameTitle;
+ this.msgNew_ = msgNew;
+ this.msgNewTitle_ = msgNewTitle;
+};
+
+Blockly.FieldScopeVariable.prototype.setMsgEmpty = function(
+ msgEmpty) {
+ this.msgEmpty_ = msgEmpty;
+};
+
+/**
+ * Return a sorted list of variable names for variable dropdown menus.
+ * Include a special option at the end for creating a new variable name.
+ * @return {!Array.} Array of variable names.
+ * @this {!Blockly.FieldScopeVariable}
+ */
+Blockly.FieldScopeVariable.dropdownCreate = function() {
+ if (this.sourceBlock_ && this.sourceBlock_.workspace) {
+ var variableList =
+ Blockly.ScopeVariables.allVariables(this.sourceBlock_.workspace,
+ this.getVarClass());
+ } else {
+ var variableList = [];
+ }
+ // Ensure that the currently selected variable is an option.
+ var name = this.getText();
+ if (name && variableList.indexOf(name) == -1) {
+ variableList.push(name);
+ }
+ variableList.sort(goog.string.caseInsensitiveCompare);
+ if (name && this.msgRename_) {
+ variableList.push(this.msgRename_);
+ }
+ if (this.msgNew_) {
+ variableList.push(this.msgNew_);
+ }
+ // Never leave an empty array. The callers expect at least one item
+ if (goog.array.isEmpty(variableList)) {
+ variableList.push('');
+ }
+
+ // Variables are not language-specific, use the name as both the user-facing
+ // text and the internal representation.
+ var options = [];
+ if (this.msgEmpty_) {
+ options.push([this.msgEmpty_, '']);
+ }
+ for (var x = 0; x < variableList.length; x++) {
+ options.push([variableList[x], variableList[x]]);
+ }
+ return options;
+};
+
+/**
+ * Event handler for a change in variable name.
+ * Special case the 'New variable...' and 'Rename variable...' options.
+ * In both of these special cases, prompt the user for a new name.
+ * @param {string} text The selected dropdown menu option.
+ * @return {null|undefined|string} An acceptable new variable name, or null if
+ * change is to be either aborted (cancel button) or has been already
+ * handled (rename), or undefined if an existing variable was chosen.
+ * @this {!Blockly.FieldScopeVariable}
+ */
+Blockly.FieldScopeVariable.dropdownChange = function(text) {
+ var me = this;
+ function promptName(promptText, defaultText) {
+ Blockly.hideChaff();
+ var newVar = window.prompt(promptText, defaultText);
+ // Merge runs of whitespace. Strip leading and trailing whitespace.
+ // Beyond this, all names are legal.
+ if (newVar) {
+ newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
+ if (newVar === me.msgRename_ ||
+ newVar === me.msgNew_) {
+ // Ok, not ALL names are legal...
+ newVar = null;
+ }
+ }
+ return newVar;
+ }
+ var workspace = this.sourceBlock_.workspace;
+ if (text === this.msgRename_) {
+ var oldVar = this.getText();
+ text = promptName(this.msgRenameTitle_.replace('%1', oldVar), oldVar);
+ if (text) {
+ Blockly.ScopeVariables.renameVariable(oldVar, text, workspace,
+ this.getVarClass());
+ }
+ return null;
+ } else if (text === this.msgNew_) {
+ text = promptName(this.msgNewTitle_, '');
+ // Since variables are case-insensitive, ensure that if the new variable
+ // matches with an existing variable, the new case prevails throughout.
+ if (text) {
+ Blockly.ScopeVariables.renameVariable(text, text, workspace,
+ this.getVarClass());
+ return text;
+ }
+ return null;
+ }
+ return undefined;
+};
diff --git a/core/scopevariables.js b/core/scopevariables.js
new file mode 100644
index 00000000000..a8af56f04e2
--- /dev/null
+++ b/core/scopevariables.js
@@ -0,0 +1,191 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Utility functions for handling variables.
+ * @author fraser@google.com (Neil Fraser)
+ */
+'use strict';
+
+goog.provide('Blockly.ScopeVariables');
+
+// TODO(scr): Fix circular dependencies
+// goog.require('Blockly.Block');
+goog.require('Blockly.Workspace');
+goog.require('goog.string');
+
+
+/**
+ * Category to separate variable names from procedures and generated functions.
+ */
+Blockly.ScopeVariables.NAME_TYPE = 'SCOPEVARIABLE';
+
+/**
+ * Find all user-created variables.
+ * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace.
+ * @return {!Array.} Array of variable names.
+ */
+Blockly.ScopeVariables.allVariables = function(root, varclass) {
+ var blocks;
+ if (root.getDescendants) {
+ // Root is Block.
+ blocks = root.getDescendants();
+ } else if (root.getAllBlocks) {
+ // Root is Workspace.
+ blocks = root.getAllBlocks();
+ } else {
+ throw 'Not Block or Workspace: ' + root;
+ }
+ var variableHash = Object.create(null);
+ // Iterate through every block and add each variable to the hash.
+ for (var x = 0; x < blocks.length; x++) {
+ var func = blocks[x].getScopeVars;
+ if (func) {
+ for(var i = 0; i < varclass.length; i++) {
+ var blockVariables = func.call(blocks[x], varclass[i]);
+ for (var y = 0; y < blockVariables.length; y++) {
+ var varName = blockVariables[y];
+ // Variable name may be null if the block is only half-built.
+ if (varName) {
+ variableHash[varName.toLowerCase()] = varName;
+ }
+ }
+ }
+ }
+ }
+ // Flatten the hash into a list.
+ var variableList = [];
+ for (var name in variableHash) {
+ variableList.push(variableHash[name]);
+ }
+ return variableList;
+};
+
+/**
+ * Find all instances of the specified variable and rename them.
+ * @param {string} oldName Variable to rename.
+ * @param {string} newName New variable name.
+ * @param {!Blockly.Workspace} workspace Workspace rename variables in.
+ */
+Blockly.ScopeVariables.renameVariable = function(oldName, newName,
+ workspace, varclass) {
+ var blocks = workspace.getAllBlocks();
+ // Iterate through every block.
+ for (var x = 0; x < blocks.length; x++) {
+ var func = blocks[x].renameScopeVar;
+ if (func) {
+ for (var i = 0; i < varclass.length; i++) {
+ func.call(blocks[x], oldName, newName, varclass[i]);
+ }
+ }
+ }
+};
+
+/**
+ * Construct the blocks required by the flyout for the variable category.
+ * @param {!Array.} blocks List of blocks to show.
+ * @param {!Array.} gaps List of widths between blocks.
+ * @param {number} margin Standard margin width for calculating gaps.
+ * @param {!Blockly.Workspace} workspace The flyout's workspace.
+ */
+Blockly.ScopeVariables.flyoutCategory = function(blocks, gaps, margin,
+ workspace, varclass) {
+ var variableList = Blockly.ScopeVariables.allVariables(workspace.targetWorkspace,
+ varclass);
+ variableList.sort(); // goog.string.caseInsensitiveCompare);
+ // In addition to the user's variables, we also want to display the default
+ // variable name at the top. We also don't want this duplicated if the
+ // user has created a variable of the same name.
+ variableList.unshift(null);
+ var defaultVariable = undefined;
+ for (var i = 0; i < variableList.length; i++) {
+ if (variableList[i] === defaultVariable) {
+ continue;
+ }
+ var getBlock = Blockly.Blocks['variables_get'] ?
+ Blockly.Block.obtain(workspace, 'variables_get') : null;
+ getBlock && getBlock.initSvg();
+ var setBlock = Blockly.Blocks['variables_set'] ?
+ Blockly.Block.obtain(workspace, 'variables_set') : null;
+ setBlock && setBlock.initSvg();
+ if (variableList[i] === null) {
+ defaultVariable = (getBlock || setBlock).getVars()[0];
+ } else {
+ getBlock && getBlock.setFieldValue(variableList[i], 'VAR');
+ setBlock && setBlock.setFieldValue(variableList[i], 'VAR');
+ }
+ setBlock && blocks.push(setBlock);
+ getBlock && blocks.push(getBlock);
+ if (getBlock && setBlock) {
+ gaps.push(margin, margin * 3);
+ } else {
+ gaps.push(margin * 2);
+ }
+ }
+};
+
+/**
+* Return a new variable name that is not yet being used. This will try to
+* generate single letter variable names in the range 'i' to 'z' to start with.
+* If no unique name is located it will try 'i' to 'z', 'a' to 'h',
+* then 'i2' to 'z2' etc. Skip 'l'.
+ * @param {!Blockly.Workspace} workspace The workspace to be unique in.
+* @return {string} New variable name.
+*/
+Blockly.ScopeVariables.generateUniqueName = function(workspace, varclass) {
+ var variableList = Blockly.ScopeVariables.allVariables(workspace,varclass);
+ var newName = '';
+ if (variableList.length) {
+ var nameSuffix = 1;
+ var letters = 'ijkmnopqrstuvwxyzabcdefgh'; // No 'l'.
+ var letterIndex = -1;
+ var potName = varclass[0];
+ while (!newName) {
+ var inUse = false;
+ for (var i = 0; i < variableList.length; i++) {
+ if (variableList[i].toLowerCase() == potName) {
+ // This potential name is already used.
+ inUse = true;
+ break;
+ }
+ }
+ if (inUse) {
+ // Try the next potential name.
+ letterIndex++;
+ if (letterIndex == letters.length) {
+ // Reached the end of the character sequence so back to 'i'.
+ // a new suffix.
+ letterIndex = 0;
+ nameSuffix++;
+ }
+ potName = varclass[0]+letters.charAt(letterIndex);
+ if (nameSuffix > 1) {
+ potName += nameSuffix;
+ }
+ } else {
+ // We can use the current potential name.
+ newName = potName;
+ }
+ }
+ } else {
+ newName = 'i';
+ }
+ return newName;
+};
diff --git a/core/typeblock.js b/core/typeblock.js
new file mode 100644
index 00000000000..092039419f8
--- /dev/null
+++ b/core/typeblock.js
@@ -0,0 +1,695 @@
+//Copyright 2013 Massachusetts Institute of Technology. All rights reserved.
+
+/**
+ * @fileoverview File to handle 'Type Blocking'. When the user starts typing the
+ * name of a Block in the workspace, a series of suggestions will appear. Upon
+ * selecting one (enter key), the chosen block will be created in the workspace
+ * This file needs additional configuration through the inject method.
+ * @author josmasflores@gmail.com (Jose Dominguez)
+ */
+'use strict';
+
+goog.provide('Blockly.TypeBlock');
+goog.require('Blockly.Xml');
+
+goog.require('goog.events');
+goog.require('goog.events.KeyCodes');
+goog.require('goog.events.KeyHandler');
+goog.require('goog.ui.ac');
+goog.require('goog.style');
+goog.require('Blockly.Drawer');
+
+goog.require('goog.ui.ac.ArrayMatcher');
+goog.require('goog.ui.ac.AutoComplete');
+goog.require('goog.ui.ac.InputHandler');
+goog.require('goog.ui.ac.Renderer');
+
+/**
+ * Main Type Block function for configuration.
+ * @param {Object} htmlConfig an object of the type:
+ {
+ typeBlockDiv: 'ai_type_block',
+ inputText: 'ac_input_text'
+ }
+ * stating the ids of the attributes to be used in the html enclosing page
+ * create a new block
+ */
+Blockly.TypeBlock = function( htmlConfig ){
+ Blockly.TypeBlock.typeBlockDiv_ =
+ goog.dom.getElement(htmlConfig['typeBlockDiv']);
+ Blockly.TypeBlock.inputText_ =
+ goog.dom.getElement(htmlConfig['inputText']);
+ Blockly.TypeBlock.createAutoComplete_();
+};
+
+/**
+ * DOM Element for the Div where the type block panel will be rendered
+ * @private
+ */
+Blockly.TypeBlock.typeBlockDiv_ = null;
+
+/**
+ * DOM Element for the input text contained in the type block panel
+ * @private
+ */
+Blockly.TypeBlock.inputText_ = null;
+
+/**
+ * Is the Type Block panel currently showing?
+ */
+Blockly.TypeBlock.visible = false;
+
+/**
+ * Mapping of options to show in the auto-complete panel. This maps the
+ * canonical name of the block, needed to create a new Blockly.Block, with the
+ * internationalised word or sentence used in typeblocks. Certain blocks do not
+ * only need the canonical block representation, but also values for dropdowns
+ * (name and value)
+ * - No dropdowns: this.typeblock: [{ translatedName: Blockly.LANG_VAR }]
+ * - With dropdowns: this.typeblock: [{ translatedName: Blockly.LANG_VAR },
+ * dropdown: {
+ * titleName: 'TITLE', value: 'value'
+ * }]
+ * - Additional types can be used to mark a block as isProcedure or
+ * isGlobalVar. These are only used to manage the loading of options in the
+ * auto-complete matcher.
+ * @private
+ */
+Blockly.TypeBlock.TBOptions_ = {};
+
+/**
+ * This array contains only the Keys of Blockly.TypeBlock.TBOptions_ to be used
+ * as options in the autocomplete widget.
+ * @private
+ */
+Blockly.TypeBlock.TBOptionsNames_ = [];
+
+/**
+ * pointer to the automcomplete widget to be able to change its contents when
+ * the Language tree is modified (additions, renaming, or deletions)
+ * @private
+ */
+Blockly.TypeBlock.ac_ = null;
+
+/**
+ * We keep a listener pointer in case of needing to unlisten to it. We only want
+ * one listener at a time, and a reload could create a second one, so we
+ * unlisten first and then listen back
+ * @private
+ */
+Blockly.TypeBlock.currentListener_ = null;
+
+Blockly.TypeBlock.onKeyDown_ = function(e){
+ if (!Blockly.TypeBlock.typeBlockDiv_) {
+ return;
+ }
+ if (e.altKey || e.ctrlKey || e.metaKey || e.keycode === 9) { // 9 is tab
+ return;
+ }
+
+ // A way to know if the user is editing a block or trying to type a new one
+// if (e.target.id === '') return;
+ if (goog.style.isElementShown(Blockly.TypeBlock.typeBlockDiv_)) {
+ // Pressing esc closes the context menu.
+ if (e.keyCode == 27) {
+ Blockly.TypeBlock.hide();
+ }
+ // Enter in the panel makes it select an option
+ if (e.keyCode === 13) {
+ Blockly.TypeBlock.hide();
+ }
+ } else if (isCharacterKey(e.charCode)) {
+ Blockly.TypeBlock.show();
+ // Can't seem to make Firefox display first character, so keep all browsers
+ // from automatically displaying the first character and add it manually.
+ e.preventDefault();
+ if (typeof e.key === 'undefined' || e.key === 'MozPrintableKey') {
+ Blockly.TypeBlock.inputText_.value =
+ String.fromCharCode(e.charCode !== 0 ? e.charCode : e.keyCode);
+// goog.events.KeyCodes.normalizeKeyCode(e.keyCode));
+ } else {
+ Blockly.TypeBlock.inputText_.value = e.key;
+ }
+ }
+};
+
+/**
+ * function to hide the autocomplete panel. Also used from hideChaff in
+ * Blockly.js
+ */
+Blockly.TypeBlock.hide = function(){
+ if (Blockly.TypeBlock.visible) {
+ Blockly.TypeBlock.visible = false;
+ Blockly.TypeBlock.ac_.dismiss();
+ goog.style.showElement(Blockly.TypeBlock.typeBlockDiv_, false);
+ }
+};
+
+/**
+ * function to show the auto-complete panel to start typing block names
+ */
+Blockly.TypeBlock.show = function(){
+ if (!Blockly.TypeBlock.visible) {
+ Blockly.TypeBlock.lazyLoadOfOptions_();
+
+ var inputHandler = Blockly.TypeBlock.ac_.getSelectionHandler();
+ var svg = Blockly.getMainWorkspace().options.svg;
+ var svgPosition = goog.style.getPageOffset(svg);
+
+ var x = Blockly.latestClick.x - svgPosition.x;
+ var y = Blockly.latestClick.y - svgPosition.y;
+
+ goog.style.setPosition(Blockly.TypeBlock.typeBlockDiv_, x, y);
+ goog.style.showElement(Blockly.TypeBlock.typeBlockDiv_, true);
+ Blockly.TypeBlock.inputText_.value = '';
+ Blockly.TypeBlock.inputText_.focus();
+ inputHandler.processFocus(Blockly.TypeBlock.inputText_);
+ Blockly.TypeBlock.visible = true;
+ }
+};
+
+/**
+ * Used as an optimisation trick to avoid reloading components and built-ins
+ * unless there is a real need to do so. needsReload.components can be set to
+ * true when a component changes.
+ * Defaults to true so that it loads the first time (set to null after loading
+ * in lazyLoadOfOptions_())
+ * @type {{components: boolean}}
+ */
+Blockly.TypeBlock.needsReload = {
+ components: true
+};
+
+/**
+ * Lazily loading options because some of them are not available during
+ * bootstrapping, and some users will never use this functionality, so we avoid
+ * having to deal with changes such as handling renaming of variables and
+ * procedures (leaving it until the moment they are used, if ever).
+ * @private
+ */
+Blockly.TypeBlock.lazyLoadOfOptions_ = function () {
+
+ // Optimisation to avoid reloading all components and built-in objects unless
+ // it is needed.
+ // needsReload.components is setup when adding/renaming/removing a component
+ // in components.js
+ if (this.needsReload.components){
+ Blockly.TypeBlock.generateOptions();
+ this.needsReload.components = null;
+ }
+ Blockly.TypeBlock.loadGlobalVariables_();
+ Blockly.TypeBlock.loadProcedures_();
+ this.reloadOptionsAfterChanges_();
+};
+
+/**
+ * This function traverses the Language tree and re-creates all the options
+ * available for type blocking. It's needed in the case of modifying the
+ * Language tree after its creation (adding or renaming components, for instance).
+ * It also loads all the built-in blocks.
+ *
+ * call 'reloadOptionsAfterChanges_' after calling this. The function
+ * lazyLoadOfOptions_ is an example of how to call this function.
+ */
+Blockly.TypeBlock.generateOptions = function() {
+
+ var buildListOfOptions = function() {
+ var listOfOptions = {};
+ var typeblockArray;
+ for (var name in Blockly.Blocks) {
+ var block = Blockly.Blocks[name];
+ if(block.typeblock){
+ typeblockArray = block.typeblock;
+ if(typeof block.typeblock == "function") {
+ typeblockArray = block.typeblock();
+ }
+ createOption(typeblockArray, name);
+ }
+ }
+
+ function createOption(tb, canonicName){
+ if (tb){
+ goog.array.forEach(tb, function(dd){
+ var dropDownValues = {};
+ var mutatorAttributes = {};
+ if (dd.dropDown){
+ if (dd.dropDown.titleName && dd.dropDown.value){
+ dropDownValues.titleName = dd.dropDown.titleName;
+ dropDownValues.value = dd.dropDown.value;
+ }
+ else {
+ throw new Error('TypeBlock not correctly set up for ' + canonicName);
+ }
+ }
+ if(dd.mutatorAttributes) {
+ mutatorAttributes = dd.mutatorAttributes;
+ }
+ listOfOptions[dd.translatedName] = {
+ canonicName: canonicName,
+ dropDown: dropDownValues,
+ mutatorAttributes: mutatorAttributes
+ };
+ });
+ }
+ }
+
+ return listOfOptions;
+ };
+
+ // This is called once on startup, and it will contain all built-in blocks.
+ // After that, it can be called on demand
+ // (for instance in the function lazyLoadOfOptions_)
+ Blockly.TypeBlock.TBOptions_ = buildListOfOptions();
+};
+
+/**
+ * This function reloads all the latest changes that might have occurred in the
+ * language tree or the structures containing procedures and variables. It only
+ * needs to be called once even if different sources are being updated at the
+ * same time (call on load proc, load vars, and generate options, only needs
+ * one call of this function; and example of that is lazyLoadOfOptions_
+ * @private
+ */
+Blockly.TypeBlock.reloadOptionsAfterChanges_ = function () {
+ Blockly.TypeBlock.TBOptionsNames_ = goog.object.getKeys(Blockly.TypeBlock.TBOptions_);
+ goog.array.sort(Blockly.TypeBlock.TBOptionsNames_);
+ Blockly.TypeBlock.ac_.matcher_.setRows(Blockly.TypeBlock.TBOptionsNames_);
+};
+
+/**
+ * Loads all procedure names as options for TypeBlocking. It is used lazily
+ * from show().
+ * Call 'reloadOptionsAfterChanges_' after calling this one. The function
+ * lazyLoadOfOptions_ is an example of how to call this function.
+ * @private
+ */
+Blockly.TypeBlock.loadProcedures_ = function(){
+ // Clean up any previous procedures in the list.
+ Blockly.TypeBlock.TBOptions_ = goog.object.filter(Blockly.TypeBlock.TBOptions_,
+ function(opti){ return !opti.isProcedure;});
+
+ var procsNoReturn = createTypeBlockForProcedures_(false);
+ goog.array.forEach(procsNoReturn, function(pro){
+ Blockly.TypeBlock.TBOptions_[pro.translatedName] = {
+ canonicName: 'procedures_callnoreturn',
+ dropDown: pro.dropDown,
+ isProcedure: true // this attribute is used to clean up before reloading
+ };
+ });
+
+ var procsReturn = createTypeBlockForProcedures_(true);
+ goog.array.forEach(procsReturn, function(pro){
+ Blockly.TypeBlock.TBOptions_[pro.translatedName] = {
+ canonicName: 'procedures_callreturn',
+ dropDown: pro.dropDown,
+ isProcedure: true
+ };
+ });
+
+ /**
+ * Procedure names can be collected for both 'with return' and 'no return'
+ * varieties from getProcedureNames()
+ * @param {boolean} withReturn indicates if the query us for 'with':true
+ * or 'no':false return
+ * @returns {Array} array of the procedures requested
+ * @private
+ */
+ function createTypeBlockForProcedures_(withReturn) {
+ var options = [];
+ var procNamesArray = Blockly.Procedures.allProcedures(Blockly.mainWorkspace);
+ var procNames;
+ if (withReturn) {
+ procNames = procNamesArray[0];
+ } else {
+ procNames = procNamesArray[1];
+ }
+ goog.array.forEach(procNames, function(proc){
+ options.push(
+ {
+ translatedName: Blockly.Msg.LANG_PROCEDURES_CALLNORETURN_CALL + ' ' + proc[0],
+ dropDown: {
+ titleName: 'PROCNAME',
+ value: proc[0]
+ }
+ }
+ );
+ });
+ return options;
+ }
+};
+
+/**
+ * Loads all global variable names as options for TypeBlocking. It is used
+ * lazily from show().
+ * Call 'reloadOptionsAfterChanges_' after calling this one. The function
+ * lazyLoadOfOptions_ is an example of how to call this function.
+ */
+Blockly.TypeBlock.loadGlobalVariables_ = function () {
+ //clean up any previous procedures in the list
+ Blockly.TypeBlock.TBOptions_ = goog.object.filter(Blockly.TypeBlock.TBOptions_,
+ function(opti){ return !opti.isGlobalvar;});
+
+ var globalVarNames = createTypeBlockForVariables_();
+ goog.array.forEach(globalVarNames, function(varName){
+ var canonicalN;
+ if (varName.translatedName.substring(0,3) === 'get')
+ canonicalN = 'lexical_variable_get';
+ else
+ canonicalN = 'lexical_variable_set';
+ Blockly.TypeBlock.TBOptions_[varName.translatedName] = {
+ canonicName: canonicalN,
+ dropDown: varName.dropDown,
+ isGlobalvar: true
+ };
+ });
+
+ /**
+ * Create TypeBlock options for global variables (a setter and a getter for each).
+ * @returns {Array} array of global var options
+ */
+ function createTypeBlockForVariables_() {
+ var options = [];
+// var varNames = Blockly.FieldLexicalVariable.getGlobalNames();
+ var varNames = Blockly.Variables.allVariables(Blockly.mainWorkspace);
+ // Make a setter and a getter for each of the names
+ goog.array.forEach(varNames, function(varName){
+ options.push(
+ {
+ translatedName: 'get global ' + varName,
+ dropDown: {
+ titleName: 'VAR',
+ value: 'global ' + varName
+ }
+ }
+ );
+ options.push(
+ {
+ translatedName: 'set global ' + varName,
+ dropDown: {
+ titleName: 'VAR',
+ value: 'global ' + varName
+ }
+ }
+ );
+ });
+ return options;
+ }
+};
+
+Blockly.TypeBlock.autoCompleteSelected = function() {
+ var blockName = Blockly.TypeBlock.inputText_.value;
+ var blockToCreate = goog.object.get(Blockly.TypeBlock.TBOptions_, blockName);
+ if (!blockToCreate) {
+ //If the input passed is not a block, check if it is a number
+ // or a pre-populated text block
+ var numberReg = new RegExp('^-?[0-9]\\d*(\.\\d+)?$', 'g');
+ var numberMatch = numberReg.exec(blockName);
+ var textReg = new RegExp('^[\"|\']+', 'g');
+ var textMatch = textReg.exec(blockName);
+ if (numberMatch && numberMatch.length > 0){
+ blockToCreate = {
+ canonicName: 'math_number',
+ dropDown: {
+ titleName: 'NUM',
+ value: blockName
+ }
+ };
+ }
+ else if (textMatch && textMatch.length === 1){
+ blockToCreate = {
+ canonicName: 'text',
+ dropDown: {
+ titleName: 'TEXT',
+ value: blockName.substring(1)
+ }
+ };
+ }
+ else
+ return; // block does not exist: return
+ }
+
+ var blockToCreateName = '';
+ var block;
+ if (blockToCreate.dropDown){
+ //All blocks should have a dropDown property, even if empty
+ blockToCreateName = blockToCreate.canonicName;
+ // components have mutator attributes we need to deal with.
+ // We can also add these for special blocks
+ // e.g., this is done for create empty list
+ var xmlString = Blockly.Drawer.getDefaultXMLString(
+ blockToCreate.canonicName,
+ blockToCreate.mutatorAttributes);
+ var xml;
+ if (xmlString === null) {
+ var blockType = blockToCreate.canonicName;
+ if (blockType == 'procedures_callnoreturn' ||
+ blockType == 'procedures_callreturn') {
+ xmlString = Blockly.Drawer.procedureCallersXMLString(
+ blockType == 'procedures_callreturn');
+ } else {
+ xmlString = '';
+ if(!goog.object.isEmpty(blockToCreate.mutatorAttributes)) {
+ xmlString += Blockly.Drawer.mutatorAttributesToXMLString(
+ blockToCreate.mutatorAttributes);
+ }
+ xmlString += '';
+ }
+ }
+ xml = Blockly.Xml.textToDom(xmlString);
+ var xmlBlock = xml.firstChild;
+ if (xml.childNodes.length > 1 &&
+ Blockly.TypeBlock.inputText_.value === 'make a list')
+ xmlBlock = xml.childNodes[1];
+ block = Blockly.Xml.domToBlock(Blockly.mainWorkspace, xmlBlock);
+
+ if (blockToCreate.dropDown.titleName && blockToCreate.dropDown.value){
+ block.setFieldValue(blockToCreate.dropDown.value,
+ blockToCreate.dropDown.titleName);
+ // change type checking for split blocks
+ if(blockToCreate.dropDown.value == 'SPLITATFIRST' ||
+ blockToCreate.dropDown.value == 'SPLIT') {
+ block.getInput("AT").setCheck('String');
+ } else if(blockToCreate.dropDown.value == 'SPLITATFIRSTOFANY' ||
+ blockToCreate.dropDown.value == 'SPLITATANY') {
+ block.getInput("AT").setCheck('Array');
+ }
+ }
+ } else {
+ throw new Error('Type Block not correctly set up for: ' + blockToCreateName);
+ }
+ // Blockly.WarningHandler.checkAllBlocksForWarningsAndErrors();
+ block.render();
+ var blockSelected = Blockly.selected;
+ var selectedX, selectedY, selectedXY;
+ if (blockSelected) {
+ selectedXY = blockSelected.getRelativeToSurfaceXY();
+ selectedX = selectedXY.x;
+ selectedY = selectedXY.y;
+ Blockly.TypeBlock.connectIfPossible(blockSelected, block);
+ if(!block.parentBlock_){
+ //Place it close but a bit out of the way from the one we created.
+ block.moveBy(Blockly.selected.getRelativeToSurfaceXY().x + 110,
+ Blockly.selected.getRelativeToSurfaceXY().y + 50);
+ }
+ block.select();
+ }
+ else {
+ //calculate positions relative to the view and the latest click
+ var svg = Blockly.getMainWorkspace().options.svg;
+ var svgPosition = goog.style.getPageOffset(svg);
+ var tbwidth = Blockly.getMainWorkspace().toolbox_.width;
+
+ var left = Blockly.mainWorkspace.getMetrics().viewLeft +
+ Blockly.latestClick.x - svgPosition.x - tbwidth;
+ var top = Blockly.mainWorkspace.getMetrics().viewTop +
+ Blockly.latestClick.y - svgPosition.y;
+ block.moveBy(left, top);
+ block.select();
+ }
+ Blockly.TypeBlock.hide();
+}
+
+/**
+ * Creates the auto-complete panel, powered by Google Closure's ac widget
+ * @private
+ */
+Blockly.TypeBlock.createAutoComplete_ = function(){
+ Blockly.TypeBlock.TBOptionsNames_ =
+ goog.object.getKeys( Blockly.TypeBlock.TBOptions_ );
+ goog.array.sort(Blockly.TypeBlock.TBOptionsNames_);
+ //if there is a key, unlisten
+ goog.events.unlistenByKey(Blockly.TypeBlock.currentListener_);
+ if (Blockly.TypeBlock.ac_) {
+ Blockly.TypeBlock.ac_.dispose(); //Make sure we only have 1 at a time
+ }
+
+ // 3 objects needed to create a goog.ui.ac.AutoComplete instance
+ var matcher = new Blockly.TypeBlock.ac.AIArrayMatcher(
+ Blockly.TypeBlock.TBOptionsNames_, false);
+ var renderer = new goog.ui.ac.Renderer();
+ var inputHandler = new goog.ui.ac.InputHandler(null, null, false);
+
+ Blockly.TypeBlock.ac_ = new goog.ui.ac.AutoComplete(matcher, renderer,
+ inputHandler);
+ Blockly.TypeBlock.ac_.setMaxMatches(100);
+ inputHandler.attachAutoComplete(Blockly.TypeBlock.ac_);
+ inputHandler.attachInputs(Blockly.TypeBlock.inputText_);
+
+ Blockly.TypeBlock.currentListener_ =
+ goog.events.listen(Blockly.TypeBlock.ac_,
+ goog.ui.ac.AutoComplete.EventType.UPDATE,
+ Blockly.TypeBlock.autoCompleteSelected
+ );
+ goog.events.listen(Blockly.TypeBlock.ac_,
+ goog.ui.ac.AutoComplete.EventType.DISMISS,
+ Blockly.TypeBlock.hide);
+};
+
+/**
+ * Blocks connect in different ways; a block with an outputConnection such as
+ * a number will connect in one of its parent's input connection (inputLis). .
+ * A block with no outputConnection could be connected to its parent's next
+ * connection.
+ */
+Blockly.TypeBlock.connectIfPossible = function(blockSelected, createdBlock) {
+ var i = 0,
+ inputList = blockSelected.inputList,
+ ilLength = inputList.length;
+
+ //If createdBlock has an output connection, we need to:
+ // connect to parent (eg: connect equals into if)
+ //else we need to:
+ // connect its previousConnection to parent (eg: connect if to if)
+ for (i = 0; i < ilLength; i++) {
+ try {
+ if (createdBlock.outputConnection != null) {
+ //Check for type validity (connect does not do it)
+ if (inputList[i].connection &&
+ inputList[i].connection.checkType_(createdBlock.outputConnection)) {
+ // is connection empty?
+ if (!inputList[i].connection.targetConnection){
+ createdBlock.outputConnection.connect(inputList[i].connection);
+ break;
+ }
+ }
+ } else {
+ createdBlock.previousConnection.connect(inputList[i].connection);
+ }
+ } catch(e) {
+ // We can ignore these exceptions; they happen when connecting two blocks
+ // that should not be connected.
+ }
+ }
+ if (createdBlock.parentBlock_ !== null) return; //Already connected --> return
+
+ // Are both blocks statement blocks?
+ // If so, connect created block below the selected block
+ if (blockSelected.outputConnection == null &&
+ createdBlock.outputConnection == null) {
+ createdBlock.previousConnection.connect(blockSelected.nextConnection);
+ return;
+ }
+
+ // No connections? Try the parent (if it exists)
+ if (blockSelected.parentBlock_) {
+ //Is the parent block a statement?
+ if (blockSelected.parentBlock_.outputConnection == null) {
+ // Is the created block a statment?
+ // If so, connect it below the parent block, which is a statement
+ if(createdBlock.outputConnection == null) {
+ blockSelected.parentBlock_.nextConnection.connect(
+ createdBlock.previousConnection);
+ return;
+ //If it's not, no connections should be made
+ } else return;
+ } else {
+ // try the parent for other connections
+ Blockly.TypeBlock.connectIfPossible(blockSelected.parentBlock_,
+ createdBlock);
+ // recursive call: creates the inner functions again,
+ // but should not be much overhead; if it is, optimise!
+ }
+ }
+};
+
+//--------------------------------------
+// A custom matcher for the auto-complete widget that can handle numbers as well
+// as the default functionality of goog.ui.ac.ArrayMatcher
+goog.provide('Blockly.TypeBlock.ac.AIArrayMatcher');
+
+goog.require('goog.iter');
+goog.require('goog.string');
+
+/**
+ * Extension of goog.ui.ac.ArrayMatcher so that it can handle
+ * any number or string typed in.
+ * @constructor
+ * @param {Array} rows Dictionary of items to match. Can be objects if they
+ * have a toString method that returns the value to match against.
+ * @param {boolean=} opt_noSimilar if true, do not do similarity matches for the
+ * input token against the dictionary.
+ * @extends {goog.ui.ac.ArrayMatcher}
+ */
+Blockly.TypeBlock.ac.AIArrayMatcher = function(rows, opt_noSimilar) {
+ goog.ui.ac.ArrayMatcher.call(rows, opt_noSimilar);
+ this.rows_ = rows;
+ this.useSimilar_ = !opt_noSimilar;
+};
+goog.inherits(Blockly.TypeBlock.ac.AIArrayMatcher, goog.ui.ac.ArrayMatcher);
+
+/**
+ * @inheritDoc
+ */
+Blockly.TypeBlock.ac.AIArrayMatcher.prototype.requestMatchingRows = function(
+ token, maxMatches, matchHandler, opt_fullString) {
+
+ var matches = this.getPrefixMatches(token, maxMatches);
+
+ // Because we allow for similar matches, Button.Text will always appear
+ // before Text. So we handle the 'text' case as a special case here
+ if (token === 'text' || token === 'Text'){
+ goog.array.remove(matches, 'Text');
+ goog.array.insertAt(matches, 'Text', 0);
+ }
+
+ // Added code to handle any number typed in the widget
+ // (including negatives and decimals)
+ var reg = new RegExp('^-?[0-9]\\d*(\.\\d+)?$', 'g');
+ var match = reg.exec(token);
+ if (match && match.length > 0){
+ matches.push(token);
+ }
+
+ // Added code to handle default values for text fields (they start with " or ')
+ var textReg = new RegExp('^[\"|\']+', 'g');
+ var textMatch = textReg.exec(token);
+ if (textMatch && textMatch.length === 1){
+ matches.push(token);
+ }
+
+ if (matches.length === 0 && this.useSimilar_) {
+ matches = this.getSimilarRows(token, maxMatches);
+ }
+
+ matchHandler(token, matches);
+};
+
+var isCharacterKey = function(charCode) {
+ // NOTE: The following regex was generated from http://apps.timwhitlock.info/js/regex#
+ var regex = new RegExp("[\"\'$+0-9<->A-Za-z|~¢-¥ª¬±-³µ¹-º¼-¾À-ˁˆ-ˑˠ-ˤˬˮ\u0300-ʹͶ-ͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ҁ\u0483-ԣԱ-Ֆՙա-և\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7א-תװ-ײ؆-؈؋\u0610-\u061aء-\u065e٠-٩ٮ-ۓە-\u06dc\u06de-\u06e8\u06ea-ۼۿܐ-\u074aݍ-ޱ߀-ߵߺ\u0901-ह\u093c-\u094dॐ-\u0954क़-\u0963०-९ॱ-ॲॻ-ॿ\u0981-\u0983অ-ঌএ-ঐও-নপ-রলশ-হ\u09bc-\u09c4\u09c7-\u09c8\u09cb-ৎ\u09d7ড়-ঢ়য়-\u09e3০-৹\u0a01-\u0a03ਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹ\u0a3c\u0a3e-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51ਖ਼-ੜਫ਼੦-\u0a75\u0a81-\u0a83અ-ઍએ-ઑઓ-નપ-રલ-ળવ-હ\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acdૐૠ-\u0ae3૦-૯૱\u0b01-\u0b03ଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହ\u0b3c-\u0b44\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57ଡ଼-ଢ଼ୟ-\u0b63୦-୯ୱ\u0b82-ஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹ\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcdௐ\u0bd7௦-௲௹\u0c01-\u0c03అ-ఌఎ-ఐఒ-నప-ళవ-హఽ-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56ౘ-ౙౠ-\u0c63౦-౯౸-౾\u0c82-\u0c83ಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹ\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6ೞೠ-\u0ce3೦-೯\u0d02-\u0d03അ-ഌഎ-ഐഒ-നപ-ഹഽ-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57ൠ-\u0d63൦-൵ൺ-ൿ\u0d82-\u0d83අ-ඖක-නඳ-රලව-ෆ\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2-\u0df3ก-\u0e3a฿-\u0e4e๐-๙ກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-\u0eb9\u0ebb-ຽເ-ໄໆ\u0ec8-\u0ecd໐-໙ໜ-ໝༀ\u0f18-\u0f19༠-༳\u0f35\u0f37\u0f39\u0f3e-ཇཉ-ཬ\u0f71-\u0f84\u0f86-ྋ\u0f90-\u0f97\u0f99-\u0fbc\u0fc6က-၉ၐ-႙Ⴀ-Ⴥა-ჺჼᄀ-ᅙᅟ-ᆢᆨ-ᇹሀ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚ\u135f፩-፼ᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙶᚁ-ᚚᚠ-ᛪ\u16ee-\u16f0ᜀ-ᜌᜎ-\u1714ᜠ-\u1734ᝀ-\u1753ᝠ-ᝬᝮ-ᝰ\u1772-\u1773ក-ឳ\u17b6-\u17d3ៗ៛-\u17dd០-៩៰-៹\u180b-\u180d᠐-᠙ᠠ-ᡷᢀ-ᢪᤀ-ᤜ\u1920-\u192b\u1930-\u193b᥆-ᥭᥰ-ᥴᦀ-ᦩ\u19b0-\u19c9᧐-᧙ᨀ-\u1a1b\u1b00-ᭋ᭐-᭙\u1b6b-\u1b73\u1b80-\u1baaᮮ-᮹ᰀ-\u1c37᱀-᱉ᱍ-ᱽᴀ-\u1de6\u1dfe-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼ\u2000-\u206e⁰-ⁱ⁴-⁼ⁿ-₌ₐ-ₔ₠-₵\u20d0-\u20f0ℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ⅉ⅋ⅎ⅓-\u2188←-↔↚-↛↠↣↦↮⇎-⇏⇒⇔⇴-⋿⌈-⌋⌠-⌡⍼⎛-⎳⏜-⏡①-⒛⓪-⓿▷◁◸-◿♯❶-➓⟀-⟄⟇-⟊⟌⟐-⟥⟰-⟿⤀-⦂⦙-⧗⧜-⧻⧾-⫿⬰-⭄⭇-⭌Ⰰ-Ⱞⰰ-ⱞⱠ-Ɐⱱ-ⱽⲀ-ⳤ⳽ⴀ-ⴥⴰ-ⵥⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ\u2de0-\u2dffⸯ々-\u3007\u3021-\u302f〱-〵\u3038-〼ぁ-ゖ\u3099-\u309aゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎ㆒-㆕ㆠ-ㆷㇰ-ㇿ㈠-㈩㉑-㉟㊀-㊉㊱-㊿㐀-䶵一-鿃ꀀ-ꒌꔀ-ꘌꘐ-ꘫꙀ-ꙟꙢ-\ua672\ua67c-\ua67dꙿ-ꚗꜗ-ꜟꜢ-ꞈꞋ-ꞌꟻ-\ua827ꡀ-ꡳ\ua880-\ua8c4꣐-꣙꤀-\ua92dꤰ-\ua953ꨀ-\uaa36ꩀ-\uaa4d꩐-꩙가-힣豈-鶴侮-頻並-龎ff-stﬓ-ﬗיִ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼\ufe00-\ufe0f\ufe20-\ufe26﹢﹤-﹦﹩ﹰ-ﹴﹶ-ﻼ$+0-9<->A-Za-z|~ヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-¬¥-₩←-↓]|[\ud840-\ud868][\udc00-\udfff]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd07-\udd33\udd40-\udd78\udd8a\uddfd\ude80-\ude9c\udea0-\uded0\udf00-\udf1e\udf20-\udf23\udf30-\udf4a\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udca0-\udca9]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f\udd00-\udd19\udd20-\udd39\ude00-\ude03\ude05-\ude06\ude0c-\ude13\ude15-\ude17\ude19-\ude33\ude38-\ude3a\ude3f-\ude47]|\ud808[\udc00-\udf6e]|\ud809[\udc00-\udc62]|\ud834[\udd65-\udd69\udd6d-\udd72\udd7b-\udd82\udd85-\udd8b\uddaa-\uddad\ude42-\ude44\udf60-\udf71]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udfcb\udfce-\udfff]|\ud869[\udc00-\uded6]|\ud87e[\udc00-\ude1d]|\udb40[\udd00-\uddef]");
+
+ //var regex = new RegExp("
+
+//");
+
+
+
+ // NOTE: if the regex above ever gives trouble, we can use the simpler one.
+ //var regex = new RegExp("[0-9A-Za-z]");
+
+ if (regex.test(String.fromCharCode(charCode))) {
+ return true;
+ } else {
+ return false;
+ }
+};
diff --git a/generators/java.js b/generators/java.js
new file mode 100644
index 00000000000..210c9a57dcc
--- /dev/null
+++ b/generators/java.js
@@ -0,0 +1,334 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Helper functions for generating Java for blocks.
+ * @author toebes@extremenetworks.com (John Toebes)
+ * Loosely based on Python version by fraser@google.com (Neil Fraser)
+ */
+'use strict';
+
+goog.provide('Blockly.Java');
+
+goog.require('Blockly.Generator');
+
+
+/**
+ * Java code generator.
+ * @type !Blockly.Generator
+ */
+Blockly.Java = new Blockly.Generator('Java');
+
+/**
+ * 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.
+ * @private
+ */
+Blockly.Java.addReservedWords(
+ // import keyword
+ // print ','.join(keyword.kwlist)
+ // http://en.wikipedia.org/wiki/List_of_Java_keywords
+ 'abstract,assert,boolean,break,case,catch,class,const,continue,default,do,double,else,enum,extends,final,finally,float,for,goto,if,implements,import,instanceof,int,interface,long,native,new,package,private,protected,public,return,short,static,strictfp,super,switch,synchronized,this,throw,throws,transient,try,void,volatile,while,' +
+ //http://en.wikipedia.org/wiki/List_of_Java_keywords#Reserved_words_for_literal_values
+ 'false,null,true,' +
+ // http://docs.Java.org/library/functions.html
+ 'abs,divmod,input,open,staticmethod,all,enumerate,int,ord,str,any,eval,isinstance,pow,sum,basestring,execfile,issubclass,print,super,bin,file,iter,property,tuple,bool,filter,len,range,type,bytearray,float,list,raw_input,unichr,callable,format,locals,reduce,unicode,chr,frozenset,long,reload,vars,classmethod,getattr,map,repr,xrange,cmp,globals,max,reversed,zip,compile,hasattr,memoryview,round,__import__,complex,hash,min,set,apply,delattr,help,next,setattr,buffer,dict,hex,object,slice,coerce,dir,id,oct,sorted,intern');
+
+/**
+ * Order of operation ENUMs.
+ * https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html
+ */
+Blockly.Java.ORDER_ATOMIC = 0; // 0 "" ...
+Blockly.Java.ORDER_COLLECTION = 1; // tuples, lists, dictionaries
+Blockly.Java.ORDER_STRING_CONVERSION = 1; // `expression...`
+
+Blockly.Java.ORDER_MEMBER = 2; // . []
+Blockly.Java.ORDER_FUNCTION_CALL = 2; // ()
+
+Blockly.Java.ORDER_POSTFIX = 3; // expr++ expr--
+Blockly.Java.ORDER_EXPONENTIATION = 3; // ** TODO: Replace this
+
+Blockly.Java.ORDER_LOGICAL_NOT = 3; // not
+Blockly.Java.ORDER_UNARY_SIGN = 4; // ++expr --expr +expr -expr ~ !
+Blockly.Java.ORDER_MULTIPLICATIVE = 5; // * / %
+Blockly.Java.ORDER_ADDITIVE = 6; // + -
+Blockly.Java.ORDER_BITWISE_SHIFT = 7; // << >> >>>
+Blockly.Java.ORDER_RELATIONAL = 8; // < > <= >= instanceof
+Blockly.Java.ORDER_EQUALITY = 9; // == !=
+Blockly.Java.ORDER_BITWISE_AND = 10; // &
+Blockly.Java.ORDER_BITWISE_XOR = 11; // ^
+Blockly.Java.ORDER_BITWISE_OR = 12; // |
+Blockly.Java.ORDER_LOGICAL_AND = 13; // &&
+Blockly.Java.ORDER_LOGICAL_OR = 14; // ||
+Blockly.Java.ORDER_CONDITIONAL = 15; // ? :
+
+Blockly.Java.ORDER_ASSIGNMENT = 16; // = += -= *= /= %= &= ^= |= <<= >>= >>>=
+
+Blockly.Java.ORDER_NONE = 99; // (...)
+
+/**
+ * Empty loops or conditionals are not allowed in Java.
+ */
+Blockly.Java.PASS = ' {}\n';
+
+/**
+ * Closure code for a section
+ */
+Blockly.Java.POSTFIX = '';
+/**
+ * Any extra indent to be added to the currently generating code block
+ */
+Blockly.Java.EXTRAINDENT = '';
+/**
+ * List of all known variable types. Only set after a call to workspaceToCode
+ */
+Blockly.Java.VariableTypes_ = {};
+/**
+ * Default Name of the application for use by all generated classes
+ */
+Blockly.Java.AppName_ = 'MyApp';
+/**
+ * List of libraries used globally by the generated java code. These are
+ * Processed by Blockly.Java.addImport
+ */
+Blockly.Java.needImports_ = ['javax.json.Json',
+ 'javax.json.JsonArray',
+ 'javax.json.JsonObject',
+ 'javax.json.JsonReader',
+ 'javax.json.stream.JsonParsingException',
+ 'java.io.IOException',
+ 'java.io.StringReader'
+ ];
+
+/**
+ * Set the application name for generated classes
+ * @param {string} name Name for the application for any generated code
+ */
+Blockly.Java.SetAppName = function(name) {
+ if (!name || name === '') {
+ name = 'MyApp';
+ }
+ this.AppName_ = name;
+ console.log(this.AppName_+' --- <'+name+ '>');
+}
+
+/**
+ * Get the application name for generated classes
+ * @return {string} name Name for the application for any generated code
+ */
+Blockly.Java.GetAppName = function() {
+ return this.AppName_;
+}
+
+/**
+ * Get the Java type of a variable by name
+ * @param {string} variable Name of the variable to get the type for
+ * @return {string} type Java type for the variablee
+ */
+Blockly.Java.GetVariableType = function(variable) {
+ var type = Blockly.Java.VariableTypes_[variable];
+ if (!type) {
+ type = 'string/*UNKNOWN_TYPE*/';
+ }
+ return type;
+};
+
+/**
+ * Add a reference to a library to import
+ * @param {string} importlib Name of the library to add to the import list
+ */
+Blockly.Java.addImport = function(importlib) {
+ var importStr = 'import '+importlib+';';
+ Blockly.Java.imports_[importStr] = importStr;
+};
+
+/**
+ * Get the list of all libraries to import
+ * @param {!Array} imports Array of libraries to add to the list
+ * @return {string} code Java code for importing all libraries referenced
+ */
+Blockly.Java.getImports = function(imports) {
+ // Add any of the imports that the top level code needs
+ if (imports) {
+ for(var i = 0; i < imports.length; i++) {
+ Blockly.Java.addImport(imports[i]);
+ }
+ }
+
+ var keys = goog.object.getValues(Blockly.Java.imports_);
+ goog.array.sort(keys);
+ return (keys.join("\n"));
+};
+
+/**
+ * Initialise the database of variable names.
+ * @param {!Blockly.Workspace} workspace Workspace to generate code from.
+ */
+Blockly.Java.init = function(workspace, imports) {
+ // Create a dictionary of definitions to be printed before the code.
+ Blockly.Java.definitions_ = Object.create(null);
+ // Create a dictionary mapping desired function names in definitions_
+ // to actual function names (to avoid collisions with user functions).
+ Blockly.Java.functionNames_ = Object.create(null);
+ // Create a dictionary of all the libraries which would be needed
+ Blockly.Java.imports_ = Object.create(null);
+ // Start with the defaults that all the code depends on
+ for(var i = 0; i < Blockly.Java.needImports_.length; i++) {
+ Blockly.Java.addImport(Blockly.Java.needImports_[i]);
+ }
+ if (!Blockly.Java.variableDB_) {
+ Blockly.Java.variableDB_ =
+ new Blockly.Names(Blockly.Java.RESERVED_WORDS_);
+ } else {
+ Blockly.Java.variableDB_.reset();
+ }
+
+ var defvars = [];
+ var variables = Blockly.Variables.allVariables(workspace);
+ var vartypes = Blockly.Variables.allVariablesTypes(workspace);
+
+ for (var x = 0; x < variables.length; x++) {
+ var key = variables[x];
+ var type = vartypes[key];
+ if (type === 'JSON') {
+ type = 'JsonObject';
+ } else if (type === 'Array') {
+ type = 'JsonArray';
+ } else if (type === 'Boolean') {
+ type = 'Boolean';
+ } else if (type === 'String') {
+ type = 'String';
+ } else if (type === 'Number') {
+ type = 'int';
+ } else if (type !== '') {
+ if (Blockly.Blocks[type] && Blockly.Blocks[type].GBPClass ) {
+ type = Blockly.Blocks[type].GBPClass;
+ } else {
+ console.log('Unknown type for '+key+' using String for'+type);
+ type = 'Object/*UNKNOWN_TYPE for '+type+'*/';
+ }
+ } else {
+ // Unknown type
+ console.log('Unknown type for '+key+' using String');
+ type = 'String/*UNKNOWN_TYPE*/';
+ }
+
+ Blockly.Java.VariableTypes_[key] = type;
+
+ defvars.push('protected ' +
+ type + ' '+
+ Blockly.Java.variableDB_.getName(variables[x],
+ Blockly.Variables.NAME_TYPE) +
+ ';');
+ }
+ Blockly.Java.definitions_['variables'] = defvars.join('\n');
+};
+
+/**
+ * Prepend the generated code with the variable definitions.
+ * @param {string} code Generated code.
+ * @return {string} Completed code.
+ */
+Blockly.Java.finish = function(code) {
+ // Convert the definitions dictionary into a list.
+ var imports = [];
+ var definitions = [];
+ for (var name in Blockly.Java.definitions_) {
+ var def = Blockly.Java.definitions_[name];
+ if (def.match(/^(from\s+\S+\s+)?import\s+\S+/)) {
+ imports.push(def);
+ } else {
+ definitions.push(def);
+ }
+ }
+ var allDefs = imports.join('\n') + '\n\n' + definitions.join('\n\n');
+ return allDefs.replace(/\n\n+/g, '\n\n').replace(/\n*$/, '\n\n\n') + code;
+};
+
+/**
+ * Naked values are top-level blocks with outputs that aren't plugged into
+ * anything.
+ * @param {string} line Line of generated code.
+ * @return {string} Legal line of code.
+ */
+Blockly.Java.scrubNakedValue = function(line) {
+ return line + '\n';
+};
+
+/**
+ * Encode a string as a properly escaped Java string, complete with quotes.
+ * @param {string} string Text to encode.
+ * @return {string} Java string.
+ * @private
+ */
+Blockly.Java.quote_ = function(string) {
+ // TODO: This is a quick hack. Replace with goog.string.quote
+ string = string.replace(/\\/g, '\\\\')
+ .replace(/\n/g, '\\\n')
+ .replace(/\%/g, '\\%')
+ .replace(/"/g, '\\"');
+ return '"' + string + '"';
+};
+
+/**
+ * Common tasks for generating Java from blocks.
+ * Handles comments for the specified block and any connected value blocks.
+ * Calls any statements following this block.
+ * @param {!Blockly.Block} block The current block.
+ * @param {string} code The Java code created for this block.
+ * @return {string} Java code with comments and subsequent blocks added.
+ * @private
+ */
+Blockly.Java.scrub_ = function(block, code, parms) {
+ var commentCode = '';
+ // Only collect comments for blocks that aren't inline.
+ if (!block.outputConnection || !block.outputConnection.targetConnection) {
+ // Collect comment for this block.
+ var comment = block.getCommentText();
+ if (comment) {
+ commentCode += Blockly.Java.prefixLines(comment, '// ') + '\n';
+ }
+ // Collect comments for all value arguments.
+ // Don't collect comments for nested statements.
+ for (var x = 0; x < block.inputList.length; x++) {
+ if (block.inputList[x].type == Blockly.INPUT_VALUE) {
+ var childBlock = block.inputList[x].connection.targetBlock();
+ if (childBlock) {
+ var comment = Blockly.Java.allNestedComments(childBlock);
+ if (comment) {
+ commentCode += Blockly.Java.prefixLines(comment, '// ');
+ }
+ }
+ }
+ }
+ }
+ var postFix = Blockly.Java.POSTFIX;
+ Blockly.Java.POSTFIX = '';
+ var extraIndent = Blockly.Java.EXTRAINDENT;
+ Blockly.Java.EXTRAINDENT = '';
+ var nextBlock = block.nextConnection && block.nextConnection.targetBlock();
+ var nextCode = Blockly.Java.blockToCode(nextBlock, parms);
+ if (extraIndent != '') {
+ nextCode = Blockly.Java.prefixLines(nextCode, extraIndent);
+ }
+ return commentCode + code + nextCode + postFix;
+};
diff --git a/generators/java/colour.js b/generators/java/colour.js
new file mode 100644
index 00000000000..05166054563
--- /dev/null
+++ b/generators/java/colour.js
@@ -0,0 +1,86 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for colour blocks.
+ * @author fraser@google.com (Neil Fraser)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.colour');
+
+goog.require('Blockly.Java');
+
+
+Blockly.Java['colour_picker'] = function(block) {
+ // Colour picker.
+ var code = '\'' + block.getFieldValue('COLOUR') + '\'';
+ return [code, Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['colour_random'] = function(block) {
+ // Generate a random colour.
+ Blockly.Java.definitions_['import_random'] = 'import random';
+ var code = '\'#%06x\' % random.randint(0, 2**24 - 1)';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['colour_rgb'] = function(block) {
+ // Compose a colour from RGB components expressed as percentages.
+ var functionName = Blockly.Java.provideFunction_(
+ 'colour_rgb',
+ [ 'def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b):',
+ ' r = round(min(100, max(0, r)) * 2.55)',
+ ' g = round(min(100, max(0, g)) * 2.55)',
+ ' b = round(min(100, max(0, b)) * 2.55)',
+ ' return \'#%02x%02x%02x\' % (r, g, b)']);
+ var r = Blockly.Java.valueToCode(block, 'RED',
+ Blockly.Java.ORDER_NONE) || 0;
+ var g = Blockly.Java.valueToCode(block, 'GREEN',
+ Blockly.Java.ORDER_NONE) || 0;
+ var b = Blockly.Java.valueToCode(block, 'BLUE',
+ Blockly.Java.ORDER_NONE) || 0;
+ var code = functionName + '(' + r + ', ' + g + ', ' + b + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['colour_blend'] = function(block) {
+ // Blend two colours together.
+ var functionName = Blockly.Java.provideFunction_(
+ 'colour_blend',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ +
+ '(colour1, colour2, ratio):',
+ ' r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16)',
+ ' g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16)',
+ ' b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16)',
+ ' ratio = min(1, max(0, ratio))',
+ ' r = round(r1 * (1 - ratio) + r2 * ratio)',
+ ' g = round(g1 * (1 - ratio) + g2 * ratio)',
+ ' b = round(b1 * (1 - ratio) + b2 * ratio)',
+ ' return \'#%02x%02x%02x\' % (r, g, b)']);
+ var colour1 = Blockly.Java.valueToCode(block, 'COLOUR1',
+ Blockly.Java.ORDER_NONE) || '\'#000000\'';
+ var colour2 = Blockly.Java.valueToCode(block, 'COLOUR2',
+ Blockly.Java.ORDER_NONE) || '\'#000000\'';
+ var ratio = Blockly.Java.valueToCode(block, 'RATIO',
+ Blockly.Java.ORDER_NONE) || 0;
+ var code = functionName + '(' + colour1 + ', ' + colour2 + ', ' + ratio + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
diff --git a/generators/java/lists.js b/generators/java/lists.js
new file mode 100644
index 00000000000..873618722ff
--- /dev/null
+++ b/generators/java/lists.js
@@ -0,0 +1,334 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for list blocks.
+ * @author q.neutron@gmail.com (Quynh Neutron)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.lists');
+
+goog.require('Blockly.Java');
+
+
+Blockly.Java['lists_create_empty'] = function(block) {
+ // Create an empty list.
+ return ['[]', Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['lists_create_with'] = function(block) {
+ // Create a list with any number of elements of any type.
+ var code = new Array(block.itemCount_);
+ for (var n = 0; n < block.itemCount_; n++) {
+ code[n] = Blockly.Java.valueToCode(block, 'ADD' + n,
+ Blockly.Java.ORDER_NONE) || 'None';
+ }
+ code = '[' + code.join(', ') + ']';
+ return [code, Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['lists_repeat'] = function(block) {
+ // Create a list with one element repeated.
+ var argument0 = Blockly.Java.valueToCode(block, 'ITEM',
+ Blockly.Java.ORDER_NONE) || 'None';
+ var argument1 = Blockly.Java.valueToCode(block, 'NUM',
+ Blockly.Java.ORDER_MULTIPLICATIVE) || '0';
+ var code = '[' + argument0 + '] * ' + argument1;
+ return [code, Blockly.Java.ORDER_MULTIPLICATIVE];
+};
+
+Blockly.Java['lists_length'] = function(block) {
+ // List length.
+ var argument0 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || '[]';
+ return ['len(' + argument0 + ')', Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['lists_isEmpty'] = function(block) {
+ // Is the list empty?
+ var argument0 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || '[]';
+ var code = 'not len(' + argument0 + ')';
+ return [code, Blockly.Java.ORDER_LOGICAL_NOT];
+};
+
+Blockly.Java['lists_indexOf'] = function(block) {
+ // Find an item in the list.
+ var argument0 = Blockly.Java.valueToCode(block, 'FIND',
+ Blockly.Java.ORDER_NONE) || '[]';
+ var argument1 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ var code;
+ if (block.getFieldValue('END') == 'FIRST') {
+ var functionName = Blockly.Java.provideFunction_(
+ 'first_index',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(myList, elem):',
+ ' try: theIndex = myList.index(elem) + 1',
+ ' except: theIndex = 0',
+ ' return theIndex']);
+ code = functionName + '(' + argument1 + ', ' + argument0 + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else {
+ var functionName = Blockly.Java.provideFunction_(
+ 'last_index',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(myList, elem):',
+ ' try: theIndex = len(myList) - myList[::-1].index(elem)',
+ ' except: theIndex = 0',
+ ' return theIndex']);
+ code = functionName + '(' + argument1 + ', ' + argument0 + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ }
+};
+
+Blockly.Java['lists_getIndex'] = function(block) {
+ // Get element at index.
+ // Note: Until January 2013 this block did not have MODE or WHERE inputs.
+ var mode = block.getFieldValue('MODE') || 'GET';
+ var where = block.getFieldValue('WHERE') || 'FROM_START';
+ var at = Blockly.Java.valueToCode(block, 'AT',
+ Blockly.Java.ORDER_UNARY_SIGN) || '1';
+ var list = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_MEMBER) || '[]';
+
+ if (where == 'FIRST') {
+ if (mode == 'GET') {
+ var code = list + '.getJsonObject(0)';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ } else {
+ var code = list + '.pop(0)';
+ if (mode == 'GET_REMOVE') {
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else if (mode == 'REMOVE') {
+ return code + '\n';
+ }
+ }
+ } else if (where == 'LAST') {
+ if (mode == 'GET') {
+ var code = list + '[-1]';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ } else {
+ var code = list + '.pop()';
+ if (mode == 'GET_REMOVE') {
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else if (mode == 'REMOVE') {
+ return code + '\n';
+ }
+ }
+ } else if (where == 'FROM_START') {
+ // Blockly uses one-based indicies.
+ if (Blockly.isNumber(at)) {
+ // If the index is a naked number, decrement it right now.
+ at = parseInt(at, 10) - 1;
+ } else {
+ // If the index is dynamic, decrement it in code.
+ at = 'int(' + at + ' - 1)';
+ }
+ if (mode == 'GET') {
+ var code = list + '.getJsonElement(' + at + ')';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ } else {
+ var code = list + '.pop(' + at + ')';
+ if (mode == 'GET_REMOVE') {
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else if (mode == 'REMOVE') {
+ return code + '\n';
+ }
+ }
+ } else if (where == 'FROM_END') {
+ if (mode == 'GET') {
+ var code = list + '[-' + at + ']';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ } else {
+ var code = list + '.pop(-' + at + ')';
+ if (mode == 'GET_REMOVE') {
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else if (mode == 'REMOVE') {
+ return code + '\n';
+ }
+ }
+ } else if (where == 'RANDOM') {
+ Blockly.Java.definitions_['import_random'] = 'import random';
+ if (mode == 'GET') {
+ code = 'random.choice(' + list + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else {
+ var functionName = Blockly.Java.provideFunction_(
+ 'lists_remove_random_item',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(myList):',
+ ' x = int(random.random() * len(myList))',
+ ' return myList.pop(x)']);
+ code = functionName + '(' + list + ')';
+ if (mode == 'GET_REMOVE') {
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else if (mode == 'REMOVE') {
+ return code + '\n';
+ }
+ }
+ }
+ throw 'Unhandled combination (lists_getIndex).';
+};
+
+Blockly.Java['lists_setIndex'] = function(block) {
+ // Set element at index.
+ // Note: Until February 2013 this block did not have MODE or WHERE inputs.
+ var list = Blockly.Java.valueToCode(block, 'LIST',
+ Blockly.Java.ORDER_MEMBER) || '[]';
+ var mode = block.getFieldValue('MODE') || 'GET';
+ var where = block.getFieldValue('WHERE') || 'FROM_START';
+ var at = Blockly.Java.valueToCode(block, 'AT',
+ Blockly.Java.ORDER_NONE) || '1';
+ var value = Blockly.Java.valueToCode(block, 'TO',
+ Blockly.Java.ORDER_NONE) || 'None';
+ // Cache non-trivial values to variables to prevent repeated look-ups.
+ // Closure, which accesses and modifies 'list'.
+ function cacheList() {
+ if (list.match(/^\w+$/)) {
+ return '';
+ }
+ var listVar = Blockly.Java.variableDB_.getDistinctName(
+ 'tmp_list', Blockly.Variables.NAME_TYPE);
+ var code = listVar + ' = ' + list + '\n';
+ list = listVar;
+ return code;
+ }
+ if (where == 'FIRST') {
+ if (mode == 'SET') {
+ return list + '[0] = ' + value + '\n';
+ } else if (mode == 'INSERT') {
+ return list + '.insert(0, ' + value + ')\n';
+ }
+ } else if (where == 'LAST') {
+ if (mode == 'SET') {
+ return list + '[-1] = ' + value + '\n';
+ } else if (mode == 'INSERT') {
+ return list + '.append(' + value + ')\n';
+ }
+ } else if (where == 'FROM_START') {
+ // Blockly uses one-based indicies.
+ if (Blockly.isNumber(at)) {
+ // If the index is a naked number, decrement it right now.
+ at = parseInt(at, 10) - 1;
+ } else {
+ // If the index is dynamic, decrement it in code.
+ at = 'int(' + at + ' - 1)';
+ }
+ if (mode == 'SET') {
+ return list + '[' + at + '] = ' + value + '\n';
+ } else if (mode == 'INSERT') {
+ return list + '.insert(' + at + ', ' + value + ')\n';
+ }
+ } else if (where == 'FROM_END') {
+ if (mode == 'SET') {
+ return list + '[-' + at + '] = ' + value + '\n';
+ } else if (mode == 'INSERT') {
+ return list + '.insert(-' + at + ', ' + value + ')\n';
+ }
+ } else if (where == 'RANDOM') {
+ Blockly.Java.definitions_['import_random'] = 'import random';
+ var code = cacheList();
+ var xVar = Blockly.Java.variableDB_.getDistinctName(
+ 'tmp_x', Blockly.Variables.NAME_TYPE);
+ code += xVar + ' = int(random.random() * len(' + list + '))\n';
+ if (mode == 'SET') {
+ code += list + '[' + xVar + '] = ' + value + '\n';
+ return code;
+ } else if (mode == 'INSERT') {
+ code += list + '.insert(' + xVar + ', ' + value + ')\n';
+ return code;
+ }
+ }
+ throw 'Unhandled combination (lists_setIndex).';
+};
+
+Blockly.Java['lists_getSublist'] = function(block) {
+ // Get sublist.
+ var list = Blockly.Java.valueToCode(block, 'LIST',
+ Blockly.Java.ORDER_MEMBER) || '[]';
+ var where1 = block.getFieldValue('WHERE1');
+ var where2 = block.getFieldValue('WHERE2');
+ var at1 = Blockly.Java.valueToCode(block, 'AT1',
+ Blockly.Java.ORDER_ADDITIVE) || '1';
+ var at2 = Blockly.Java.valueToCode(block, 'AT2',
+ Blockly.Java.ORDER_ADDITIVE) || '1';
+ if (where1 == 'FIRST' || (where1 == 'FROM_START' && at1 == '1')) {
+ at1 = '';
+ } else if (where1 == 'FROM_START') {
+ // Blockly uses one-based indicies.
+ if (Blockly.isNumber(at1)) {
+ // If the index is a naked number, decrement it right now.
+ at1 = parseInt(at1, 10) - 1;
+ } else {
+ // If the index is dynamic, decrement it in code.
+ at1 = 'int(' + at1 + ' - 1)';
+ }
+ } else if (where1 == 'FROM_END') {
+ if (Blockly.isNumber(at1)) {
+ at1 = -parseInt(at1, 10);
+ } else {
+ at1 = '-int(' + at1 + ')';
+ }
+ }
+ if (where2 == 'LAST' || (where2 == 'FROM_END' && at2 == '1')) {
+ at2 = '';
+ } else if (where1 == 'FROM_START') {
+ if (Blockly.isNumber(at2)) {
+ at2 = parseInt(at2, 10);
+ } else {
+ at2 = 'int(' + at2 + ')';
+ }
+ } else if (where1 == 'FROM_END') {
+ if (Blockly.isNumber(at2)) {
+ // If the index is a naked number, increment it right now.
+ // Add special case for -0.
+ at2 = 1 - parseInt(at2, 10);
+ if (at2 == 0) {
+ at2 = '';
+ }
+ } else {
+ // If the index is dynamic, increment it in code.
+ Blockly.Java.definitions_['import_sys'] = 'import sys';
+ at2 = 'int(1 - ' + at2 + ') or sys.maxsize';
+ }
+ }
+ var code = list + '[' + at1 + ' : ' + at2 + ']';
+ return [code, Blockly.Java.ORDER_MEMBER];
+};
+
+Blockly.Java['lists_split'] = function(block) {
+ // Block for splitting text into a list, or joining a list into text.
+ var mode = block.getFieldValue('MODE');
+ if (mode == 'SPLIT') {
+ var value_input = Blockly.Java.valueToCode(block, 'INPUT',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ var value_delim = Blockly.Java.valueToCode(block, 'DELIM',
+ Blockly.Java.ORDER_NONE);
+ var code = value_input + '.split(' + value_delim + ')';
+ } else if (mode == 'JOIN') {
+ var value_input = Blockly.Java.valueToCode(block, 'INPUT',
+ Blockly.Java.ORDER_NONE) || '[]';
+ var value_delim = Blockly.Java.valueToCode(block, 'DELIM',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ var code = value_delim + '.join(' + value_input + ')';
+ } else {
+ throw 'Unknown mode: ' + mode;
+ }
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
diff --git a/generators/java/logic.js b/generators/java/logic.js
new file mode 100644
index 00000000000..a9c94f6c480
--- /dev/null
+++ b/generators/java/logic.js
@@ -0,0 +1,128 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for logic blocks.
+ * @author toebes@extremenetworks.com (John Toebes)
+ * Based on Python version by q.neutron@gmail.com (Quynh Neutron)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.logic');
+
+goog.require('Blockly.Java');
+
+Blockly.Java['controls_if'] = function(block) {
+ // If/elseif/else condition.
+ var n = 0;
+ var argument = Blockly.Java.valueToCode(block, 'IF' + n,
+ Blockly.Java.ORDER_NONE) || 'false';
+ var branch = Blockly.Java.statementToCode(block, 'DO' + n) ||
+ Blockly.Java.PASS;
+ var code = 'if (' + argument + ') {\n' + branch;
+ for (n = 1; n <= block.elseifCount_; n++) {
+ argument = Blockly.Java.valueToCode(block, 'IF' + n,
+ Blockly.Java.ORDER_NONE) || 'false';
+ branch = Blockly.Java.statementToCode(block, 'DO' + n) ||
+ Blockly.Java.PASS;
+ code += '} else if (' + argument + ') {\n' + branch;
+ }
+ if (block.elseCount_) {
+ branch = Blockly.Java.statementToCode(block, 'ELSE') ||
+ Blockly.Java.PASS;
+ code += '} else {\n' + branch;
+ }
+ code += '}\n';
+ return code;
+};
+
+Blockly.Java['logic_compare'] = function(block) {
+ // Comparison operator.
+ var OPERATORS = {
+ 'EQ': '==',
+ 'NEQ': '!=',
+ 'LT': '<',
+ 'LTE': '<=',
+ 'GT': '>',
+ 'GTE': '>='
+ };
+ var operator = OPERATORS[block.getFieldValue('OP')];
+ var order = Blockly.Java.ORDER_RELATIONAL;
+ var argument0 = Blockly.Java.valueToCode(block, 'A', order) || '0';
+ var argument1 = Blockly.Java.valueToCode(block, 'B', order) || '0';
+ var code = argument0 + ' ' + operator + ' ' + argument1;
+ return [code, order];
+};
+
+Blockly.Java['logic_operation'] = function(block) {
+ // Operations 'and', 'or'.
+ var operator = (block.getFieldValue('OP') == 'AND') ? ' && ' : ' || ';
+ var order = (operator == 'and') ? Blockly.Java.ORDER_LOGICAL_AND :
+ Blockly.Java.ORDER_LOGICAL_OR;
+ var argument0 = Blockly.Java.valueToCode(block, 'A', order);
+ var argument1 = Blockly.Java.valueToCode(block, 'B', order);
+ if (!argument0 && !argument1) {
+ // If there are no arguments, then the return value is false.
+ argument0 = 'false';
+ argument1 = 'false';
+ } else {
+ // Single missing arguments have no effect on the return value.
+ var defaultArgument = (operator == ' && ') ? 'true' : 'false';
+ if (!argument0) {
+ argument0 = defaultArgument;
+ }
+ if (!argument1) {
+ argument1 = defaultArgument;
+ }
+ }
+ var code = argument0 + operator + argument1;
+ return [code, order];
+};
+
+Blockly.Java['logic_negate'] = function(block) {
+ // Negation.
+ var argument0 = Blockly.Java.valueToCode(block, 'BOOL',
+ Blockly.Java.ORDER_LOGICAL_NOT) || 'true';
+ var code = '!(' + argument0 + ')';
+ return [code, Blockly.Java.ORDER_LOGICAL_NOT];
+};
+
+Blockly.Java['logic_boolean'] = function(block) {
+ // Boolean values true and false.
+ var code = (block.getFieldValue('BOOL') == 'TRUE') ? 'true' : 'false';
+ return [code, Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['logic_null'] = function(block) {
+ // Null data type.
+ return ['null', Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['logic_ternary'] = function(block) {
+ // Ternary operator.
+ var value_if = Blockly.Java.valueToCode(block, 'IF',
+ Blockly.Java.ORDER_CONDITIONAL) || 'false';
+ var value_then = Blockly.Java.valueToCode(block, 'THEN',
+ Blockly.Java.ORDER_CONDITIONAL) || 'null';
+ var value_else = Blockly.Java.valueToCode(block, 'ELSE',
+ Blockly.Java.ORDER_CONDITIONAL) || 'null';
+ var code = value_if + ' ? ' + value_then + ' : ' + value_else;
+ return [code, Blockly.Java.ORDER_CONDITIONAL];
+};
diff --git a/generators/java/loops.js b/generators/java/loops.js
new file mode 100644
index 00000000000..1b52eeedaf5
--- /dev/null
+++ b/generators/java/loops.js
@@ -0,0 +1,165 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for loop blocks.
+ * @author toebes@extremenetworks.com (John Toebes)
+ * Based on Python version by q.neutron@gmail.com (Quynh Neutron)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.loops');
+
+goog.require('Blockly.Java');
+
+Blockly.Java['controls_repeat'] = function(block) {
+ // Repeat n times (internal number).
+ var repeats = parseInt(block.getFieldValue('TIMES'), 10);
+ var branch = Blockly.Java.statementToCode(block, 'DO');
+ branch = Blockly.Java.addLoopTrap(branch, block.id) ||
+ Blockly.Java.PASS;
+ var loopVar = Blockly.Java.variableDB_.getDistinctName(
+ 'count', Blockly.Variables.NAME_TYPE);
+ var code = 'for (int ' + loopVar + '=0; '+
+ loopVar+' < ' + repeats + ';'+
+ loopVar+'++) {\n' +
+ branch +
+ '} // end for\n';
+ return code;
+};
+
+Blockly.Java['controls_repeat_ext'] = function(block) {
+ // Repeat n times (external number).
+ var repeats = Blockly.Java.valueToCode(block, 'TIMES',
+ Blockly.Java.ORDER_NONE) || '0';
+ if (Blockly.isNumber(repeats)) {
+ repeats = parseInt(repeats, 10);
+ } else {
+ repeats = 'int(' + repeats + ')';
+ }
+ var branch = Blockly.Java.statementToCode(block, 'DO');
+ branch = Blockly.Java.addLoopTrap(branch, block.id) ||
+ Blockly.Java.PASS;
+ var loopVar = Blockly.Java.variableDB_.getDistinctName(
+ 'count', Blockly.Variables.NAME_TYPE);
+ var code = 'for (int ' + loopVar + '=0; '+
+ loopVar +' < ' + repeats + ';'+
+ loopVar + '++) {\n' +
+ branch +
+ '} // end for\n';
+ return code;
+};
+
+Blockly.Java['controls_whileUntil'] = function(block) {
+ // Do while/until loop.
+ var until = block.getFieldValue('MODE') == 'UNTIL';
+ var argument0 = Blockly.Java.valueToCode(block, 'BOOL',
+ until ? Blockly.Java.ORDER_LOGICAL_NOT :
+ Blockly.Java.ORDER_NONE) || 'false';
+ var branch = Blockly.Java.statementToCode(block, 'DO');
+ branch = Blockly.Java.addLoopTrap(branch, block.id) ||
+ Blockly.Java.PASS;
+ if (until) {
+ argument0 = '!' + argument0;
+ }
+ return 'while (' + argument0 + ') {\n' +
+ branch +
+ '} // end while\n';
+};
+
+Blockly.Java['controls_for'] = function(block) {
+ // For loop.
+ var variable0 = Blockly.Java.variableDB_.getName(
+ block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
+ var vartype0 = Blockly.Java.GetVariableType(block.getFieldValue('VAR'));
+ var argument0 = Blockly.Java.valueToCode(block, 'FROM',
+ Blockly.Java.ORDER_NONE) || '0';
+ var argument1 = Blockly.Java.valueToCode(block, 'TO',
+ Blockly.Java.ORDER_NONE) || '0';
+ var increment = Blockly.Java.valueToCode(block, 'BY',
+ Blockly.Java.ORDER_NONE) || '1';
+ var branch = Blockly.Java.statementToCode(block, 'DO');
+ branch = Blockly.Java.addLoopTrap(branch, block.id) ||
+ Blockly.Java.PASS;
+
+ var code = '';
+ var range;
+
+ if (Blockly.isNumber(argument0) &&
+ Blockly.isNumber(argument1) &&
+ Blockly.isNumber(increment)) {
+ // All parameters are simple numbers.
+ argument0 = parseFloat(argument0);
+ argument1 = parseFloat(argument1);
+ increment = Math.abs(parseFloat(increment));
+ var direction = '<=';
+ var doincrement = '++';
+ // All parameters are integers.
+ if (argument0 > argument1) {
+ // Count down.
+ direction = '>=';
+ increment = -increment;
+ }
+ if (increment < 0) {
+ doincrement = ' -= ' + Math.abs(increment);
+ } else if (increment != 1) {
+ doincrement = ' += ' + increment;
+ }
+ code += 'for (' + variable0 + ' = ' + argument0 + '; ' +
+ variable0 + direction + argument1 + '; ' +
+ variable0 + doincrement + ')';
+ } else {
+ code += 'for (' + variable0 + ' = ' + argument0 + '; ' +
+ variable0 + '<=' + argument1 + '; ' +
+ variable0 + ' += ' + increment + ')';
+
+ }
+ code += ' {\n' +
+ branch +
+ '} // end for\n';
+ return code;
+};
+
+Blockly.Java['controls_forEach'] = function(block) {
+ // For each loop.
+ var variable0 = Blockly.Java.variableDB_.getName(
+ block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
+ var vartype0 = Blockly.Java.GetVariableType(block.getFieldValue('VAR'));
+ var argument0 = Blockly.Java.valueToCode(block, 'LIST',
+ Blockly.Java.ORDER_RELATIONAL) || '[]';
+ var branch = Blockly.Java.statementToCode(block, 'DO');
+ branch = Blockly.Java.addLoopTrap(branch, block.id) ||
+ Blockly.Java.PASS;
+ var code = 'for (' +vartype0 + ' ' + variable0 + ' :' + argument0 + ') {\n'
+ + branch + '} // end for\n';
+ return code;
+};
+
+
+Blockly.Java['controls_flow_statements'] = function(block) {
+ // Flow statements: continue, break.
+ switch (block.getFieldValue('FLOW')) {
+ case 'BREAK':
+ return 'break;\n';
+ case 'CONTINUE':
+ return 'continue;\n';
+ }
+ throw 'Unknown flow statement.';
+};
diff --git a/generators/java/math.js b/generators/java/math.js
new file mode 100644
index 00000000000..25de2622bb6
--- /dev/null
+++ b/generators/java/math.js
@@ -0,0 +1,384 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for math blocks.
+ * @author q.neutron@gmail.com (Quynh Neutron)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.math');
+
+goog.require('Blockly.Java');
+
+
+// If any new block imports any library, add that library name here.
+Blockly.Java.addReservedWords('math,random');
+
+Blockly.Java['math_number'] = function(block) {
+ // Numeric value.
+ var code = parseFloat(block.getFieldValue('NUM'));
+ var order = code < 0 ? Blockly.Java.ORDER_UNARY_SIGN :
+ Blockly.Java.ORDER_ATOMIC;
+ return [code, order];
+};
+
+Blockly.Java['math_arithmetic'] = function(block) {
+ // Basic arithmetic operators, and power.
+ var OPERATORS = {
+ 'ADD': [' + ', Blockly.Java.ORDER_ADDITIVE],
+ 'MINUS': [' - ', Blockly.Java.ORDER_ADDITIVE],
+ 'MULTIPLY': [' * ', Blockly.Java.ORDER_MULTIPLICATIVE],
+ 'DIVIDE': [' / ', Blockly.Java.ORDER_MULTIPLICATIVE],
+ 'POWER': [' ** ', Blockly.Java.ORDER_EXPONENTIATION]
+ };
+ var tuple = OPERATORS[block.getFieldValue('OP')];
+ var operator = tuple[0];
+ var order = tuple[1];
+ var argument0 = Blockly.Java.valueToCode(block, 'A', order) || '0';
+ var argument1 = Blockly.Java.valueToCode(block, 'B', order) || '0';
+ var code = argument0 + operator + argument1;
+ return [code, order];
+ // In case of 'DIVIDE', division between integers returns different results
+ // in Java 2 and 3. However, is not an issue since Blockly does not
+ // guarantee identical results in all languages. To do otherwise would
+ // require every operator to be wrapped in a function call. This would kill
+ // legibility of the generated code.
+};
+
+Blockly.Java['math_single'] = function(block) {
+ // Math operators with single operand.
+ var operator = block.getFieldValue('OP');
+ var code;
+ var arg;
+ if (operator == 'NEG') {
+ // Negation is a special case given its different operator precedence.
+ var code = Blockly.Java.valueToCode(block, 'NUM',
+ Blockly.Java.ORDER_UNARY_SIGN) || '0';
+ return ['-' + code, Blockly.Java.ORDER_UNARY_SIGN];
+ }
+ Blockly.Java.definitions_['import_math'] = 'import math';
+ if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') {
+ arg = Blockly.Java.valueToCode(block, 'NUM',
+ Blockly.Java.ORDER_MULTIPLICATIVE) || '0';
+ } else {
+ arg = Blockly.Java.valueToCode(block, 'NUM',
+ Blockly.Java.ORDER_NONE) || '0';
+ }
+ // First, handle cases which generate values that don't need parentheses
+ // wrapping the code.
+ switch (operator) {
+ case 'ABS':
+ code = 'Math.fabs(' + arg + ')';
+ break;
+ case 'ROOT':
+ code = 'Math.sqrt(' + arg + ')';
+ break;
+ case 'LN':
+ code = 'Math.log(' + arg + ')';
+ break;
+ case 'LOG10':
+ code = 'Math.log10(' + arg + ')';
+ break;
+ case 'EXP':
+ code = 'Math.exp(' + arg + ')';
+ break;
+ case 'POW10':
+ code = 'Math.pow(10,' + arg + ')';
+ break;
+ case 'ROUND':
+ code = 'round(' + arg + ')';
+ break;
+ case 'ROUNDUP':
+ code = 'Math.ceil(' + arg + ')';
+ break;
+ case 'ROUNDDOWN':
+ code = 'Math.floor(' + arg + ')';
+ break;
+ case 'SIN':
+ code = 'Math.sin(' + arg + ' / 180.0 * Math.pi)';
+ break;
+ case 'COS':
+ code = 'Math.cos(' + arg + ' / 180.0 * Math.pi)';
+ break;
+ case 'TAN':
+ code = 'Math.tan(' + arg + ' / 180.0 * Math.pi)';
+ break;
+ }
+ if (code) {
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ }
+ // Second, handle cases which generate values that may need parentheses
+ // wrapping the code.
+ switch (operator) {
+ case 'ASIN':
+ code = 'Math.asin(' + arg + ') / Math.pi * 180';
+ break;
+ case 'ACOS':
+ code = 'Math.acos(' + arg + ') / Math.pi * 180';
+ break;
+ case 'ATAN':
+ code = 'Math.atan(' + arg + ') / Math.pi * 180';
+ break;
+ default:
+ throw 'Unknown math operator: ' + operator;
+ }
+ return [code, Blockly.Java.ORDER_MULTIPLICATIVE];
+};
+
+Blockly.Java['math_constant'] = function(block) {
+ // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
+ var CONSTANTS = {
+ 'PI': ['Math.pi', Blockly.Java.ORDER_MEMBER],
+ 'E': ['Math.e', Blockly.Java.ORDER_MEMBER],
+ 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2',
+ Blockly.Java.ORDER_MULTIPLICATIVE],
+ 'SQRT2': ['Math.sqrt(2)', Blockly.Java.ORDER_MEMBER],
+ 'SQRT1_2': ['Math.sqrt(1.0 / 2)', Blockly.Java.ORDER_MEMBER],
+ 'INFINITY': ['float(\'inf\')', Blockly.Java.ORDER_ATOMIC]
+ };
+ var constant = block.getFieldValue('CONSTANT');
+ if (constant != 'INFINITY') {
+ Blockly.Java.definitions_['import_math'] = 'import math';
+ }
+ return CONSTANTS[constant];
+};
+
+Blockly.Java['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.
+ var number_to_check = Blockly.Java.valueToCode(block, 'NUMBER_TO_CHECK',
+ Blockly.Java.ORDER_MULTIPLICATIVE) || '0';
+ var dropdown_property = block.getFieldValue('PROPERTY');
+ var code;
+ if (dropdown_property == 'PRIME') {
+ Blockly.Java.definitions_['import_math'] = 'import math';
+ var functionName = Blockly.Java.provideFunction_(
+ 'math_isPrime',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(n):',
+ ' # https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
+ ' # If n is not a number but a string, try parsing it.',
+ ' if type(n) not in (int, float, long):',
+ ' try:',
+ ' n = float(n)',
+ ' except:',
+ ' return False',
+ ' if n == 2 or n == 3:',
+ ' return True',
+ ' # False if n is negative, is 1, or not whole,' +
+ ' or if n is divisible by 2 or 3.',
+ ' if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0:',
+ ' return False',
+ ' # Check all the numbers of form 6k +/- 1, up to sqrt(n).',
+ ' for x in range(6, int(Math.sqrt(n)) + 2, 6):',
+ ' if n % (x - 1) == 0 or n % (x + 1) == 0:',
+ ' return False',
+ ' return True']);
+ code = functionName + '(' + number_to_check + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ }
+ switch (dropdown_property) {
+ case 'EVEN':
+ code = number_to_check + ' % 2 == 0';
+ break;
+ case 'ODD':
+ code = number_to_check + ' % 2 == 1';
+ break;
+ case 'WHOLE':
+ code = number_to_check + ' % 1 == 0';
+ break;
+ case 'POSITIVE':
+ code = number_to_check + ' > 0';
+ break;
+ case 'NEGATIVE':
+ code = number_to_check + ' < 0';
+ break;
+ case 'DIVISIBLE_BY':
+ var divisor = Blockly.Java.valueToCode(block, 'DIVISOR',
+ Blockly.Java.ORDER_MULTIPLICATIVE);
+ // If 'divisor' is some code that evals to 0, Java will raise an error.
+ if (!divisor || divisor == '0') {
+ return ['False', Blockly.Java.ORDER_ATOMIC];
+ }
+ code = number_to_check + ' % ' + divisor + ' == 0';
+ break;
+ }
+ return [code, Blockly.Java.ORDER_RELATIONAL];
+};
+
+Blockly.Java['math_change'] = function(block) {
+ // Add to a variable in place.
+ var argument0 = Blockly.Java.valueToCode(block, 'DELTA',
+ Blockly.Java.ORDER_ADDITIVE) || '0';
+ var varName = Blockly.Java.variableDB_.getName(block.getFieldValue('VAR'),
+ Blockly.Variables.NAME_TYPE);
+ return varName + ' = (' + varName + ' if type(' + varName +
+ ') in (int, float, long) else 0) + ' + argument0 + '\n';
+};
+
+// Rounding functions have a single operand.
+Blockly.Java['math_round'] = Blockly.Java['math_single'];
+// Trigonometry functions have a single operand.
+Blockly.Java['math_trig'] = Blockly.Java['math_single'];
+
+Blockly.Java['math_on_list'] = function(block) {
+ // Math functions for lists.
+ var func = block.getFieldValue('OP');
+ var list = Blockly.Java.valueToCode(block, 'LIST',
+ Blockly.Java.ORDER_NONE) || '[]';
+ var code;
+ switch (func) {
+ case 'SUM':
+ code = 'sum(' + list + ')';
+ break;
+ case 'MIN':
+ code = 'min(' + list + ')';
+ break;
+ case 'MAX':
+ code = 'max(' + list + ')';
+ break;
+ case 'AVERAGE':
+ var functionName = Blockly.Java.provideFunction_(
+ 'math_mean',
+ // This operation excludes null and values that aren't int or float:',
+ // math_mean([null, null, "aString", 1, 9]) == 5.0.',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(myList):',
+ ' localList = [e for e in myList if type(e) in (int, float, long)]',
+ ' if not localList: return',
+ ' return float(sum(localList)) / len(localList)']);
+ code = functionName + '(' + list + ')';
+ break;
+ case 'MEDIAN':
+ var functionName = Blockly.Java.provideFunction_(
+ 'math_median',
+ // This operation excludes null values:
+ // math_median([null, null, 1, 3]) == 2.0.
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(myList):',
+ ' localList = sorted([e for e in myList ' +
+ 'if type(e) in (int, float, long)])',
+ ' if not localList: return',
+ ' if len(localList) % 2 == 0:',
+ ' return (localList[len(localList) / 2 - 1] + ' +
+ 'localList[len(localList) / 2]) / 2.0',
+ ' else:',
+ ' return localList[(len(localList) - 1) / 2]']);
+ code = functionName + '(' + list + ')';
+ break;
+ case 'MODE':
+ var functionName = Blockly.Java.provideFunction_(
+ 'math_modes',
+ // 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].
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(some_list):',
+ ' modes = []',
+ ' # Using a lists of [item, count] to keep count rather than dict',
+ ' # to avoid "unhashable" errors when the counted item is ' +
+ 'itself a list or dict.',
+ ' counts = []',
+ ' maxCount = 1',
+ ' for item in some_list:',
+ ' found = False',
+ ' for count in counts:',
+ ' if count[0] == item:',
+ ' count[1] += 1',
+ ' maxCount = max(maxCount, count[1])',
+ ' found = True',
+ ' if not found:',
+ ' counts.append([item, 1])',
+ ' for counted_item, item_count in counts:',
+ ' if item_count == maxCount:',
+ ' modes.append(counted_item)',
+ ' return modes']);
+ code = functionName + '(' + list + ')';
+ break;
+ case 'STD_DEV':
+ Blockly.Java.definitions_['import_math'] = 'import math';
+ var functionName = Blockly.Java.provideFunction_(
+ 'math_standard_deviation',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(numbers):',
+ ' n = len(numbers)',
+ ' if n == 0: return',
+ ' mean = float(sum(numbers)) / n',
+ ' variance = sum((x - mean) ** 2 for x in numbers) / n',
+ ' return Math.sqrt(variance)']);
+ code = functionName + '(' + list + ')';
+ break;
+ case 'RANDOM':
+ Blockly.Java.definitions_['import_random'] = 'import random';
+ code = 'random.choice(' + list + ')';
+ break;
+ default:
+ throw 'Unknown operator: ' + func;
+ }
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['math_modulo'] = function(block) {
+ // Remainder computation.
+ var argument0 = Blockly.Java.valueToCode(block, 'DIVIDEND',
+ Blockly.Java.ORDER_MULTIPLICATIVE) || '0';
+ var argument1 = Blockly.Java.valueToCode(block, 'DIVISOR',
+ Blockly.Java.ORDER_MULTIPLICATIVE) || '0';
+ var code = argument0 + ' % ' + argument1;
+ return [code, Blockly.Java.ORDER_MULTIPLICATIVE];
+};
+
+
+Blockly.Java['math_format_as_decimal'] = function(block) {
+ // Remainder computation.
+ var argument0 = Blockly.Java.valueToCode(block, 'NUM',
+ Blockly.Java.ORDER_MULTIPLICATIVE) || '0';
+ var argument1 = Blockly.Java.valueToCode(block, 'PLACES',
+ Blockly.Java.ORDER_MULTIPLICATIVE) || '0';
+ var leng = Array(++argument1).join('0');
+ var code = 'new DecimalFormat("#.'+leng+'").format('+argument0+')';
+ return [code, Blockly.Java.ORDER_MULTIPLICATIVE];
+};
+
+Blockly.Java['math_constrain'] = function(block) {
+ // Constrain a number between two limits.
+ var argument0 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || '0';
+ var argument1 = Blockly.Java.valueToCode(block, 'LOW',
+ Blockly.Java.ORDER_NONE) || '0';
+ var argument2 = Blockly.Java.valueToCode(block, 'HIGH',
+ Blockly.Java.ORDER_NONE) || 'float(\'inf\')';
+ var code = 'min(max(' + argument0 + ', ' + argument1 + '), ' +
+ argument2 + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['math_random_int'] = function(block) {
+ // Random integer between [X] and [Y].
+ Blockly.Java.definitions_['import_random'] = 'import random';
+ var argument0 = Blockly.Java.valueToCode(block, 'FROM',
+ Blockly.Java.ORDER_NONE) || '0';
+ var argument1 = Blockly.Java.valueToCode(block, 'TO',
+ Blockly.Java.ORDER_NONE) || '0';
+ var code = 'random.randint(' + argument0 + ', ' + argument1 + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['math_random_float'] = function(block) {
+ // Random fraction between 0 and 1.
+ Blockly.Java.definitions_['import_random'] = 'import random';
+ return ['random.random()', Blockly.Java.ORDER_FUNCTION_CALL];
+};
diff --git a/generators/java/procedures.js b/generators/java/procedures.js
new file mode 100644
index 00000000000..27795bb2f0f
--- /dev/null
+++ b/generators/java/procedures.js
@@ -0,0 +1,109 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for procedure blocks.
+ * @author fraser@google.com (Neil Fraser)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.procedures');
+
+goog.require('Blockly.Java');
+
+
+Blockly.Java['procedures_defreturn'] = function(block) {
+ // Define a procedure with a return value.
+ var funcName = Blockly.Java.variableDB_.getName(block.getFieldValue('NAME'),
+ Blockly.Procedures.NAME_TYPE);
+ var branch = Blockly.Java.statementToCode(block, 'STACK');
+ if (Blockly.Java.STATEMENT_PREFIX) {
+ branch = Blockly.Java.prefixLines(
+ Blockly.Java.STATEMENT_PREFIX.replace(/%1/g,
+ '\'' + block.id + '\''), Blockly.Java.INDENT) + branch;
+ }
+ if (Blockly.Java.INFINITE_LOOP_TRAP) {
+ branch = Blockly.Java.INFINITE_LOOP_TRAP.replace(/%1/g,
+ '"' + block.id + '"') + branch;
+ }
+ var returnValue = Blockly.Java.valueToCode(block, 'RETURN',
+ Blockly.Java.ORDER_NONE) || '';
+ if (returnValue) {
+ returnValue = ' return ' + returnValue + ';\n';
+ } else if (!branch) {
+ branch = Blockly.Java.PASS;
+ }
+ var args = [];
+ for (var x = 0; x < block.arguments_.length; x++) {
+ args[x] = Blockly.Java.variableDB_.getName(block.arguments_[x]['name'],
+ Blockly.Variables.NAME_TYPE);
+ }
+ var code = 'public ' + funcName + '(' + args.join(', ') + '){\n' +
+ branch + returnValue + "}";
+ code = Blockly.Java.scrub_(block, code);
+ Blockly.Java.definitions_[funcName] = code;
+ return null;
+};
+
+// Defining a procedure without a return value uses the same generator as
+// a procedure with a return value.
+Blockly.Java['procedures_defnoreturn'] =
+ Blockly.Java['procedures_defreturn'];
+
+Blockly.Java['procedures_callreturn'] = function(block) {
+ // Call a procedure with a return value.
+ var funcName = Blockly.Java.variableDB_.getName(block.getFieldValue('NAME'),
+ Blockly.Procedures.NAME_TYPE);
+ var args = [];
+ for (var x = 0; x < block.arguments_.length; x++) {
+ args[x] = Blockly.Java.valueToCode(block, 'ARG' + x,
+ Blockly.Java.ORDER_NONE) || 'null';
+ }
+ var code = funcName + '(' + args.join(', ') + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['procedures_callnoreturn'] = function(block) {
+ // Call a procedure with no return value.
+ var funcName = Blockly.Java.variableDB_.getName(block.getFieldValue('NAME'),
+ Blockly.Procedures.NAME_TYPE);
+ var args = [];
+ for (var x = 0; x < block.arguments_.length; x++) {
+ args[x] = Blockly.Java.valueToCode(block, 'ARG' + x,
+ Blockly.Java.ORDER_NONE) || 'null';
+ }
+ var code = funcName + '(' + args.join(', ') + ');\n';
+ return code;
+};
+
+Blockly.Java['procedures_ifreturn'] = function(block) {
+ // Conditionally return value from a procedure.
+ var condition = Blockly.Java.valueToCode(block, 'CONDITION',
+ Blockly.Java.ORDER_NONE) || 'False';
+ var code = 'if (' + condition + '){\n';
+ if (block.hasReturnValue_) {
+ var value = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || 'None';
+ code += ' return ' + value + ';\n}';
+ } else {
+ code += ' return;\n}';
+ }
+ return code;
+};
diff --git a/generators/java/text.js b/generators/java/text.js
new file mode 100644
index 00000000000..d289bcb5475
--- /dev/null
+++ b/generators/java/text.js
@@ -0,0 +1,304 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for text blocks.
+ * @author q.neutron@gmail.com (Quynh Neutron)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.texts');
+
+goog.require('Blockly.Java');
+
+
+Blockly.Java['text'] = function(block) {
+ // Text value.
+ var code = Blockly.Java.quote_(block.getFieldValue('TEXT'));
+ return [code, Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['text_join'] = function(block) {
+ // Create a string made up of any number of elements of any type.
+ //Should we allow joining by '-' or ',' or any other characters?
+ var code;
+ if (block.itemCount_ == 0) {
+ return ['\'\'', Blockly.Java.ORDER_ATOMIC];
+ } else if (block.itemCount_ == 1) {
+ var argument0 = Blockly.Java.valueToCode(block, 'ADD0',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ code = 'str(' + argument0 + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ } else if (block.itemCount_ == 2) {
+ var argument0 = Blockly.Java.valueToCode(block, 'ADD0',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ var argument1 = Blockly.Java.valueToCode(block, 'ADD1',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ var code = 'str(' + argument0 + ') + str(' + argument1 + ')';
+ return [code, Blockly.Java.ORDER_UNARY_SIGN];
+ } else {
+ var code = [];
+ for (var n = 0; n < block.itemCount_; n++) {
+ code[n] = Blockly.Java.valueToCode(block, 'ADD' + n,
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ }
+ var tempVar = Blockly.Java.variableDB_.getDistinctName('temp_value',
+ Blockly.Variables.NAME_TYPE);
+ code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' +
+ code.join(', ') + ']])';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ }
+};
+
+Blockly.Java['text_append'] = function(block) {
+ // Append to a variable in place.
+ var varName = Blockly.Java.variableDB_.getName(block.getFieldValue('VAR'),
+ Blockly.Variables.NAME_TYPE);
+ var argument0 = Blockly.Java.valueToCode(block, 'TEXT',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ return varName + ' = str(' + varName + ') + str(' + argument0 + ')\n';
+};
+
+Blockly.Java['text_length'] = function(block) {
+ // String length.
+ var argument0 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ return ['len(' + argument0 + ')', Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['text_isEmpty'] = function(block) {
+ // Is the string null?
+ var argument0 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ var code = 'not len(' + argument0 + ')';
+ return [code, Blockly.Java.ORDER_LOGICAL_NOT];
+};
+
+Blockly.Java['text_indexOf'] = function(block) {
+ // Search the text for a substring.
+ // Should we allow for non-case sensitive???
+ var operator = block.getFieldValue('END') == 'FIRST' ? 'find' : 'rfind';
+ var argument0 = Blockly.Java.valueToCode(block, 'FIND',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ var argument1 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ var code = argument1 + '.' + operator + '(' + argument0 + ') + 1';
+ return [code, Blockly.Java.ORDER_MEMBER];
+};
+
+Blockly.Java['text_charAt'] = function(block) {
+ // Get letter at index.
+ // Note: Until January 2013 this block did not have the WHERE input.
+ var where = block.getFieldValue('WHERE') || 'FROM_START';
+ var at = Blockly.Java.valueToCode(block, 'AT',
+ Blockly.Java.ORDER_UNARY_SIGN) || '1';
+ var text = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ switch (where) {
+ case 'FIRST':
+ var code = text + '[0]';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ case 'LAST':
+ var code = text + '[-1]';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ case 'FROM_START':
+ // Blockly uses one-based indicies.
+ if (Blockly.isNumber(at)) {
+ // If the index is a naked number, decrement it right now.
+ at = parseInt(at, 10) - 1;
+ } else {
+ // If the index is dynamic, decrement it in code.
+ at = 'int(' + at + ' - 1)';
+ }
+ var code = text + '[' + at + ']';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ case 'FROM_END':
+ var code = text + '[-' + at + ']';
+ return [code, Blockly.Java.ORDER_MEMBER];
+ case 'RANDOM':
+ Blockly.Java.definitions_['import_random'] = 'import random';
+ var functionName = Blockly.Java.provideFunction_(
+ 'text_random_letter',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(text):',
+ ' x = int(random.random() * len(text))',
+ ' return text[x];']);
+ code = functionName + '(' + text + ')';
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+ }
+ throw 'Unhandled option (text_charAt).';
+};
+
+Blockly.Java['text_getSubstring'] = function(block) {
+ // Get substring.
+ var text = Blockly.Java.valueToCode(block, 'STRING',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ var where1 = block.getFieldValue('WHERE1');
+ var where2 = block.getFieldValue('WHERE2');
+ var at1 = Blockly.Java.valueToCode(block, 'AT1',
+ Blockly.Java.ORDER_ADDITIVE) || '1';
+ var at2 = Blockly.Java.valueToCode(block, 'AT2',
+ Blockly.Java.ORDER_ADDITIVE) || '1';
+ if (where1 == 'FIRST' || (where1 == 'FROM_START' && at1 == '1')) {
+ at1 = '';
+ } else if (where1 == 'FROM_START') {
+ // Blockly uses one-based indicies.
+ if (Blockly.isNumber(at1)) {
+ // If the index is a naked number, decrement it right now.
+ at1 = parseInt(at1, 10) - 1;
+ } else {
+ // If the index is dynamic, decrement it in code.
+ at1 = 'int(' + at1 + ' - 1)';
+ }
+ } else if (where1 == 'FROM_END') {
+ if (Blockly.isNumber(at1)) {
+ at1 = -parseInt(at1, 10);
+ } else {
+ at1 = '-int(' + at1 + ')';
+ }
+ }
+ if (where2 == 'LAST' || (where2 == 'FROM_END' && at2 == '1')) {
+ at2 = '';
+ } else if (where1 == 'FROM_START') {
+ if (Blockly.isNumber(at2)) {
+ at2 = parseInt(at2, 10);
+ } else {
+ at2 = 'int(' + at2 + ')';
+ }
+ } else if (where1 == 'FROM_END') {
+ if (Blockly.isNumber(at2)) {
+ // If the index is a naked number, increment it right now.
+ at2 = 1 - parseInt(at2, 10);
+ if (at2 == 0) {
+ at2 = '';
+ }
+ } else {
+ // If the index is dynamic, increment it in code.
+ // Add special case for -0.
+ Blockly.Java.definitions_['import_sys'] = 'import sys';
+ at2 = 'int(1 - ' + at2 + ') or sys.maxsize';
+ }
+ }
+ var code = text + '[' + at1 + ' : ' + at2 + ']';
+ return [code, Blockly.Java.ORDER_MEMBER];
+};
+
+Blockly.Java['text_changeCase'] = function(block) {
+ // Change capitalization.
+ var OPERATORS = {
+ 'UPPERCASE': '.upper()',
+ 'LOWERCASE': '.lower()',
+ 'TITLECASE': '.title()'
+ };
+ var operator = OPERATORS[block.getFieldValue('CASE')];
+ var argument0 = Blockly.Java.valueToCode(block, 'TEXT',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ var code = argument0 + operator;
+ return [code, Blockly.Java.ORDER_MEMBER];
+};
+
+Blockly.Java['text_trim'] = function(block) {
+ // Trim spaces.
+ var OPERATORS = {
+ 'LEFT': '.lstrip()',
+ 'RIGHT': '.rstrip()',
+ 'BOTH': '.strip()'
+ };
+ var operator = OPERATORS[block.getFieldValue('MODE')];
+ var argument0 = Blockly.Java.valueToCode(block, 'TEXT',
+ Blockly.Java.ORDER_MEMBER) || '\'\'';
+ var code = argument0 + operator;
+ return [code, Blockly.Java.ORDER_MEMBER];
+};
+
+Blockly.Java['text_print'] = function(block) {
+ // Print statement.
+ var argument0 = Blockly.Java.valueToCode(block, 'TEXT',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ return 'System.out.println(' + argument0 + '.toString());\n';
+};
+
+Blockly.Java['text_prompt'] = function(block) {
+ // Prompt function (internal message).
+ var functionName = Blockly.Java.provideFunction_(
+ 'text_prompt',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(msg):',
+ ' try:',
+ ' return raw_input(msg)',
+ ' except NameError:',
+ ' return input(msg)']);
+ var msg = Blockly.Java.quote_(block.getFieldValue('TEXT'));
+ var code = functionName + '(' + msg + ')';
+ var toNumber = block.getFieldValue('TYPE') == 'NUMBER';
+ if (toNumber) {
+ code = 'float(' + code + ')';
+ }
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+Blockly.Java['text_prompt_ext'] = function(block) {
+ // Prompt function (external message).
+ var functionName = Blockly.Java.provideFunction_(
+ 'text_prompt',
+ ['def ' + Blockly.Java.FUNCTION_NAME_PLACEHOLDER_ + '(msg):',
+ ' try:',
+ ' return raw_input(msg)',
+ ' except NameError:',
+ ' return input(msg)']);
+ var msg = Blockly.Java.valueToCode(block, 'TEXT',
+ Blockly.Java.ORDER_NONE) || '\'\'';
+ var code = functionName + '(' + msg + ')';
+ var toNumber = block.getFieldValue('TYPE') == 'NUMBER';
+ if (toNumber) {
+ code = 'float(' + code + ')';
+ }
+ return [code, Blockly.Java.ORDER_FUNCTION_CALL];
+};
+
+
+Blockly.Java['text_comment'] = function(block) {
+ // Display comment
+
+ var comment = block.getFieldValue('COMMENT') || '';
+ var code = '/*\n' + comment + '\n*/\n';
+
+ return code;
+};
+
+
+Blockly.Java['text_code_insert'] = function(block) {
+ // Display code
+ var type = block.getFieldValue('TYPE') || '';
+ var code = '';
+ if (type == 'Java')
+ {
+ var comment = block.getFieldValue('CODE') || '';
+ code = '//Arbitrary Java code insert block';
+ if (comment != '')
+ {
+ code += '\n';
+ code += comment +'\n';
+ }
+ else
+ {
+ code += ' is empty\n';
+ }
+ }
+ return code;
+};
diff --git a/generators/java/variables.js b/generators/java/variables.js
new file mode 100644
index 00000000000..edef9f71ed4
--- /dev/null
+++ b/generators/java/variables.js
@@ -0,0 +1,119 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2012 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Java for variable blocks.
+ * @author q.neutron@gmail.com (Quynh Neutron)
+ */
+'use strict';
+
+goog.provide('Blockly.Java.variables');
+
+goog.require('Blockly.Java');
+
+
+Blockly.Java['variables_get'] = function(block) {
+ // Variable getter.
+ var code = Blockly.Java.variableDB_.getName(block.getFieldValue('VAR'),
+ Blockly.Variables.NAME_TYPE);
+ return [code, Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['variables_set'] = function(block) {
+ // Variable setter.
+ var argument0 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || '0';
+ var varName = Blockly.Java.variableDB_.getName(block.getFieldValue('VAR'),
+ Blockly.Variables.NAME_TYPE);
+ return varName + ' = ' + argument0 + ';\n';
+};
+
+Blockly.Java['hash_variables_get'] = function(block) {
+ // Variable getter.
+ var getter = 'getString';
+ var parent = block.getParent();
+ // Look at our parents to see if we know the type that we are assigning to
+ if (parent) {
+ var func = parent.getVars;
+ if (func) {
+ var blockVariables = func.call(parent);
+ for (var y = 0; y < blockVariables.length; y++) {
+ var varName = blockVariables[y];
+ // Variable name may be null if the block is only half-built.
+ if (varName) {
+ var vartype = Blockly.Java.GetVariableType(varName);
+
+ if (vartype === 'JsonArray') {
+ getter = 'getJsonArray';
+ } else if (vartype === 'JsonObject') {
+ getter = 'getJsonObject';
+ }
+ }
+ }
+ }
+ }
+ var code = Blockly.Java.variableDB_.getName(block.getFieldValue('VAR'),
+ Blockly.Variables.NAME_TYPE) + '.' + getter + '('+
+ Blockly.Java.quote_(block.getFieldValue('HASHKEY')) + ')' ;
+ return [code, Blockly.Java.ORDER_ATOMIC];
+};
+
+Blockly.Java['hash_parmvariables_get'] = function(block) {
+ // Variable getter.
+ var getter = 'getString';
+ var parent = block.getParent();
+ // Look at our parents to see if we know the type that we are assigning to
+ if (parent) {
+ var func = parent.getVars;
+ if (func) {
+ var blockVariables = func.call(parent);
+ for (var y = 0; y < blockVariables.length; y++) {
+ var varName = blockVariables[y];
+ // Variable name may be null if the block is only half-built.
+ if (varName) {
+ var vartype = Blockly.Java.GetVariableType(varName);
+
+ if (vartype === 'JsonArray') {
+ getter = 'getJsonArray';
+ } else if (vartype === 'JsonObject') {
+ getter = 'getJsonObject';
+ }
+ }
+ }
+ } else {
+ }
+ }
+ var argument0 = Blockly.Java.valueToCode(block, 'VAR',
+ Blockly.Java.ORDER_NONE) || '0';
+ var code = argument0 + '.' + getter + '('+
+ Blockly.Java.quote_(block.getFieldValue('HASHKEY')) + ')' ;
+ return [code, Blockly.Java.ORDER_ATOMIC];
+};
+
+
+Blockly.Java['hash_variables_set'] = function(block) {
+ // Variable setter.
+ var argument0 = Blockly.Java.valueToCode(block, 'VALUE',
+ Blockly.Java.ORDER_NONE) || '0';
+ var varName = Blockly.Java.variableDB_.getName(block.getFieldValue('VAR'),
+ Blockly.Variables.NAME_TYPE);
+ return varName + '{' + block.getFieldValue('HASHKEY') + '}' +
+ ' = ' + argument0 + ';\n';
+};
diff --git a/java_compressed.js b/java_compressed.js
new file mode 100644
index 00000000000..abde74bae3b
--- /dev/null
+++ b/java_compressed.js
@@ -0,0 +1,98 @@
+// Do not edit this file; automatically generated by build.py.
+"use strict";
+
+
+// Copyright 2012 Google Inc. Apache License 2.0
+Blockly.Java=new Blockly.Generator("Java");Blockly.Java.addReservedWords("abstract,assert,boolean,break,case,catch,class,const,continue,default,do,double,else,enum,extends,final,finally,float,for,goto,if,implements,import,instanceof,int,interface,long,native,new,package,private,protected,public,return,short,static,strictfp,super,switch,synchronized,this,throw,throws,transient,try,void,volatile,while,false,null,true,abs,divmod,input,open,staticmethod,all,enumerate,int,ord,str,any,eval,isinstance,pow,sum,basestring,execfile,issubclass,print,super,bin,file,iter,property,tuple,bool,filter,len,range,type,bytearray,float,list,raw_input,unichr,callable,format,locals,reduce,unicode,chr,frozenset,long,reload,vars,classmethod,getattr,map,repr,xrange,cmp,globals,max,reversed,zip,compile,hasattr,memoryview,round,__import__,complex,hash,min,set,apply,delattr,help,next,setattr,buffer,dict,hex,object,slice,coerce,dir,id,oct,sorted,intern");
+Blockly.Java.ORDER_ATOMIC=0;Blockly.Java.ORDER_COLLECTION=1;Blockly.Java.ORDER_STRING_CONVERSION=1;Blockly.Java.ORDER_MEMBER=2;Blockly.Java.ORDER_FUNCTION_CALL=2;Blockly.Java.ORDER_POSTFIX=3;Blockly.Java.ORDER_EXPONENTIATION=3;Blockly.Java.ORDER_LOGICAL_NOT=3;Blockly.Java.ORDER_UNARY_SIGN=4;Blockly.Java.ORDER_MULTIPLICATIVE=5;Blockly.Java.ORDER_ADDITIVE=6;Blockly.Java.ORDER_BITWISE_SHIFT=7;Blockly.Java.ORDER_RELATIONAL=8;Blockly.Java.ORDER_EQUALITY=9;Blockly.Java.ORDER_BITWISE_AND=10;
+Blockly.Java.ORDER_BITWISE_XOR=11;Blockly.Java.ORDER_BITWISE_OR=12;Blockly.Java.ORDER_LOGICAL_AND=13;Blockly.Java.ORDER_LOGICAL_OR=14;Blockly.Java.ORDER_CONDITIONAL=15;Blockly.Java.ORDER_ASSIGNMENT=16;Blockly.Java.ORDER_NONE=99;Blockly.Java.PASS=" {}\n";Blockly.Java.POSTFIX="";Blockly.Java.EXTRAINDENT="";Blockly.Java.VariableTypes_={};Blockly.Java.AppName_="MyApp";Blockly.Java.needImports_="javax.json.Json javax.json.JsonArray javax.json.JsonObject javax.json.JsonReader javax.json.stream.JsonParsingException java.io.IOException java.io.StringReader".split(" ");
+Blockly.Java.SetAppName=function(a){a&&""!==a||(a="MyApp");this.AppName_=a;console.log(this.AppName_+" --- <"+a+">")};Blockly.Java.GetAppName=function(){return this.AppName_};Blockly.Java.GetVariableType=function(a){(a=Blockly.Java.VariableTypes_[a])||(a="string/*UNKNOWN_TYPE*/");return a};Blockly.Java.addImport=function(a){a="import "+a+";";Blockly.Java.imports_[a]=a};
+Blockly.Java.getImports=function(a){if(a)for(var b=0;b",GTE:">="}[a.getFieldValue("OP")],c=Blockly.Java.ORDER_RELATIONAL,d=Blockly.Java.valueToCode(a,"A",c)||"0";a=Blockly.Java.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]};
+Blockly.Java.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?" && ":" || ",c="and"==b?Blockly.Java.ORDER_LOGICAL_AND:Blockly.Java.ORDER_LOGICAL_OR,d=Blockly.Java.valueToCode(a,"A",c);a=Blockly.Java.valueToCode(a,"B",c);if(d||a){var e=" && "==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+b+a,c]};Blockly.Java.logic_negate=function(a){return["!("+(Blockly.Java.valueToCode(a,"BOOL",Blockly.Java.ORDER_LOGICAL_NOT)||"true")+")",Blockly.Java.ORDER_LOGICAL_NOT]};
+Blockly.Java.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"true":"false",Blockly.Java.ORDER_ATOMIC]};Blockly.Java.logic_null=function(a){return["null",Blockly.Java.ORDER_ATOMIC]};Blockly.Java.logic_ternary=function(a){var b=Blockly.Java.valueToCode(a,"IF",Blockly.Java.ORDER_CONDITIONAL)||"false",c=Blockly.Java.valueToCode(a,"THEN",Blockly.Java.ORDER_CONDITIONAL)||"null";a=Blockly.Java.valueToCode(a,"ELSE",Blockly.Java.ORDER_CONDITIONAL)||"null";return[b+" ? "+c+" : "+a,Blockly.Java.ORDER_CONDITIONAL]};
+// Copyright 2012 Google Inc. Apache License 2.0
+Blockly.Java.loops={};Blockly.Java.controls_repeat=function(a){var b=parseInt(a.getFieldValue("TIMES"),10),c=Blockly.Java.statementToCode(a,"DO"),c=Blockly.Java.addLoopTrap(c,a.id)||Blockly.Java.PASS;a=Blockly.Java.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE);return"for (int "+a+"=0; "+a+" < "+b+";"+a+"++) {\n"+c+"} // end for\n"};
+Blockly.Java.controls_repeat_ext=function(a){var b=Blockly.Java.valueToCode(a,"TIMES",Blockly.Java.ORDER_NONE)||"0",b=Blockly.isNumber(b)?parseInt(b,10):"int("+b+")",c=Blockly.Java.statementToCode(a,"DO"),c=Blockly.Java.addLoopTrap(c,a.id)||Blockly.Java.PASS;a=Blockly.Java.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE);return"for (int "+a+"=0; "+a+" < "+b+";"+a+"++) {\n"+c+"} // end for\n"};
+Blockly.Java.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.Java.valueToCode(a,"BOOL",b?Blockly.Java.ORDER_LOGICAL_NOT:Blockly.Java.ORDER_NONE)||"false",d=Blockly.Java.statementToCode(a,"DO"),d=Blockly.Java.addLoopTrap(d,a.id)||Blockly.Java.PASS;b&&(c="!"+c);return"while ("+c+") {\n"+d+"} // end while\n"};
+Blockly.Java.controls_for=function(a){var b=Blockly.Java.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);Blockly.Java.GetVariableType(a.getFieldValue("VAR"));var c=Blockly.Java.valueToCode(a,"FROM",Blockly.Java.ORDER_NONE)||"0",d=Blockly.Java.valueToCode(a,"TO",Blockly.Java.ORDER_NONE)||"0",e=Blockly.Java.valueToCode(a,"BY",Blockly.Java.ORDER_NONE)||"1",g=Blockly.Java.statementToCode(a,"DO"),g=Blockly.Java.addLoopTrap(g,a.id)||Blockly.Java.PASS;a="";if(Blockly.isNumber(c)&&
+Blockly.isNumber(d)&&Blockly.isNumber(e)){var c=parseFloat(c),d=parseFloat(d),e=Math.abs(parseFloat(e)),h="<=",f="++";c>d&&(h=">=",e=-e);0>e?f=" -= "+Math.abs(e):1!=e&&(f=" += "+e);a+="for ("+b+" = "+c+"; "+b+h+d+"; "+b+f+")"}else a+="for ("+b+" = "+c+"; "+b+"<="+d+"; "+b+" += "+e+")";return a+(" {\n"+g+"} // end for\n")};
+Blockly.Java.controls_forEach=function(a){var b=Blockly.Java.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.Java.GetVariableType(a.getFieldValue("VAR")),d=Blockly.Java.valueToCode(a,"LIST",Blockly.Java.ORDER_RELATIONAL)||"[]",e=Blockly.Java.statementToCode(a,"DO"),e=Blockly.Java.addLoopTrap(e,a.id)||Blockly.Java.PASS;return"for ("+c+" "+b+" :"+d+") {\n"+e+"} // end for\n"};
+Blockly.Java.controls_flow_statements=function(a){switch(a.getFieldValue("FLOW")){case "BREAK":return"break;\n";case "CONTINUE":return"continue;\n"}throw"Unknown flow statement.";};
+// Copyright 2012 Google Inc. Apache License 2.0
+Blockly.Java.math={};Blockly.Java.addReservedWords("math,random");Blockly.Java.math_number=function(a){a=parseFloat(a.getFieldValue("NUM"));return[a,0>a?Blockly.Java.ORDER_UNARY_SIGN:Blockly.Java.ORDER_ATOMIC]};
+Blockly.Java.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.Java.ORDER_ADDITIVE],MINUS:[" - ",Blockly.Java.ORDER_ADDITIVE],MULTIPLY:[" * ",Blockly.Java.ORDER_MULTIPLICATIVE],DIVIDE:[" / ",Blockly.Java.ORDER_MULTIPLICATIVE],POWER:[" ** ",Blockly.Java.ORDER_EXPONENTIATION]}[a.getFieldValue("OP")],c=b[0],b=b[1],d=Blockly.Java.valueToCode(a,"A",b)||"0";a=Blockly.Java.valueToCode(a,"B",b)||"0";return[d+c+a,b]};
+Blockly.Java.math_single=function(a){var b=a.getFieldValue("OP"),c;if("NEG"==b)return c=Blockly.Java.valueToCode(a,"NUM",Blockly.Java.ORDER_UNARY_SIGN)||"0",["-"+c,Blockly.Java.ORDER_UNARY_SIGN];Blockly.Java.definitions_.import_math="import math";a="SIN"==b||"COS"==b||"TAN"==b?Blockly.Java.valueToCode(a,"NUM",Blockly.Java.ORDER_MULTIPLICATIVE)||"0":Blockly.Java.valueToCode(a,"NUM",Blockly.Java.ORDER_NONE)||"0";switch(b){case "ABS":c="Math.fabs("+a+")";break;case "ROOT":c="Math.sqrt("+a+")";break;
+case "LN":c="Math.log("+a+")";break;case "LOG10":c="Math.log10("+a+")";break;case "EXP":c="Math.exp("+a+")";break;case "POW10":c="Math.pow(10,"+a+")";break;case "ROUND":c="round("+a+")";break;case "ROUNDUP":c="Math.ceil("+a+")";break;case "ROUNDDOWN":c="Math.floor("+a+")";break;case "SIN":c="Math.sin("+a+" / 180.0 * Math.pi)";break;case "COS":c="Math.cos("+a+" / 180.0 * Math.pi)";break;case "TAN":c="Math.tan("+a+" / 180.0 * Math.pi)"}if(c)return[c,Blockly.Java.ORDER_FUNCTION_CALL];switch(b){case "ASIN":c=
+"Math.asin("+a+") / Math.pi * 180";break;case "ACOS":c="Math.acos("+a+") / Math.pi * 180";break;case "ATAN":c="Math.atan("+a+") / Math.pi * 180";break;default:throw"Unknown math operator: "+b;}return[c,Blockly.Java.ORDER_MULTIPLICATIVE]};
+Blockly.Java.math_constant=function(a){var b={PI:["Math.pi",Blockly.Java.ORDER_MEMBER],E:["Math.e",Blockly.Java.ORDER_MEMBER],GOLDEN_RATIO:["(1 + Math.sqrt(5)) / 2",Blockly.Java.ORDER_MULTIPLICATIVE],SQRT2:["Math.sqrt(2)",Blockly.Java.ORDER_MEMBER],SQRT1_2:["Math.sqrt(1.0 / 2)",Blockly.Java.ORDER_MEMBER],INFINITY:["float('inf')",Blockly.Java.ORDER_ATOMIC]};a=a.getFieldValue("CONSTANT");"INFINITY"!=a&&(Blockly.Java.definitions_.import_math="import math");return b[a]};
+Blockly.Java.math_number_property=function(a){var b=Blockly.Java.valueToCode(a,"NUMBER_TO_CHECK",Blockly.Java.ORDER_MULTIPLICATIVE)||"0",c=a.getFieldValue("PROPERTY"),d;if("PRIME"==c)return Blockly.Java.definitions_.import_math="import math",d=Blockly.Java.provideFunction_("math_isPrime",["def "+Blockly.Java.FUNCTION_NAME_PLACEHOLDER_+"(n):"," # https://en.wikipedia.org/wiki/Primality_test#Naive_methods"," # If n is not a number but a string, try parsing it."," if type(n) not in (int, float, long):",
+" try:"," n = float(n)"," except:"," return False"," if n == 2 or n == 3:"," return True"," # False if n is negative, is 1, or not whole, or if n is divisible by 2 or 3."," if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0:"," return False"," # Check all the numbers of form 6k +/- 1, up to sqrt(n)."," for x in range(6, int(Math.sqrt(n)) + 2, 6):"," if n % (x - 1) == 0 or n % (x + 1) == 0:"," return False"," return True"])+"("+b+")",[d,Blockly.Java.ORDER_FUNCTION_CALL];
+switch(c){case "EVEN":d=b+" % 2 == 0";break;case "ODD":d=b+" % 2 == 1";break;case "WHOLE":d=b+" % 1 == 0";break;case "POSITIVE":d=b+" > 0";break;case "NEGATIVE":d=b+" < 0";break;case "DIVISIBLE_BY":a=Blockly.Java.valueToCode(a,"DIVISOR",Blockly.Java.ORDER_MULTIPLICATIVE);if(!a||"0"==a)return["False",Blockly.Java.ORDER_ATOMIC];d=b+" % "+a+" == 0"}return[d,Blockly.Java.ORDER_RELATIONAL]};
+Blockly.Java.math_change=function(a){var b=Blockly.Java.valueToCode(a,"DELTA",Blockly.Java.ORDER_ADDITIVE)||"0";a=Blockly.Java.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);return a+" = ("+a+" if type("+a+") in (int, float, long) else 0) + "+b+"\n"};Blockly.Java.math_round=Blockly.Java.math_single;Blockly.Java.math_trig=Blockly.Java.math_single;
+Blockly.Java.math_on_list=function(a){var b=a.getFieldValue("OP");a=Blockly.Java.valueToCode(a,"LIST",Blockly.Java.ORDER_NONE)||"[]";switch(b){case "SUM":b="sum("+a+")";break;case "MIN":b="min("+a+")";break;case "MAX":b="max("+a+")";break;case "AVERAGE":b=Blockly.Java.provideFunction_("math_mean",["def "+Blockly.Java.FUNCTION_NAME_PLACEHOLDER_+"(myList):"," localList = [e for e in myList if type(e) in (int, float, long)]"," if not localList: return"," return float(sum(localList)) / len(localList)"]);
+b=b+"("+a+")";break;case "MEDIAN":b=Blockly.Java.provideFunction_("math_median",["def "+Blockly.Java.FUNCTION_NAME_PLACEHOLDER_+"(myList):"," localList = sorted([e for e in myList if type(e) in (int, float, long)])"," if not localList: return"," if len(localList) % 2 == 0:"," return (localList[len(localList) / 2 - 1] + localList[len(localList) / 2]) / 2.0"," else:"," return localList[(len(localList) - 1) / 2]"]);b=b+"("+a+")";break;case "MODE":b=Blockly.Java.provideFunction_("math_modes",
+["def "+Blockly.Java.FUNCTION_NAME_PLACEHOLDER_+"(some_list):"," modes = []"," # Using a lists of [item, count] to keep count rather than dict",' # to avoid "unhashable" errors when the counted item is itself a list or dict.'," counts = []"," maxCount = 1"," for item in some_list:"," found = False"," for count in counts:"," if count[0] == item:"," count[1] += 1"," maxCount = max(maxCount, count[1])"," found = True"," if not found:"," counts.append([item, 1])",
+" for counted_item, item_count in counts:"," if item_count == maxCount:"," modes.append(counted_item)"," return modes"]);b=b+"("+a+")";break;case "STD_DEV":Blockly.Java.definitions_.import_math="import math";b=Blockly.Java.provideFunction_("math_standard_deviation",["def "+Blockly.Java.FUNCTION_NAME_PLACEHOLDER_+"(numbers):"," n = len(numbers)"," if n == 0: return"," mean = float(sum(numbers)) / n"," variance = sum((x - mean) ** 2 for x in numbers) / n"," return Math.sqrt(variance)"]);
+b=b+"("+a+")";break;case "RANDOM":Blockly.Java.definitions_.import_random="import random";b="random.choice("+a+")";break;default:throw"Unknown operator: "+b;}return[b,Blockly.Java.ORDER_FUNCTION_CALL]};Blockly.Java.math_modulo=function(a){var b=Blockly.Java.valueToCode(a,"DIVIDEND",Blockly.Java.ORDER_MULTIPLICATIVE)||"0";a=Blockly.Java.valueToCode(a,"DIVISOR",Blockly.Java.ORDER_MULTIPLICATIVE)||"0";return[b+" % "+a,Blockly.Java.ORDER_MULTIPLICATIVE]};
+Blockly.Java.math_format_as_decimal=function(a){var b=Blockly.Java.valueToCode(a,"NUM",Blockly.Java.ORDER_MULTIPLICATIVE)||"0";a=Blockly.Java.valueToCode(a,"PLACES",Blockly.Java.ORDER_MULTIPLICATIVE)||"0";return['new DecimalFormat("#.'+Array(++a).join("0")+'").format('+b+")",Blockly.Java.ORDER_MULTIPLICATIVE]};
+Blockly.Java.math_constrain=function(a){var b=Blockly.Java.valueToCode(a,"VALUE",Blockly.Java.ORDER_NONE)||"0",c=Blockly.Java.valueToCode(a,"LOW",Blockly.Java.ORDER_NONE)||"0";a=Blockly.Java.valueToCode(a,"HIGH",Blockly.Java.ORDER_NONE)||"float('inf')";return["min(max("+b+", "+c+"), "+a+")",Blockly.Java.ORDER_FUNCTION_CALL]};
+Blockly.Java.math_random_int=function(a){Blockly.Java.definitions_.import_random="import random";var b=Blockly.Java.valueToCode(a,"FROM",Blockly.Java.ORDER_NONE)||"0";a=Blockly.Java.valueToCode(a,"TO",Blockly.Java.ORDER_NONE)||"0";return["random.randint("+b+", "+a+")",Blockly.Java.ORDER_FUNCTION_CALL]};Blockly.Java.math_random_float=function(a){Blockly.Java.definitions_.import_random="import random";return["random.random()",Blockly.Java.ORDER_FUNCTION_CALL]};
+// Copyright 2012 Google Inc. Apache License 2.0
+Blockly.Java.procedures={};
+Blockly.Java.procedures_defreturn=function(a){var b=Blockly.Java.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE),c=Blockly.Java.statementToCode(a,"STACK");Blockly.Java.STATEMENT_PREFIX&&(c=Blockly.Java.prefixLines(Blockly.Java.STATEMENT_PREFIX.replace(/%1/g,"'"+a.id+"'"),Blockly.Java.INDENT)+c);Blockly.Java.INFINITE_LOOP_TRAP&&(c=Blockly.Java.INFINITE_LOOP_TRAP.replace(/%1/g,'"'+a.id+'"')+c);var d=Blockly.Java.valueToCode(a,"RETURN",Blockly.Java.ORDER_NONE)||"";d?d=" return "+
+d+";\n":c||(c=Blockly.Java.PASS);for(var e=[],g=0;g
Date: Tue, 30 Jun 2015 11:11:38 -0400
Subject: [PATCH 03/84] Initial Java implementation
Implementation of Java generators and Typeblock capability
---
blockly_compressed.js | 215 +++++++++++++++-------------
blockly_uncompressed.js | 106 ++++++++------
blocks_compressed.js | 2 +-
build.py | 1 +
core/connection.js | 6 +-
core/field_image.js | 9 +-
core/field_label.js | 1 +
core/generator.js | 75 ++++++++--
core/input.js | 27 +++-
core/names.js | 12 +-
core/procedures.js | 8 +-
core/toolbox.js | 9 --
core/variables.js | 72 ++++++++++
core/workspace_svg.js | 5 +-
dart_compressed.js | 8 +-
demos/code/code.js | 10 +-
demos/index.html | 2 +-
demos/maxBlocks/index.html | 3 +-
generators/dart/procedures.js | 2 +-
generators/dart/variables.js | 19 +++
generators/javascript/procedures.js | 2 +-
generators/javascript/variables.js | 19 +++
generators/python.js | 13 ++
generators/python/math.js | 10 ++
generators/python/procedures.js | 2 +-
generators/python/variables.js | 19 +++
javascript_compressed.js | 8 +-
python_compressed.js | 13 +-
tests/generators/index.html | 32 ++---
tests/generators/unittest.js | 28 ++--
30 files changed, 492 insertions(+), 246 deletions(-)
diff --git a/blockly_compressed.js b/blockly_compressed.js
index 486ea44504d..424cfe7841b 100644
--- a/blockly_compressed.js
+++ b/blockly_compressed.js
@@ -3,33 +3,34 @@
var COMPILED=!0,goog=goog||{};goog.global=this;goog.isDef=function(a){return void 0!==a};goog.exportPath_=function(a,b,c){a=a.split(".");c=c||goog.global;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)!a.length&&goog.isDef(b)?c[d]=b:c=c[d]?c[d]:c[d]={}};
goog.define=function(a,b){var c=b;COMPILED||(goog.global.CLOSURE_UNCOMPILED_DEFINES&&Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_UNCOMPILED_DEFINES,a)?c=goog.global.CLOSURE_UNCOMPILED_DEFINES[a]:goog.global.CLOSURE_DEFINES&&Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_DEFINES,a)&&(c=goog.global.CLOSURE_DEFINES[a]));goog.exportPath_(a,c)};goog.DEBUG=!0;goog.LOCALE="en";goog.TRUSTED_SITE=!0;goog.STRICT_MODE_COMPATIBLE=!1;goog.DISALLOW_TEST_ONLY_CODE=COMPILED&&!goog.DEBUG;
-goog.provide=function(a){if(!COMPILED&&goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');goog.constructNamespace_(a)};goog.constructNamespace_=function(a,b){if(!COMPILED){delete goog.implicitNamespaces_[a];for(var c=a;(c=c.substring(0,c.lastIndexOf(".")))&&!goog.getObjectByName(c);)goog.implicitNamespaces_[c]=!0}goog.exportPath_(a,b)};goog.VALID_MODULE_RE_=/^[a-zA-Z_$][a-zA-Z0-9._$]*$/;
+goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING=!1;goog.provide=function(a){if(!COMPILED&&goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');goog.constructNamespace_(a)};goog.constructNamespace_=function(a,b){if(!COMPILED){delete goog.implicitNamespaces_[a];for(var c=a;(c=c.substring(0,c.lastIndexOf(".")))&&!goog.getObjectByName(c);)goog.implicitNamespaces_[c]=!0}goog.exportPath_(a,b)};goog.VALID_MODULE_RE_=/^[a-zA-Z_$][a-zA-Z0-9._$]*$/;
goog.module=function(a){if(!goog.isString(a)||!a||-1==a.search(goog.VALID_MODULE_RE_))throw Error("Invalid module identifier");if(!goog.isInModuleLoader_())throw Error("Module "+a+" has been loaded incorrectly.");if(goog.moduleLoaderState_.moduleName)throw Error("goog.module may only be called once per module.");goog.moduleLoaderState_.moduleName=a;if(!COMPILED){if(goog.isProvided_(a))throw Error('Namespace "'+a+'" already declared.');delete goog.implicitNamespaces_[a]}};goog.module.get=function(a){return goog.module.getInternal_(a)};
goog.module.getInternal_=function(a){if(!COMPILED)return goog.isProvided_(a)?a in goog.loadedModules_?goog.loadedModules_[a]:goog.getObjectByName(a):null};goog.moduleLoaderState_=null;goog.isInModuleLoader_=function(){return null!=goog.moduleLoaderState_};goog.module.declareTestMethods=function(){if(!goog.isInModuleLoader_())throw Error("goog.module.declareTestMethods must be called from within a goog.module");goog.moduleLoaderState_.declareTestMethods=!0};
goog.module.declareLegacyNamespace=function(){if(!COMPILED&&!goog.isInModuleLoader_())throw Error("goog.module.declareLegacyNamespace must be called from within a goog.module");if(!COMPILED&&!goog.moduleLoaderState_.moduleName)throw Error("goog.module must be called prior to goog.module.declareLegacyNamespace.");goog.moduleLoaderState_.declareLegacyNamespace=!0};
goog.setTestOnly=function(a){if(goog.DISALLOW_TEST_ONLY_CODE)throw a=a||"",Error("Importing test-only code into non-debug environment"+(a?": "+a:"."));};goog.forwardDeclare=function(a){};COMPILED||(goog.isProvided_=function(a){return a in goog.loadedModules_||!goog.implicitNamespaces_[a]&&goog.isDefAndNotNull(goog.getObjectByName(a))},goog.implicitNamespaces_={"goog.module":!0});
goog.getObjectByName=function(a,b){for(var c=a.split("."),d=b||goog.global,e;e=c.shift();)if(goog.isDefAndNotNull(d[e]))d=d[e];else return null;return d};goog.globalize=function(a,b){var c=b||goog.global,d;for(d in a)c[d]=a[d]};goog.addDependency=function(a,b,c,d){if(goog.DEPENDENCIES_ENABLED){var e;a=a.replace(/\\/g,"/");for(var f=goog.dependencies_,g=0;e=b[g];g++)f.nameToPath[e]=a,f.pathIsModule[a]=!!d;for(d=0;b=c[d];d++)a in f.requires||(f.requires[a]={}),f.requires[a][b]=!0}};
goog.ENABLE_DEBUG_LOADER=!0;goog.logToConsole_=function(a){goog.global.console&&goog.global.console.error(a)};
-goog.require=function(a){if(!COMPILED){goog.ENABLE_DEBUG_LOADER&&goog.IS_OLD_IE_&&goog.maybeProcessDeferredDep_(a);if(goog.isProvided_(a))return goog.isInModuleLoader_()?goog.module.getInternal_(a):null;if(goog.ENABLE_DEBUG_LOADER){var b=goog.getPathFromDeps_(a);if(b)return goog.included_[b]=!0,goog.writeScripts_(),null}a="goog.require could not find: "+a;goog.logToConsole_(a);throw Error(a);}};goog.basePath="";goog.nullFunction=function(){};goog.identityFunction=function(a,b){return a};
+goog.require=function(a){if(!COMPILED){goog.ENABLE_DEBUG_LOADER&&goog.IS_OLD_IE_&&goog.maybeProcessDeferredDep_(a);if(goog.isProvided_(a))return goog.isInModuleLoader_()?goog.module.getInternal_(a):null;if(goog.ENABLE_DEBUG_LOADER){var b=goog.getPathFromDeps_(a);if(b)return goog.included_[b]=!0,goog.writeScripts_(),null}a="goog.require could not find: "+a;goog.logToConsole_(a);throw Error(a);}};goog.basePath="";goog.nullFunction=function(){};
goog.abstractMethod=function(){throw Error("unimplemented abstract method");};goog.addSingletonGetter=function(a){a.getInstance=function(){if(a.instance_)return a.instance_;goog.DEBUG&&(goog.instantiatedSingletons_[goog.instantiatedSingletons_.length]=a);return a.instance_=new a}};goog.instantiatedSingletons_=[];goog.LOAD_MODULE_USING_EVAL=!0;goog.SEAL_MODULE_EXPORTS=goog.DEBUG;goog.loadedModules_={};goog.DEPENDENCIES_ENABLED=!COMPILED&&goog.ENABLE_DEBUG_LOADER;
-goog.DEPENDENCIES_ENABLED&&(goog.included_={},goog.dependencies_={pathIsModule:{},nameToPath:{},requires:{},visited:{},written:{},deferred:{}},goog.inHtmlDocument_=function(){var a=goog.global.document;return"undefined"!=typeof a&&"write"in a},goog.findBasePath_=function(){if(goog.isDef(goog.global.CLOSURE_BASE_PATH))goog.basePath=goog.global.CLOSURE_BASE_PATH;else if(goog.inHtmlDocument_())for(var a=goog.global.document.getElementsByTagName("script"),b=a.length-1;0<=b;--b){var c=a[b].src,d=c.lastIndexOf("?"),
+goog.DEPENDENCIES_ENABLED&&(goog.included_={},goog.dependencies_={pathIsModule:{},nameToPath:{},requires:{},visited:{},written:{},deferred:{}},goog.inHtmlDocument_=function(){var a=goog.global.document;return"undefined"!=typeof a&&"write"in a},goog.findBasePath_=function(){if(goog.global.CLOSURE_BASE_PATH)goog.basePath=goog.global.CLOSURE_BASE_PATH;else if(goog.inHtmlDocument_())for(var a=goog.global.document.getElementsByTagName("SCRIPT"),b=a.length-1;0<=b;--b){var c=a[b].src,d=c.lastIndexOf("?"),
d=-1==d?c.length:d;if("base.js"==c.substr(d-7,7)){goog.basePath=c.substr(0,d-7);break}}},goog.importScript_=function(a,b){(goog.global.CLOSURE_IMPORT_SCRIPT||goog.writeScriptTag_)(a,b)&&(goog.dependencies_.written[a]=!0)},goog.IS_OLD_IE_=!goog.global.atob&&goog.global.document&&goog.global.document.all,goog.importModule_=function(a){goog.importScript_("",'goog.retrieveAndExecModule_("'+a+'");')&&(goog.dependencies_.written[a]=!0)},goog.queuedModules_=[],goog.wrapModule_=function(a,b){return goog.LOAD_MODULE_USING_EVAL&&
goog.isDef(goog.global.JSON)?"goog.loadModule("+goog.global.JSON.stringify(b+"\n//# sourceURL="+a+"\n")+");":'goog.loadModule(function(exports) {"use strict";'+b+"\n;return exports});\n//# sourceURL="+a+"\n"},goog.loadQueuedModules_=function(){var a=goog.queuedModules_.length;if(0\x3c/script>")):c.write('
-
-
-
-
-
-
-
-
-
-
-
-
@@ -46,6 +34,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -138,13 +136,13 @@
setOutput(code);
}
-function toPhp() {
- var code = Blockly.PHP.workspaceToCode(workspace);
+function toDart() {
+ var code = Blockly.Dart.workspaceToCode(workspace);
setOutput(code);
}
-function toDart() {
- var code = Blockly.Dart.workspaceToCode(workspace);
+function toJava() {
+ var code = Blockly.Java.workspaceToCode(workspace);
setOutput(code);
}
@@ -423,8 +421,8 @@ Blockly Generator Tests
-
+
|
diff --git a/tests/generators/unittest.js b/tests/generators/unittest.js
index 560e6b91fee..1bd917c945f 100644
--- a/tests/generators/unittest.js
+++ b/tests/generators/unittest.js
@@ -36,7 +36,18 @@ Blockly.Blocks['unittest_main'] = {
},
getVars: function() {
return ['unittestResults'];
- }
+ },
+ /**
+ * Return all types of variables referenced by this block.
+ * @return {!Array. |