+ |
|
-
-
+
+
|
@@ -138,7 +347,7 @@
|
- Language code:
+ Block Definition:
|
|
-
+
-
+
+
@@ -226,5 +436,305 @@
330
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ 10
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ 1
+
+
+
+
+
+
+ 9
+
+
+
+
+
+
+ 45
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+ 3.1
+
+
+
+
+
+
+
+ 64
+
+
+
+
+ 10
+
+
+
+
+
+
+ 50
+
+
+
+
+ 1
+
+
+
+
+ 100
+
+
+
+
+
+
+ 1
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ abc
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text
+
+
+
+
+ abc
+
+
+
+
+
+
+ text
+
+
+
+
+
+
+ text
+
+
+
+
+
+
+ abc
+
+
+
+
+
+
+ abc
+
+
+
+
+
+
+ abc
+
+
+
+
+
+
+ abc
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+
+
+
+
+ list
+
+
+
+
+
+
+ list
+
+
+
+
+
+
+ list
+
+
+
+
+
+
+ list
+
+
+
+
+
+
+ ,
+
+
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+ 50
+
+
+
+
+ 0
+
+
+
+
+
+
+ #ff0000
+
+
+
+
+ #3333ff
+
+
+
+
+ 0.5
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/blocklyfactory/standard_categories.js b/demos/blockfactory/standard_categories.js
similarity index 100%
rename from demos/blocklyfactory/standard_categories.js
rename to demos/blockfactory/standard_categories.js
diff --git a/demos/blocklyfactory/workspacefactory/index.html b/demos/blockfactory/workspacefactory/index.html
similarity index 100%
rename from demos/blocklyfactory/workspacefactory/index.html
rename to demos/blockfactory/workspacefactory/index.html
diff --git a/demos/blocklyfactory/workspacefactory/style.css b/demos/blockfactory/workspacefactory/style.css
similarity index 100%
rename from demos/blocklyfactory/workspacefactory/style.css
rename to demos/blockfactory/workspacefactory/style.css
diff --git a/demos/blocklyfactory/workspacefactory/wfactory_controller.js b/demos/blockfactory/workspacefactory/wfactory_controller.js
similarity index 100%
rename from demos/blocklyfactory/workspacefactory/wfactory_controller.js
rename to demos/blockfactory/workspacefactory/wfactory_controller.js
diff --git a/demos/blocklyfactory/workspacefactory/wfactory_generator.js b/demos/blockfactory/workspacefactory/wfactory_generator.js
similarity index 100%
rename from demos/blocklyfactory/workspacefactory/wfactory_generator.js
rename to demos/blockfactory/workspacefactory/wfactory_generator.js
diff --git a/demos/blocklyfactory/workspacefactory/wfactory_init.js b/demos/blockfactory/workspacefactory/wfactory_init.js
similarity index 100%
rename from demos/blocklyfactory/workspacefactory/wfactory_init.js
rename to demos/blockfactory/workspacefactory/wfactory_init.js
diff --git a/demos/blocklyfactory/workspacefactory/wfactory_model.js b/demos/blockfactory/workspacefactory/wfactory_model.js
similarity index 100%
rename from demos/blocklyfactory/workspacefactory/wfactory_model.js
rename to demos/blockfactory/workspacefactory/wfactory_model.js
diff --git a/demos/blocklyfactory/workspacefactory/wfactory_view.js b/demos/blockfactory/workspacefactory/wfactory_view.js
similarity index 100%
rename from demos/blocklyfactory/workspacefactory/wfactory_view.js
rename to demos/blockfactory/workspacefactory/wfactory_view.js
diff --git a/demos/blockfactory_old/blocks.js b/demos/blockfactory_old/blocks.js
new file mode 100644
index 000000000..856780a52
--- /dev/null
+++ b/demos/blockfactory_old/blocks.js
@@ -0,0 +1,826 @@
+/**
+ * Blockly Demos: Block Factory Blocks
+ *
+ * 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 Blocks for Blockly's Block Factory application.
+ * @author fraser@google.com (Neil Fraser)
+ */
+'use strict';
+
+Blockly.Blocks['factory_base'] = {
+ // Base of new block.
+ init: function() {
+ this.setColour(120);
+ this.appendDummyInput()
+ .appendField('name')
+ .appendField(new Blockly.FieldTextInput('block_type'), 'NAME');
+ this.appendStatementInput('INPUTS')
+ .setCheck('Input')
+ .appendField('inputs');
+ var dropdown = new Blockly.FieldDropdown([
+ ['automatic inputs', 'AUTO'],
+ ['external inputs', 'EXT'],
+ ['inline inputs', 'INT']]);
+ this.appendDummyInput()
+ .appendField(dropdown, 'INLINE');
+ dropdown = new Blockly.FieldDropdown([
+ ['no connections', 'NONE'],
+ ['← left output', 'LEFT'],
+ ['↕ top+bottom connections', 'BOTH'],
+ ['↑ top connection', 'TOP'],
+ ['↓ bottom connection', 'BOTTOM']],
+ function(option) {
+ this.sourceBlock_.updateShape_(option);
+ // Connect a shadow block to this new input.
+ this.sourceBlock_.spawnOutputShadow_(option);
+ });
+ this.appendDummyInput()
+ .appendField(dropdown, 'CONNECTIONS');
+ this.appendValueInput('COLOUR')
+ .setCheck('Colour')
+ .appendField('colour');
+ this.setTooltip('Build a custom block by plugging\n' +
+ 'fields, inputs and other blocks here.');
+ this.setHelpUrl(
+ 'https://developers.google.com/blockly/guides/create-custom-blocks/block-factory');
+ },
+ mutationToDom: function() {
+ var container = document.createElement('mutation');
+ container.setAttribute('connections', this.getFieldValue('CONNECTIONS'));
+ return container;
+ },
+ domToMutation: function(xmlElement) {
+ var connections = xmlElement.getAttribute('connections');
+ this.updateShape_(connections);
+ },
+ spawnOutputShadow_: function(option) {
+ // Helper method for deciding which type of outputs this block needs
+ // to attach shaddow blocks to.
+ switch (option) {
+ case 'LEFT':
+ this.connectOutputShadow_('OUTPUTTYPE');
+ break;
+ case 'TOP':
+ this.connectOutputShadow_('TOPTYPE');
+ break;
+ case 'BOTTOM':
+ this.connectOutputShadow_('BOTTOMTYPE');
+ break;
+ case 'BOTH':
+ this.connectOutputShadow_('TOPTYPE');
+ this.connectOutputShadow_('BOTTOMTYPE');
+ break;
+ }
+ },
+ connectOutputShadow_: function(outputType) {
+ // Helper method to create & connect shadow block.
+ var type = this.workspace.newBlock('type_null');
+ type.setShadow(true);
+ type.outputConnection.connect(this.getInput(outputType).connection);
+ type.initSvg();
+ type.render();
+ },
+ updateShape_: function(option) {
+ var outputExists = this.getInput('OUTPUTTYPE');
+ var topExists = this.getInput('TOPTYPE');
+ var bottomExists = this.getInput('BOTTOMTYPE');
+ if (option == 'LEFT') {
+ if (!outputExists) {
+ this.addTypeInput_('OUTPUTTYPE', 'output type');
+ }
+ } else if (outputExists) {
+ this.removeInput('OUTPUTTYPE');
+ }
+ if (option == 'TOP' || option == 'BOTH') {
+ if (!topExists) {
+ this.addTypeInput_('TOPTYPE', 'top type');
+ }
+ } else if (topExists) {
+ this.removeInput('TOPTYPE');
+ }
+ if (option == 'BOTTOM' || option == 'BOTH') {
+ if (!bottomExists) {
+ this.addTypeInput_('BOTTOMTYPE', 'bottom type');
+ }
+ } else if (bottomExists) {
+ this.removeInput('BOTTOMTYPE');
+ }
+ },
+ addTypeInput_: function(name, label) {
+ this.appendValueInput(name)
+ .setCheck('Type')
+ .appendField(label);
+ this.moveInputBefore(name, 'COLOUR');
+ }
+};
+
+var FIELD_MESSAGE = 'fields %1 %2';
+var FIELD_ARGS = [
+ {
+ "type": "field_dropdown",
+ "name": "ALIGN",
+ "options": [['left', 'LEFT'], ['right', 'RIGHT'], ['centre', 'CENTRE']],
+ },
+ {
+ "type": "input_statement",
+ "name": "FIELDS",
+ "check": "Field"
+ }
+];
+
+var TYPE_MESSAGE = 'type %1';
+var TYPE_ARGS = [
+ {
+ "type": "input_value",
+ "name": "TYPE",
+ "check": "Type",
+ "align": "RIGHT"
+ }
+];
+
+Blockly.Blocks['input_value'] = {
+ // Value input.
+ init: function() {
+ this.jsonInit({
+ "message0": "value input %1 %2",
+ "args0": [
+ {
+ "type": "field_input",
+ "name": "INPUTNAME",
+ "text": "NAME"
+ },
+ {
+ "type": "input_dummy"
+ }
+ ],
+ "message1": FIELD_MESSAGE,
+ "args1": FIELD_ARGS,
+ "message2": TYPE_MESSAGE,
+ "args2": TYPE_ARGS,
+ "previousStatement": "Input",
+ "nextStatement": "Input",
+ "colour": 210,
+ "tooltip": "A value socket for horizontal connections.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=71"
+ });
+ },
+ onchange: function() {
+ inputNameCheck(this);
+ }
+};
+
+Blockly.Blocks['input_statement'] = {
+ // Statement input.
+ init: function() {
+ this.jsonInit({
+ "message0": "statement input %1 %2",
+ "args0": [
+ {
+ "type": "field_input",
+ "name": "INPUTNAME",
+ "text": "NAME"
+ },
+ {
+ "type": "input_dummy"
+ },
+ ],
+ "message1": FIELD_MESSAGE,
+ "args1": FIELD_ARGS,
+ "message2": TYPE_MESSAGE,
+ "args2": TYPE_ARGS,
+ "previousStatement": "Input",
+ "nextStatement": "Input",
+ "colour": 210,
+ "tooltip": "A statement socket for enclosed vertical stacks.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=246"
+ });
+ },
+ onchange: function() {
+ inputNameCheck(this);
+ }
+};
+
+Blockly.Blocks['input_dummy'] = {
+ // Dummy input.
+ init: function() {
+ this.jsonInit({
+ "message0": "dummy input",
+ "message1": FIELD_MESSAGE,
+ "args1": FIELD_ARGS,
+ "previousStatement": "Input",
+ "nextStatement": "Input",
+ "colour": 210,
+ "tooltip": "For adding fields on a separate row with no " +
+ "connections. Alignment options (left, right, centre) " +
+ "apply only to multi-line fields.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=293"
+ });
+ }
+};
+
+Blockly.Blocks['field_static'] = {
+ // Text value.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('text')
+ .appendField(new Blockly.FieldTextInput(''), 'TEXT');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('Static text that serves as a label.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=88');
+ }
+};
+
+Blockly.Blocks['field_input'] = {
+ // Text input.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('text input')
+ .appendField(new Blockly.FieldTextInput('default'), 'TEXT')
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('An input field for the user to enter text.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
+ },
+ onchange: function() {
+ fieldNameCheck(this);
+ }
+};
+
+Blockly.Blocks['field_number'] = {
+ // Numeric input.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('numeric input')
+ .appendField(new Blockly.FieldNumber(0), 'VALUE')
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.appendDummyInput()
+ .appendField('min')
+ .appendField(new Blockly.FieldNumber(-Infinity), 'MIN')
+ .appendField('max')
+ .appendField(new Blockly.FieldNumber(Infinity), 'MAX')
+ .appendField('precision')
+ .appendField(new Blockly.FieldNumber(0, 0), 'PRECISION');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('An input field for the user to enter a number.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
+ },
+ onchange: function() {
+ fieldNameCheck(this);
+ }
+};
+
+Blockly.Blocks['field_angle'] = {
+ // Angle input.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('angle input')
+ .appendField(new Blockly.FieldAngle('90'), 'ANGLE')
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('An input field for the user to enter an angle.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=372');
+ },
+ onchange: function() {
+ fieldNameCheck(this);
+ }
+};
+
+Blockly.Blocks['field_dropdown'] = {
+ // Dropdown menu.
+ init: function() {
+ this.appendDummyInput()
+ .appendField('dropdown')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.optionCount_ = 3;
+ this.updateShape_();
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setMutator(new Blockly.Mutator(['field_dropdown_option']));
+ this.setColour(160);
+ this.setTooltip('Dropdown menu with a list of options.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
+ },
+ mutationToDom: function(workspace) {
+ // Create XML to represent menu options.
+ var container = document.createElement('mutation');
+ container.setAttribute('options', this.optionCount_);
+ return container;
+ },
+ domToMutation: function(container) {
+ // Parse XML to restore the menu options.
+ this.optionCount_ = parseInt(container.getAttribute('options'), 10);
+ this.updateShape_();
+ },
+ decompose: function(workspace) {
+ // Populate the mutator's dialog with this block's components.
+ var containerBlock = workspace.newBlock('field_dropdown_container');
+ containerBlock.initSvg();
+ var connection = containerBlock.getInput('STACK').connection;
+ for (var i = 0; i < this.optionCount_; i++) {
+ var optionBlock = workspace.newBlock('field_dropdown_option');
+ optionBlock.initSvg();
+ connection.connect(optionBlock.previousConnection);
+ connection = optionBlock.nextConnection;
+ }
+ return containerBlock;
+ },
+ compose: function(containerBlock) {
+ // Reconfigure this block based on the mutator dialog's components.
+ var optionBlock = containerBlock.getInputTargetBlock('STACK');
+ // Count number of inputs.
+ var data = [];
+ while (optionBlock) {
+ data.push([optionBlock.userData_, optionBlock.cpuData_]);
+ optionBlock = optionBlock.nextConnection &&
+ optionBlock.nextConnection.targetBlock();
+ }
+ this.optionCount_ = data.length;
+ this.updateShape_();
+ // Restore any data.
+ for (var i = 0; i < this.optionCount_; i++) {
+ this.setFieldValue(data[i][0] || 'option', 'USER' + i);
+ this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
+ }
+ },
+ saveConnections: function(containerBlock) {
+ // Store names and values for each option.
+ var optionBlock = containerBlock.getInputTargetBlock('STACK');
+ var i = 0;
+ while (optionBlock) {
+ optionBlock.userData_ = this.getFieldValue('USER' + i);
+ optionBlock.cpuData_ = this.getFieldValue('CPU' + i);
+ i++;
+ optionBlock = optionBlock.nextConnection &&
+ optionBlock.nextConnection.targetBlock();
+ }
+ },
+ updateShape_: function() {
+ // Modify this block to have the correct number of options.
+ // Add new options.
+ for (var i = 0; i < this.optionCount_; i++) {
+ if (!this.getInput('OPTION' + i)) {
+ this.appendDummyInput('OPTION' + i)
+ .appendField(new Blockly.FieldTextInput('option'), 'USER' + i)
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
+ }
+ }
+ // Remove deleted options.
+ while (this.getInput('OPTION' + i)) {
+ this.removeInput('OPTION' + i);
+ i++;
+ }
+ },
+ onchange: function() {
+ if (this.workspace && this.optionCount_ < 1) {
+ this.setWarningText('Drop down menu must\nhave at least one option.');
+ } else {
+ fieldNameCheck(this);
+ }
+ }
+};
+
+Blockly.Blocks['field_dropdown_container'] = {
+ // Container.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('add options');
+ this.appendStatementInput('STACK');
+ this.setTooltip('Add, remove, or reorder options\n' +
+ 'to reconfigure this dropdown menu.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['field_dropdown_option'] = {
+ // Add option.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('option');
+ this.setPreviousStatement(true);
+ this.setNextStatement(true);
+ this.setTooltip('Add a new option to the dropdown menu.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['field_checkbox'] = {
+ // Checkbox.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('checkbox')
+ .appendField(new Blockly.FieldCheckbox('TRUE'), 'CHECKED')
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('Checkbox field.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=485');
+ },
+ onchange: function() {
+ fieldNameCheck(this);
+ }
+};
+
+Blockly.Blocks['field_colour'] = {
+ // Colour input.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('colour')
+ .appendField(new Blockly.FieldColour('#ff0000'), 'COLOUR')
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('Colour input field.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=495');
+ },
+ onchange: function() {
+ fieldNameCheck(this);
+ }
+};
+
+Blockly.Blocks['field_date'] = {
+ // Date input.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('date')
+ .appendField(new Blockly.FieldDate(), 'DATE')
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('Date input field.');
+ },
+ onchange: function() {
+ fieldNameCheck(this);
+ }
+};
+
+Blockly.Blocks['field_variable'] = {
+ // Dropdown for variables.
+ init: function() {
+ this.setColour(160);
+ this.appendDummyInput()
+ .appendField('variable')
+ .appendField(new Blockly.FieldTextInput('item'), 'TEXT')
+ .appendField(',')
+ .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('Dropdown menu for variable names.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=510');
+ },
+ onchange: function() {
+ fieldNameCheck(this);
+ }
+};
+
+Blockly.Blocks['field_image'] = {
+ // Image.
+ init: function() {
+ this.setColour(160);
+ var src = 'https://www.gstatic.com/codesite/ph/images/star_on.gif';
+ this.appendDummyInput()
+ .appendField('image')
+ .appendField(new Blockly.FieldTextInput(src), 'SRC');
+ this.appendDummyInput()
+ .appendField('width')
+ .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH')
+ .appendField('height')
+ .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT')
+ .appendField('alt text')
+ .appendField(new Blockly.FieldTextInput('*'), 'ALT');
+ this.setPreviousStatement(true, 'Field');
+ this.setNextStatement(true, 'Field');
+ this.setTooltip('Static image (JPEG, PNG, GIF, SVG, BMP).\n' +
+ 'Retains aspect ratio regardless of height and width.\n' +
+ 'Alt text is for when collapsed.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=567');
+ }
+};
+
+Blockly.Blocks['type_group'] = {
+ // Group of types.
+ init: function() {
+ this.typeCount_ = 2;
+ this.updateShape_();
+ this.setOutput(true, 'Type');
+ this.setMutator(new Blockly.Mutator(['type_group_item']));
+ this.setColour(230);
+ this.setTooltip('Allows more than one type to be accepted.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677');
+ },
+ mutationToDom: function(workspace) {
+ // Create XML to represent a group of types.
+ var container = document.createElement('mutation');
+ container.setAttribute('types', this.typeCount_);
+ return container;
+ },
+ domToMutation: function(container) {
+ // Parse XML to restore the group of types.
+ this.typeCount_ = parseInt(container.getAttribute('types'), 10);
+ this.updateShape_();
+ for (var i = 0; i < this.typeCount_; i++) {
+ this.removeInput('TYPE' + i);
+ }
+ for (var i = 0; i < this.typeCount_; i++) {
+ var input = this.appendValueInput('TYPE' + i)
+ .setCheck('Type');
+ if (i == 0) {
+ input.appendField('any of');
+ }
+ }
+ },
+ decompose: function(workspace) {
+ // Populate the mutator's dialog with this block's components.
+ var containerBlock = workspace.newBlock('type_group_container');
+ containerBlock.initSvg();
+ var connection = containerBlock.getInput('STACK').connection;
+ for (var i = 0; i < this.typeCount_; i++) {
+ var typeBlock = workspace.newBlock('type_group_item');
+ typeBlock.initSvg();
+ connection.connect(typeBlock.previousConnection);
+ connection = typeBlock.nextConnection;
+ }
+ return containerBlock;
+ },
+ compose: function(containerBlock) {
+ // Reconfigure this block based on the mutator dialog's components.
+ var typeBlock = containerBlock.getInputTargetBlock('STACK');
+ // Count number of inputs.
+ var connections = [];
+ while (typeBlock) {
+ connections.push(typeBlock.valueConnection_);
+ typeBlock = typeBlock.nextConnection &&
+ typeBlock.nextConnection.targetBlock();
+ }
+ // Disconnect any children that don't belong.
+ for (var i = 0; i < this.typeCount_; i++) {
+ var connection = this.getInput('TYPE' + i).connection.targetConnection;
+ if (connection && connections.indexOf(connection) == -1) {
+ connection.disconnect();
+ }
+ }
+ this.typeCount_ = connections.length;
+ this.updateShape_();
+ // Reconnect any child blocks.
+ for (var i = 0; i < this.typeCount_; i++) {
+ Blockly.Mutator.reconnect(connections[i], this, 'TYPE' + i);
+ }
+ },
+ saveConnections: function(containerBlock) {
+ // Store a pointer to any connected child blocks.
+ var typeBlock = containerBlock.getInputTargetBlock('STACK');
+ var i = 0;
+ while (typeBlock) {
+ var input = this.getInput('TYPE' + i);
+ typeBlock.valueConnection_ = input && input.connection.targetConnection;
+ i++;
+ typeBlock = typeBlock.nextConnection &&
+ typeBlock.nextConnection.targetBlock();
+ }
+ },
+ updateShape_: function() {
+ // Modify this block to have the correct number of inputs.
+ // Add new inputs.
+ for (var i = 0; i < this.typeCount_; i++) {
+ if (!this.getInput('TYPE' + i)) {
+ var input = this.appendValueInput('TYPE' + i);
+ if (i == 0) {
+ input.appendField('any of');
+ }
+ }
+ }
+ // Remove deleted inputs.
+ while (this.getInput('TYPE' + i)) {
+ this.removeInput('TYPE' + i);
+ i++;
+ }
+ }
+};
+
+Blockly.Blocks['type_group_container'] = {
+ // Container.
+ init: function() {
+ this.jsonInit({
+ "message0": "add types %1 %2",
+ "args0": [
+ {"type": "input_dummy"},
+ {"type": "input_statement", "name": "STACK"}
+ ],
+ "colour": 230,
+ "tooltip": "Add, or remove allowed type.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
+ });
+ }
+};
+
+Blockly.Blocks['type_group_item'] = {
+ // Add type.
+ init: function() {
+ this.jsonInit({
+ "message0": "type",
+ "previousStatement": null,
+ "nextStatement": null,
+ "colour": 230,
+ "tooltip": "Add a new allowed type.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
+ });
+ }
+};
+
+Blockly.Blocks['type_null'] = {
+ // Null type.
+ valueType: null,
+ init: function() {
+ this.jsonInit({
+ "message0": "any",
+ "output": "Type",
+ "colour": 230,
+ "tooltip": "Any type is allowed.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
+ });
+ }
+};
+
+Blockly.Blocks['type_boolean'] = {
+ // Boolean type.
+ valueType: 'Boolean',
+ init: function() {
+ this.jsonInit({
+ "message0": "Boolean",
+ "output": "Type",
+ "colour": 230,
+ "tooltip": "Booleans (true/false) are allowed.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
+ });
+ }
+};
+
+Blockly.Blocks['type_number'] = {
+ // Number type.
+ valueType: 'Number',
+ init: function() {
+ this.jsonInit({
+ "message0": "Number",
+ "output": "Type",
+ "colour": 230,
+ "tooltip": "Numbers (int/float) are allowed.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
+ });
+ }
+};
+
+Blockly.Blocks['type_string'] = {
+ // String type.
+ valueType: 'String',
+ init: function() {
+ this.jsonInit({
+ "message0": "String",
+ "output": "Type",
+ "colour": 230,
+ "tooltip": "Strings (text) are allowed.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
+ });
+ }
+};
+
+Blockly.Blocks['type_list'] = {
+ // List type.
+ valueType: 'Array',
+ init: function() {
+ this.jsonInit({
+ "message0": "Array",
+ "output": "Type",
+ "colour": 230,
+ "tooltip": "Arrays (lists) are allowed.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
+ });
+ }
+};
+
+Blockly.Blocks['type_other'] = {
+ // Other type.
+ init: function() {
+ this.jsonInit({
+ "message0": "other %1",
+ "args0": [{"type": "field_input", "name": "TYPE", "text": ""}],
+ "output": "Type",
+ "colour": 230,
+ "tooltip": "Custom type to allow.",
+ "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=702"
+ });
+ }
+};
+
+Blockly.Blocks['colour_hue'] = {
+ // Set the colour of the block.
+ init: function() {
+ this.appendDummyInput()
+ .appendField('hue:')
+ .appendField(new Blockly.FieldAngle('0', this.validator), 'HUE');
+ this.setOutput(true, 'Colour');
+ this.setTooltip('Paint the block with this colour.');
+ this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=55');
+ },
+ validator: function(text) {
+ // Update the current block's colour to match.
+ var hue = parseInt(text, 10);
+ if (!isNaN(hue)) {
+ this.sourceBlock_.setColour(hue);
+ }
+ },
+ mutationToDom: function(workspace) {
+ var container = document.createElement('mutation');
+ container.setAttribute('colour', this.getColour());
+ return container;
+ },
+ domToMutation: function(container) {
+ this.setColour(container.getAttribute('colour'));
+ }
+};
+
+/**
+ * Check to see if more than one field has this name.
+ * Highly inefficient (On^2), but n is small.
+ * @param {!Blockly.Block} referenceBlock Block to check.
+ */
+function fieldNameCheck(referenceBlock) {
+ if (!referenceBlock.workspace) {
+ // Block has been deleted.
+ return;
+ }
+ var name = referenceBlock.getFieldValue('FIELDNAME').toLowerCase();
+ var count = 0;
+ var blocks = referenceBlock.workspace.getAllBlocks();
+ for (var i = 0, block; block = blocks[i]; i++) {
+ var otherName = block.getFieldValue('FIELDNAME');
+ if (!block.disabled && !block.getInheritedDisabled() &&
+ otherName && otherName.toLowerCase() == name) {
+ count++;
+ }
+ }
+ var msg = (count > 1) ?
+ 'There are ' + count + ' field blocks\n with this name.' : null;
+ referenceBlock.setWarningText(msg);
+}
+
+/**
+ * Check to see if more than one input has this name.
+ * Highly inefficient (On^2), but n is small.
+ * @param {!Blockly.Block} referenceBlock Block to check.
+ */
+function inputNameCheck(referenceBlock) {
+ if (!referenceBlock.workspace) {
+ // Block has been deleted.
+ return;
+ }
+ var name = referenceBlock.getFieldValue('INPUTNAME').toLowerCase();
+ var count = 0;
+ var blocks = referenceBlock.workspace.getAllBlocks();
+ for (var i = 0, block; block = blocks[i]; i++) {
+ var otherName = block.getFieldValue('INPUTNAME');
+ if (!block.disabled && !block.getInheritedDisabled() &&
+ otherName && otherName.toLowerCase() == name) {
+ count++;
+ }
+ }
+ var msg = (count > 1) ?
+ 'There are ' + count + ' input blocks\n with this name.' : null;
+ referenceBlock.setWarningText(msg);
+}
diff --git a/demos/blockfactory_old/factory.js b/demos/blockfactory_old/factory.js
new file mode 100644
index 000000000..4af5ff28a
--- /dev/null
+++ b/demos/blockfactory_old/factory.js
@@ -0,0 +1,850 @@
+/**
+ * Blockly Demos: Block Factory
+ *
+ * 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 JavaScript for Blockly's Block Factory application.
+ * @author fraser@google.com (Neil Fraser)
+ */
+'use strict';
+
+/**
+ * Workspace for user to build block.
+ * @type {Blockly.Workspace}
+ */
+var mainWorkspace = null;
+
+/**
+ * Workspace for preview of block.
+ * @type {Blockly.Workspace}
+ */
+var previewWorkspace = null;
+
+/**
+ * Name of block if not named.
+ */
+var UNNAMED = 'unnamed';
+
+/**
+ * Change the language code format.
+ */
+function formatChange() {
+ var mask = document.getElementById('blocklyMask');
+ var languagePre = document.getElementById('languagePre');
+ var languageTA = document.getElementById('languageTA');
+ if (document.getElementById('format').value == 'Manual') {
+ Blockly.hideChaff();
+ mask.style.display = 'block';
+ languagePre.style.display = 'none';
+ languageTA.style.display = 'block';
+ var code = languagePre.textContent.trim();
+ languageTA.value = code;
+ languageTA.focus();
+ updatePreview();
+ } else {
+ mask.style.display = 'none';
+ languageTA.style.display = 'none';
+ languagePre.style.display = 'block';
+ updateLanguage();
+ }
+ disableEnableLink();
+}
+
+/**
+ * Update the language code based on constructs made in Blockly.
+ */
+function updateLanguage() {
+ var rootBlock = getRootBlock();
+ if (!rootBlock) {
+ return;
+ }
+ var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
+ if (!blockType) {
+ blockType = UNNAMED;
+ }
+ blockType = blockType.replace(/\W/g, '_').replace(/^(\d)/, '_\\1');
+ switch (document.getElementById('format').value) {
+ case 'JSON':
+ var code = formatJson_(blockType, rootBlock);
+ break;
+ case 'JavaScript':
+ var code = formatJavaScript_(blockType, rootBlock);
+ break;
+ }
+ injectCode(code, 'languagePre');
+ updatePreview();
+}
+
+/**
+ * Update the language code as JSON.
+ * @param {string} blockType Name of block.
+ * @param {!Blockly.Block} rootBlock Factory_base block.
+ * @return {string} Generanted language code.
+ * @private
+ */
+function formatJson_(blockType, rootBlock) {
+ var JS = {};
+ // Type is not used by Blockly, but may be used by a loader.
+ JS.type = blockType;
+ // Generate inputs.
+ var message = [];
+ var args = [];
+ var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
+ var lastInput = null;
+ while (contentsBlock) {
+ if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
+ var fields = getFieldsJson_(contentsBlock.getInputTargetBlock('FIELDS'));
+ for (var i = 0; i < fields.length; i++) {
+ if (typeof fields[i] == 'string') {
+ message.push(fields[i].replace(/%/g, '%%'));
+ } else {
+ args.push(fields[i]);
+ message.push('%' + args.length);
+ }
+ }
+
+ var input = {type: contentsBlock.type};
+ // Dummy inputs don't have names. Other inputs do.
+ if (contentsBlock.type != 'input_dummy') {
+ input.name = contentsBlock.getFieldValue('INPUTNAME');
+ }
+ var check = JSON.parse(getOptTypesFrom(contentsBlock, 'TYPE') || 'null');
+ if (check) {
+ input.check = check;
+ }
+ var align = contentsBlock.getFieldValue('ALIGN');
+ if (align != 'LEFT') {
+ input.align = align;
+ }
+ args.push(input);
+ message.push('%' + args.length);
+ lastInput = contentsBlock;
+ }
+ contentsBlock = contentsBlock.nextConnection &&
+ contentsBlock.nextConnection.targetBlock();
+ }
+ // Remove last input if dummy and not empty.
+ if (lastInput && lastInput.type == 'input_dummy') {
+ var fields = lastInput.getInputTargetBlock('FIELDS');
+ if (fields && getFieldsJson_(fields).join('').trim() != '') {
+ var align = lastInput.getFieldValue('ALIGN');
+ if (align != 'LEFT') {
+ JS.lastDummyAlign0 = align;
+ }
+ args.pop();
+ message.pop();
+ }
+ }
+ JS.message0 = message.join(' ');
+ if (args.length) {
+ JS.args0 = args;
+ }
+ // Generate inline/external switch.
+ if (rootBlock.getFieldValue('INLINE') == 'EXT') {
+ JS.inputsInline = false;
+ } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
+ JS.inputsInline = true;
+ }
+ // Generate output, or next/previous connections.
+ switch (rootBlock.getFieldValue('CONNECTIONS')) {
+ case 'LEFT':
+ JS.output =
+ JSON.parse(getOptTypesFrom(rootBlock, 'OUTPUTTYPE') || 'null');
+ break;
+ case 'BOTH':
+ JS.previousStatement =
+ JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
+ JS.nextStatement =
+ JSON.parse(getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
+ break;
+ case 'TOP':
+ JS.previousStatement =
+ JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
+ break;
+ case 'BOTTOM':
+ JS.nextStatement =
+ JSON.parse(getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
+ break;
+ }
+ // Generate colour.
+ var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
+ if (colourBlock && !colourBlock.disabled) {
+ var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
+ JS.colour = hue;
+ }
+ JS.tooltip = '';
+ JS.helpUrl = 'http://www.example.com/';
+ return JSON.stringify(JS, null, ' ');
+}
+
+/**
+ * Update the language code as JavaScript.
+ * @param {string} blockType Name of block.
+ * @param {!Blockly.Block} rootBlock Factory_base block.
+ * @return {string} Generanted language code.
+ * @private
+ */
+function formatJavaScript_(blockType, rootBlock) {
+ var code = [];
+ code.push("Blockly.Blocks['" + blockType + "'] = {");
+ code.push(" init: function() {");
+ // Generate inputs.
+ var TYPES = {'input_value': 'appendValueInput',
+ 'input_statement': 'appendStatementInput',
+ 'input_dummy': 'appendDummyInput'};
+ var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
+ while (contentsBlock) {
+ if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
+ var name = '';
+ // Dummy inputs don't have names. Other inputs do.
+ if (contentsBlock.type != 'input_dummy') {
+ name = escapeString(contentsBlock.getFieldValue('INPUTNAME'));
+ }
+ code.push(' this.' + TYPES[contentsBlock.type] + '(' + name + ')');
+ var check = getOptTypesFrom(contentsBlock, 'TYPE');
+ if (check) {
+ code.push(' .setCheck(' + check + ')');
+ }
+ var align = contentsBlock.getFieldValue('ALIGN');
+ if (align != 'LEFT') {
+ code.push(' .setAlign(Blockly.ALIGN_' + align + ')');
+ }
+ var fields = getFieldsJs_(contentsBlock.getInputTargetBlock('FIELDS'));
+ for (var i = 0; i < fields.length; i++) {
+ code.push(' .appendField(' + fields[i] + ')');
+ }
+ // Add semicolon to last line to finish the statement.
+ code[code.length - 1] += ';';
+ }
+ contentsBlock = contentsBlock.nextConnection &&
+ contentsBlock.nextConnection.targetBlock();
+ }
+ // Generate inline/external switch.
+ if (rootBlock.getFieldValue('INLINE') == 'EXT') {
+ code.push(' this.setInputsInline(false);');
+ } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
+ code.push(' this.setInputsInline(true);');
+ }
+ // Generate output, or next/previous connections.
+ switch (rootBlock.getFieldValue('CONNECTIONS')) {
+ case 'LEFT':
+ code.push(connectionLineJs_('setOutput', 'OUTPUTTYPE'));
+ break;
+ case 'BOTH':
+ code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
+ code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
+ break;
+ case 'TOP':
+ code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
+ break;
+ case 'BOTTOM':
+ code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
+ break;
+ }
+ // Generate colour.
+ var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
+ if (colourBlock && !colourBlock.disabled) {
+ var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
+ if (!isNaN(hue)) {
+ code.push(' this.setColour(' + hue + ');');
+ }
+ }
+ code.push(" this.setTooltip('');");
+ code.push(" this.setHelpUrl('http://www.example.com/');");
+ code.push(' }');
+ code.push('};');
+ return code.join('\n');
+}
+
+/**
+ * Create JS code required to create a top, bottom, or value connection.
+ * @param {string} functionName JavaScript function name.
+ * @param {string} typeName Name of type input.
+ * @return {string} Line of JavaScript code to create connection.
+ * @private
+ */
+function connectionLineJs_(functionName, typeName) {
+ var type = getOptTypesFrom(getRootBlock(), typeName);
+ if (type) {
+ type = ', ' + type;
+ } else {
+ type = '';
+ }
+ return ' this.' + functionName + '(true' + type + ');';
+}
+
+/**
+ * Returns field strings and any config.
+ * @param {!Blockly.Block} block Input block.
+ * @return {!Array.} Field strings.
+ * @private
+ */
+function getFieldsJs_(block) {
+ var fields = [];
+ while (block) {
+ if (!block.disabled && !block.getInheritedDisabled()) {
+ switch (block.type) {
+ case 'field_static':
+ // Result: 'hello'
+ fields.push(escapeString(block.getFieldValue('TEXT')));
+ break;
+ case 'field_input':
+ // Result: new Blockly.FieldTextInput('Hello'), 'GREET'
+ fields.push('new Blockly.FieldTextInput(' +
+ escapeString(block.getFieldValue('TEXT')) + '), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ break;
+ case 'field_number':
+ // Result: new Blockly.FieldNumber(10, 0, 100, 1), 'NUMBER'
+ var args = [
+ Number(block.getFieldValue('VALUE')),
+ Number(block.getFieldValue('MIN')),
+ Number(block.getFieldValue('MAX')),
+ Number(block.getFieldValue('PRECISION'))
+ ];
+ // Remove any trailing arguments that aren't needed.
+ if (args[3] == 0) {
+ args.pop();
+ if (args[2] == Infinity) {
+ args.pop();
+ if (args[1] == -Infinity) {
+ args.pop();
+ }
+ }
+ }
+ fields.push('new Blockly.FieldNumber(' + args.join(', ') + '), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ break;
+ case 'field_angle':
+ // Result: new Blockly.FieldAngle(90), 'ANGLE'
+ fields.push('new Blockly.FieldAngle(' +
+ Number(block.getFieldValue('ANGLE')) + '), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ break;
+ case 'field_checkbox':
+ // Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
+ fields.push('new Blockly.FieldCheckbox(' +
+ escapeString(block.getFieldValue('CHECKED')) + '), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ break;
+ case 'field_colour':
+ // Result: new Blockly.FieldColour('#ff0000'), 'COLOUR'
+ fields.push('new Blockly.FieldColour(' +
+ escapeString(block.getFieldValue('COLOUR')) + '), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ break;
+ case 'field_date':
+ // Result: new Blockly.FieldDate('2015-02-04'), 'DATE'
+ fields.push('new Blockly.FieldDate(' +
+ escapeString(block.getFieldValue('DATE')) + '), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ break;
+ case 'field_variable':
+ // Result: new Blockly.FieldVariable('item'), 'VAR'
+ var varname = escapeString(block.getFieldValue('TEXT') || null);
+ fields.push('new Blockly.FieldVariable(' + varname + '), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ break;
+ case 'field_dropdown':
+ // Result:
+ // new Blockly.FieldDropdown([['yes', '1'], ['no', '0']]), 'TOGGLE'
+ var options = [];
+ for (var i = 0; i < block.optionCount_; i++) {
+ options[i] = '[' + escapeString(block.getFieldValue('USER' + i)) +
+ ', ' + escapeString(block.getFieldValue('CPU' + i)) + ']';
+ }
+ if (options.length) {
+ fields.push('new Blockly.FieldDropdown([' +
+ options.join(', ') + ']), ' +
+ escapeString(block.getFieldValue('FIELDNAME')));
+ }
+ break;
+ case 'field_image':
+ // Result: new Blockly.FieldImage('http://...', 80, 60, '*')
+ var src = escapeString(block.getFieldValue('SRC'));
+ var width = Number(block.getFieldValue('WIDTH'));
+ var height = Number(block.getFieldValue('HEIGHT'));
+ var alt = escapeString(block.getFieldValue('ALT'));
+ fields.push('new Blockly.FieldImage(' +
+ src + ', ' + width + ', ' + height + ', ' + alt + ')');
+ break;
+ }
+ }
+ block = block.nextConnection && block.nextConnection.targetBlock();
+ }
+ return fields;
+}
+
+/**
+ * Returns field strings and any config.
+ * @param {!Blockly.Block} block Input block.
+ * @return {!Array.} Array of static text and field configs.
+ * @private
+ */
+function getFieldsJson_(block) {
+ var fields = [];
+ while (block) {
+ if (!block.disabled && !block.getInheritedDisabled()) {
+ switch (block.type) {
+ case 'field_static':
+ // Result: 'hello'
+ fields.push(block.getFieldValue('TEXT'));
+ break;
+ case 'field_input':
+ fields.push({
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ text: block.getFieldValue('TEXT')
+ });
+ break;
+ case 'field_number':
+ var obj = {
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ value: parseFloat(block.getFieldValue('VALUE'))
+ };
+ var min = parseFloat(block.getFieldValue('MIN'));
+ if (min > -Infinity) {
+ obj.min = min;
+ }
+ var max = parseFloat(block.getFieldValue('MAX'));
+ if (max < Infinity) {
+ obj.max = max;
+ }
+ var precision = parseFloat(block.getFieldValue('PRECISION'));
+ if (precision) {
+ obj.precision = precision;
+ }
+ fields.push(obj);
+ break;
+ case 'field_angle':
+ fields.push({
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ angle: Number(block.getFieldValue('ANGLE'))
+ });
+ break;
+ case 'field_checkbox':
+ fields.push({
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ checked: block.getFieldValue('CHECKED') == 'TRUE'
+ });
+ break;
+ case 'field_colour':
+ fields.push({
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ colour: block.getFieldValue('COLOUR')
+ });
+ break;
+ case 'field_date':
+ fields.push({
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ date: block.getFieldValue('DATE')
+ });
+ break;
+ case 'field_variable':
+ fields.push({
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ variable: block.getFieldValue('TEXT') || null
+ });
+ break;
+ case 'field_dropdown':
+ var options = [];
+ for (var i = 0; i < block.optionCount_; i++) {
+ options[i] = [block.getFieldValue('USER' + i),
+ block.getFieldValue('CPU' + i)];
+ }
+ if (options.length) {
+ fields.push({
+ type: block.type,
+ name: block.getFieldValue('FIELDNAME'),
+ options: options
+ });
+ }
+ break;
+ case 'field_image':
+ fields.push({
+ type: block.type,
+ src: block.getFieldValue('SRC'),
+ width: Number(block.getFieldValue('WIDTH')),
+ height: Number(block.getFieldValue('HEIGHT')),
+ alt: block.getFieldValue('ALT')
+ });
+ break;
+ }
+ }
+ block = block.nextConnection && block.nextConnection.targetBlock();
+ }
+ return fields;
+}
+
+/**
+ * Escape a string.
+ * @param {string} string String to escape.
+ * @return {string} Escaped string surrouned by quotes.
+ */
+function escapeString(string) {
+ return JSON.stringify(string);
+}
+
+/**
+ * Fetch the type(s) defined in the given input.
+ * Format as a string for appending to the generated code.
+ * @param {!Blockly.Block} block Block with input.
+ * @param {string} name Name of the input.
+ * @return {?string} String defining the types.
+ */
+function getOptTypesFrom(block, name) {
+ var types = getTypesFrom_(block, name);
+ if (types.length == 0) {
+ return undefined;
+ } else if (types.indexOf('null') != -1) {
+ return 'null';
+ } else if (types.length == 1) {
+ return types[0];
+ } else {
+ return '[' + types.join(', ') + ']';
+ }
+}
+
+/**
+ * Fetch the type(s) defined in the given input.
+ * @param {!Blockly.Block} block Block with input.
+ * @param {string} name Name of the input.
+ * @return {!Array.} List of types.
+ * @private
+ */
+function getTypesFrom_(block, name) {
+ var typeBlock = block.getInputTargetBlock(name);
+ var types;
+ if (!typeBlock || typeBlock.disabled) {
+ types = [];
+ } else if (typeBlock.type == 'type_other') {
+ types = [escapeString(typeBlock.getFieldValue('TYPE'))];
+ } else if (typeBlock.type == 'type_group') {
+ types = [];
+ for (var i = 0; i < typeBlock.typeCount_; i++) {
+ types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + i));
+ }
+ // Remove duplicates.
+ var hash = Object.create(null);
+ for (var n = types.length - 1; n >= 0; n--) {
+ if (hash[types[n]]) {
+ types.splice(n, 1);
+ }
+ hash[types[n]] = true;
+ }
+ } else {
+ types = [escapeString(typeBlock.valueType)];
+ }
+ return types;
+}
+
+/**
+ * Update the generator code.
+ * @param {!Blockly.Block} block Rendered block in preview workspace.
+ */
+function updateGenerator(block) {
+ function makeVar(root, name) {
+ name = name.toLowerCase().replace(/\W/g, '_');
+ return ' var ' + root + '_' + name;
+ }
+ var language = document.getElementById('language').value;
+ var code = [];
+ code.push("Blockly." + language + "['" + block.type +
+ "'] = function(block) {");
+
+ // Generate getters for any fields or inputs.
+ for (var i = 0, input; input = block.inputList[i]; i++) {
+ for (var j = 0, field; field = input.fieldRow[j]; j++) {
+ var name = field.name;
+ if (!name) {
+ continue;
+ }
+ if (field instanceof Blockly.FieldVariable) {
+ // Subclass of Blockly.FieldDropdown, must test first.
+ code.push(makeVar('variable', name) +
+ " = Blockly." + language +
+ ".variableDB_.getName(block.getFieldValue('" + name +
+ "'), Blockly.Variables.NAME_TYPE);");
+ } else if (field instanceof Blockly.FieldAngle) {
+ // Subclass of Blockly.FieldTextInput, must test first.
+ code.push(makeVar('angle', name) +
+ " = block.getFieldValue('" + name + "');");
+ } else if (Blockly.FieldDate && field instanceof Blockly.FieldDate) {
+ // Blockly.FieldDate may not be compiled into Blockly.
+ code.push(makeVar('date', name) +
+ " = block.getFieldValue('" + name + "');");
+ } else if (field instanceof Blockly.FieldColour) {
+ code.push(makeVar('colour', name) +
+ " = block.getFieldValue('" + name + "');");
+ } else if (field instanceof Blockly.FieldCheckbox) {
+ code.push(makeVar('checkbox', name) +
+ " = block.getFieldValue('" + name + "') == 'TRUE';");
+ } else if (field instanceof Blockly.FieldDropdown) {
+ code.push(makeVar('dropdown', name) +
+ " = block.getFieldValue('" + name + "');");
+ } else if (field instanceof Blockly.FieldNumber) {
+ code.push(makeVar('number', name) +
+ " = block.getFieldValue('" + name + "');");
+ } else if (field instanceof Blockly.FieldTextInput) {
+ code.push(makeVar('text', name) +
+ " = block.getFieldValue('" + name + "');");
+ }
+ }
+ var name = input.name;
+ if (name) {
+ if (input.type == Blockly.INPUT_VALUE) {
+ code.push(makeVar('value', name) +
+ " = Blockly." + language + ".valueToCode(block, '" + name +
+ "', Blockly." + language + ".ORDER_ATOMIC);");
+ } else if (input.type == Blockly.NEXT_STATEMENT) {
+ code.push(makeVar('statements', name) +
+ " = Blockly." + language + ".statementToCode(block, '" +
+ name + "');");
+ }
+ }
+ }
+ // Most languages end lines with a semicolon. Python does not.
+ var lineEnd = {
+ 'JavaScript': ';',
+ 'Python': '',
+ 'PHP': ';',
+ 'Dart': ';'
+ };
+ code.push(" // TODO: Assemble " + language + " into code variable.");
+ if (block.outputConnection) {
+ code.push(" var code = '...';");
+ code.push(" // TODO: Change ORDER_NONE to the correct strength.");
+ code.push(" return [code, Blockly." + language + ".ORDER_NONE];");
+ } else {
+ code.push(" var code = '..." + (lineEnd[language] || '') + "\\n';");
+ code.push(" return code;");
+ }
+ code.push("};");
+
+ injectCode(code.join('\n'), 'generatorPre');
+}
+
+/**
+ * Existing direction ('ltr' vs 'rtl') of preview.
+ */
+var oldDir = null;
+
+/**
+ * Update the preview display.
+ */
+function updatePreview() {
+ // Toggle between LTR/RTL if needed (also used in first display).
+ var newDir = document.getElementById('direction').value;
+ if (oldDir != newDir) {
+ if (previewWorkspace) {
+ previewWorkspace.dispose();
+ }
+ var rtl = newDir == 'rtl';
+ previewWorkspace = Blockly.inject('preview',
+ {rtl: rtl,
+ media: '../../media/',
+ scrollbars: true});
+ oldDir = newDir;
+ }
+ previewWorkspace.clear();
+
+ // Fetch the code and determine its format (JSON or JavaScript).
+ var format = document.getElementById('format').value;
+ if (format == 'Manual') {
+ var code = document.getElementById('languageTA').value;
+ // If the code is JSON, it will parse, otherwise treat as JS.
+ try {
+ JSON.parse(code);
+ format = 'JSON';
+ } catch (e) {
+ format = 'JavaScript';
+ }
+ } else {
+ var code = document.getElementById('languagePre').textContent;
+ }
+ if (!code.trim()) {
+ // Nothing to render. Happens while cloud storage is loading.
+ return;
+ }
+
+ // Backup Blockly.Blocks object so that main workspace and preview don't
+ // collide if user creates a 'factory_base' block, for instance.
+ var backupBlocks = Blockly.Blocks;
+ try {
+ // Make a shallow copy.
+ Blockly.Blocks = {};
+ for (var prop in backupBlocks) {
+ Blockly.Blocks[prop] = backupBlocks[prop];
+ }
+
+ if (format == 'JSON') {
+ var json = JSON.parse(code);
+ Blockly.Blocks[json.type || UNNAMED] = {
+ init: function() {
+ this.jsonInit(json);
+ }
+ };
+ } else if (format == 'JavaScript') {
+ eval(code);
+ } else {
+ throw 'Unknown format: ' + format;
+ }
+
+ // Look for a block on Blockly.Blocks that does not match the backup.
+ var blockType = null;
+ for (var type in Blockly.Blocks) {
+ if (typeof Blockly.Blocks[type].init == 'function' &&
+ Blockly.Blocks[type] != backupBlocks[type]) {
+ blockType = type;
+ break;
+ }
+ }
+ if (!blockType) {
+ return;
+ }
+
+ // Create the preview block.
+ var previewBlock = previewWorkspace.newBlock(blockType);
+ previewBlock.initSvg();
+ previewBlock.render();
+ previewBlock.setMovable(false);
+ previewBlock.setDeletable(false);
+ previewBlock.moveBy(15, 10);
+ previewWorkspace.clearUndo();
+
+ updateGenerator(previewBlock);
+ } finally {
+ Blockly.Blocks = backupBlocks;
+ }
+}
+
+/**
+ * Inject code into a pre tag, with syntax highlighting.
+ * Safe from HTML/script injection.
+ * @param {string} code Lines of code.
+ * @param {string} id ID of element to inject into.
+ */
+function injectCode(code, id) {
+ var pre = document.getElementById(id);
+ pre.textContent = code;
+ code = pre.innerHTML;
+ code = prettyPrintOne(code, 'js');
+ pre.innerHTML = code;
+}
+
+/**
+ * Return the uneditable container block that everything else attaches to.
+ * @return {Blockly.Block}
+ */
+function getRootBlock() {
+ var blocks = mainWorkspace.getTopBlocks(false);
+ for (var i = 0, block; block = blocks[i]; i++) {
+ if (block.type == 'factory_base') {
+ return block;
+ }
+ }
+ return null;
+}
+
+/**
+ * Disable the link button if the format is 'Manual', enable otherwise.
+ */
+function disableEnableLink() {
+ var linkButton = document.getElementById('linkButton');
+ linkButton.disabled = document.getElementById('format').value == 'Manual';
+}
+
+/**
+ * Initialize Blockly and layout. Called on page load.
+ */
+function init() {
+ if ('BlocklyStorage' in window) {
+ BlocklyStorage.HTTPREQUEST_ERROR =
+ 'There was a problem with the request.\n';
+ BlocklyStorage.LINK_ALERT =
+ 'Share your blocks with this link:\n\n%1';
+ BlocklyStorage.HASH_ERROR =
+ 'Sorry, "%1" doesn\'t correspond with any saved Blockly file.';
+ BlocklyStorage.XML_ERROR = 'Could not load your saved file.\n'+
+ 'Perhaps it was created with a different version of Blockly?';
+ var linkButton = document.getElementById('linkButton');
+ linkButton.style.display = 'inline-block';
+ linkButton.addEventListener('click',
+ function() {BlocklyStorage.link(mainWorkspace);});
+ disableEnableLink();
+ }
+
+ document.getElementById('helpButton').addEventListener('click',
+ function() {
+ open('https://developers.google.com/blockly/guides/create-custom-blocks/block-factory',
+ 'BlockFactoryHelp');
+ });
+
+ var expandList = [
+ document.getElementById('blockly'),
+ document.getElementById('blocklyMask'),
+ document.getElementById('preview'),
+ document.getElementById('languagePre'),
+ document.getElementById('languageTA'),
+ document.getElementById('generatorPre')
+ ];
+ var onresize = function(e) {
+ for (var i = 0, expand; expand = expandList[i]; i++) {
+ expand.style.width = (expand.parentNode.offsetWidth - 2) + 'px';
+ expand.style.height = (expand.parentNode.offsetHeight - 2) + 'px';
+ }
+ };
+ onresize();
+ window.addEventListener('resize', onresize);
+
+ var toolbox = document.getElementById('toolbox');
+ mainWorkspace = Blockly.inject('blockly',
+ {collapse: false,
+ toolbox: toolbox,
+ media: '../../media/'});
+
+ // Create the root block.
+ if ('BlocklyStorage' in window && window.location.hash.length > 1) {
+ BlocklyStorage.retrieveXml(window.location.hash.substring(1),
+ mainWorkspace);
+ } else {
+ var xml = '';
+ Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), mainWorkspace);
+ }
+ mainWorkspace.clearUndo();
+
+ mainWorkspace.addChangeListener(Blockly.Events.disableOrphans);
+ mainWorkspace.addChangeListener(updateLanguage);
+ document.getElementById('direction')
+ .addEventListener('change', updatePreview);
+ document.getElementById('languageTA')
+ .addEventListener('change', updatePreview);
+ document.getElementById('languageTA')
+ .addEventListener('keyup', updatePreview);
+ document.getElementById('format')
+ .addEventListener('change', formatChange);
+ document.getElementById('language')
+ .addEventListener('change', updatePreview);
+}
+window.addEventListener('load', init);
diff --git a/demos/blockfactory_old/icon.png b/demos/blockfactory_old/icon.png
new file mode 100644
index 000000000..d4d19b457
Binary files /dev/null and b/demos/blockfactory_old/icon.png differ
diff --git a/demos/blockfactory_old/index.html b/demos/blockfactory_old/index.html
new file mode 100644
index 000000000..449b9a5e8
--- /dev/null
+++ b/demos/blockfactory_old/index.html
@@ -0,0 +1,230 @@
+
+
+
+
+
+ Blockly Demo: Block Factory
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+ |
+
+
+
+
+ Preview:
+
+
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+ |
+
+
+
+ Language code:
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+ Generator stub:
+
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 20
+ 65
+ 120
+ 160
+ 210
+ 230
+ 260
+ 290
+ 330
+
+
+
+
diff --git a/demos/blocklyfactory/link.png b/demos/blockfactory_old/link.png
similarity index 100%
rename from demos/blocklyfactory/link.png
rename to demos/blockfactory_old/link.png
diff --git a/demos/blocklyfactory/factory.js b/demos/blocklyfactory/factory.js
deleted file mode 100644
index 873c007b2..000000000
--- a/demos/blocklyfactory/factory.js
+++ /dev/null
@@ -1,265 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2016 Google Inc.
- * https://developers.google.com/blockly/
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @fileoverview JavaScript for Blockly's Block Factory application through
- * which users can build blocks using a visual interface and dynamically
- * generate a preview block and starter code for the block (block definition and
- * generator stub. Uses the Block Factory namespace. Depends on the FactoryUtils
- * for its code generation functions.
- *
- * @author fraser@google.com (Neil Fraser), quachtina96 (Tina Quach)
- */
-'use strict';
-
-/**
- * Namespace for Block Factory.
- */
-goog.provide('BlockFactory');
-
-goog.require('FactoryUtils');
-goog.require('StandardCategories');
-
-
-/**
- * Workspace for user to build block.
- * @type {Blockly.Workspace}
- */
-BlockFactory.mainWorkspace = null;
-
-/**
- * Workspace for preview of block.
- * @type {Blockly.Workspace}
- */
-BlockFactory.previewWorkspace = null;
-
-/**
- * Name of block if not named.
- */
-BlockFactory.UNNAMED = 'unnamed';
-
-/**
- * Existing direction ('ltr' vs 'rtl') of preview.
- */
-BlockFactory.oldDir = null;
-
-/*
- * The starting XML for the Block Factory main workspace. Contains the
- * unmovable, undeletable factory_base block.
- */
-BlockFactory.STARTER_BLOCK_XML_TEXT = '';
-
-/**
- * Change the language code format.
- */
-BlockFactory.formatChange = function() {
- var mask = document.getElementById('blocklyMask');
- var languagePre = document.getElementById('languagePre');
- var languageTA = document.getElementById('languageTA');
- if (document.getElementById('format').value == 'Manual') {
- Blockly.hideChaff();
- mask.style.display = 'block';
- languagePre.style.display = 'none';
- languageTA.style.display = 'block';
- var code = languagePre.textContent.trim();
- languageTA.value = code;
- languageTA.focus();
- BlockFactory.updatePreview();
- } else {
- mask.style.display = 'none';
- languageTA.style.display = 'none';
- languagePre.style.display = 'block';
- BlockFactory.updateLanguage();
- }
- BlockFactory.disableEnableLink();
-};
-
-/**
- * Update the language code based on constructs made in Blockly.
- */
-BlockFactory.updateLanguage = function() {
- var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
- if (!rootBlock) {
- return;
- }
- var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
- if (!blockType) {
- blockType = BlockFactory.UNNAMED;
- }
- var format = document.getElementById('format').value;
- var code = FactoryUtils.getBlockDefinition(blockType, rootBlock, format,
- BlockFactory.mainWorkspace);
- FactoryUtils.injectCode(code, 'languagePre');
- BlockFactory.updatePreview();
-};
-
-/**
- * Update the generator code.
- * @param {!Blockly.Block} block Rendered block in preview workspace.
- */
-BlockFactory.updateGenerator = function(block) {
- var language = document.getElementById('language').value;
- var generatorStub = FactoryUtils.getGeneratorStub(block, language);
- FactoryUtils.injectCode(generatorStub, 'generatorPre');
-};
-
-/**
- * Update the preview display.
- */
-BlockFactory.updatePreview = function() {
- // Toggle between LTR/RTL if needed (also used in first display).
- var newDir = document.getElementById('direction').value;
- if (BlockFactory.oldDir != newDir) {
- if (BlockFactory.previewWorkspace) {
- BlockFactory.previewWorkspace.dispose();
- }
- var rtl = newDir == 'rtl';
- BlockFactory.previewWorkspace = Blockly.inject('preview',
- {rtl: rtl,
- media: '../../media/',
- scrollbars: true});
- BlockFactory.oldDir = newDir;
- }
- BlockFactory.previewWorkspace.clear();
-
- // Fetch the code and determine its format (JSON or JavaScript).
- var format = document.getElementById('format').value;
- if (format == 'Manual') {
- var code = document.getElementById('languageTA').value;
- // If the code is JSON, it will parse, otherwise treat as JS.
- try {
- JSON.parse(code);
- format = 'JSON';
- } catch (e) {
- format = 'JavaScript';
- }
- } else {
- var code = document.getElementById('languagePre').textContent;
- }
- if (!code.trim()) {
- // Nothing to render. Happens while cloud storage is loading.
- return;
- }
-
- // Backup Blockly.Blocks object so that main workspace and preview don't
- // collide if user creates a 'factory_base' block, for instance.
- var backupBlocks = Blockly.Blocks;
- try {
- // Make a shallow copy.
- Blockly.Blocks = Object.create(null);
- for (var prop in backupBlocks) {
- Blockly.Blocks[prop] = backupBlocks[prop];
- }
-
- if (format == 'JSON') {
- var json = JSON.parse(code);
- Blockly.Blocks[json.type || BlockFactory.UNNAMED] = {
- init: function() {
- this.jsonInit(json);
- }
- };
- } else if (format == 'JavaScript') {
- eval(code);
- } else {
- throw 'Unknown format: ' + format;
- }
-
- // Look for a block on Blockly.Blocks that does not match the backup.
- var blockType = null;
- for (var type in Blockly.Blocks) {
- if (typeof Blockly.Blocks[type].init == 'function' &&
- Blockly.Blocks[type] != backupBlocks[type]) {
- blockType = type;
- break;
- }
- }
- if (!blockType) {
- return;
- }
-
- // Create the preview block.
- var previewBlock = BlockFactory.previewWorkspace.newBlock(blockType);
- previewBlock.initSvg();
- previewBlock.render();
- previewBlock.setMovable(false);
- previewBlock.setDeletable(false);
- previewBlock.moveBy(15, 10);
- BlockFactory.previewWorkspace.clearUndo();
- BlockFactory.updateGenerator(previewBlock);
-
- // Warn user only if their block type is already exists in Blockly's
- // standard library.
- var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
- if (StandardCategories.coreBlockTypes.indexOf(blockType) != -1) {
- rootBlock.setWarningText('A core Blockly block already exists ' +
- 'under this name.');
-
- } else if (blockType == 'block_type') {
- // Warn user to let them know they can't save a block under the default
- // name 'block_type'
- rootBlock.setWarningText('You cannot save a block with the default ' +
- 'name, "block_type"');
-
- } else {
- rootBlock.setWarningText(null);
- }
-
- } finally {
- Blockly.Blocks = backupBlocks;
- }
-};
-
-/**
- * Disable link and save buttons if the format is 'Manual', enable otherwise.
- */
-BlockFactory.disableEnableLink = function() {
- var linkButton = document.getElementById('linkButton');
- var saveBlockButton = document.getElementById('localSaveButton');
- var saveToLibButton = document.getElementById('saveToBlockLibraryButton');
- var disabled = document.getElementById('format').value == 'Manual';
- linkButton.disabled = disabled;
- saveBlockButton.disabled = disabled;
- saveToLibButton.disabled = disabled;
-};
-
-/**
- * Render starter block (factory_base).
- */
-BlockFactory.showStarterBlock = function() {
- BlockFactory.mainWorkspace.clear();
- var xml = Blockly.Xml.textToDom(BlockFactory.STARTER_BLOCK_XML_TEXT);
- Blockly.Xml.domToWorkspace(xml, BlockFactory.mainWorkspace);
-};
-
-/**
- * Returns whether or not the current block open is the starter block.
- */
-BlockFactory.isStarterBlock = function() {
- var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
- // The starter block does not have blocks nested into the factory_base block.
- return !(rootBlock.getChildren().length > 0 ||
- // The starter block's name is the default, 'block_type'.
- rootBlock.getFieldValue('NAME').trim().toLowerCase() != 'block_type' ||
- // The starter block has no connections.
- rootBlock.getFieldValue('CONNECTIONS') != 'NONE' ||
- // The starter block has automatic inputs.
- rootBlock.getFieldValue('INLINE') != 'AUTO');
-};
diff --git a/demos/blocklyfactory/index.html b/demos/blocklyfactory/index.html
deleted file mode 100644
index 7521cf7fd..000000000
--- a/demos/blocklyfactory/index.html
+++ /dev/null
@@ -1,740 +0,0 @@
-
-
-
-
-
-
-
- Blockly Demo: Blockly Factory
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Blockly >
- Demos > Blockly Factory
-
-
-
- Block Factory
- Block Exporter
- Workspace Factory
-
-
-
-
-
- First, select blocks from your block library by clicking on them. Then, use the Export Settings form to download starter code for selected blocks.
-
-
-
- Block Selector
-
-
-
-
-
-
-
-
-
-
-
- Export Settings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
- |
-
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
-
- |
-
- |
-
-
-
- Block Definition:
-
-
- |
-
-
- |
-
-
- |
-
-
-
- Generator stub:
-
-
- |
-
-
- |
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 20
- 65
- 120
- 160
- 210
- 230
- 260
- 290
- 330
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 10
-
-
-
-
-
-
-
- 1
-
-
-
-
- 10
-
-
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
- 1
-
-
-
-
-
-
- 9
-
-
-
-
-
-
- 45
-
-
-
-
-
-
-
- 0
-
-
-
-
-
-
- 3.1
-
-
-
-
-
-
-
- 64
-
-
-
-
- 10
-
-
-
-
-
-
- 50
-
-
-
-
- 1
-
-
-
-
- 100
-
-
-
-
-
-
- 1
-
-
-
-
- 100
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- abc
-
-
-
-
-
-
-
-
-
-
-
-
-
- text
-
-
-
-
- abc
-
-
-
-
-
-
- text
-
-
-
-
-
-
- text
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
-
-
-
-
-
-
- 5
-
-
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- ,
-
-
-
-
-
-
-
-
-
-
-
- 100
-
-
-
-
- 50
-
-
-
-
- 0
-
-
-
-
-
-
- #ff0000
-
-
-
-
- #3333ff
-
-
-
-
- 0.5
-
-
-
-
-
-
-
-
-
-
-
-
-
|