app/assets/htmls/gs-element-blockly.html in gobstones-blockly-0.6.0 vs app/assets/htmls/gs-element-blockly.html in gobstones-blockly-0.8.5
- old
+ new
@@ -107,707 +107,10 @@
<block type="AsociacionDeTeclaCompletar"></block>
</category>
</xml>
</template>
- <script>
- Polymer({
- is: 'gs-element-blockly',
-
- properties: {
- /*
- * `primitiveProcedures` lista de definiciones de procedimientos primitivos.
- * Ej: ["Poner_FloresAl_"] Se transormara en un bloque con dos inputs inline, cuyo
- * codigo sera Poner_FloresAl_(arg1, arg2).
- */
- primitiveProcedures: {
- type: Array,
- observer: '_onPrimitiveProceduresChange'
- },
-
- /*
- * `primitiveFunctions` lista de definiciones de funciones primitivas.
- * Ej: ["cantidadDeFlores_en_"] Se transormara en un bloque con dos inputs inline, cuyo
- * codigo sera cantidadDeFlores_en_(arg1, arg2).
- */
- primitiveFunctions: {
- type: Array,
- observer: '_onPrimitiveFunctionsChange'
- },
-
- /*
- * `toolbox` Definicion de visibilidad y habilitación de los bloques en el toolbox.
- * Pueden nombrarse bloques individuales o categorias.
- *
- * Ej:
- *
- * ```json
- * {
- * "visible": ["Comandos primitivos", "Literales", "Expresiones"],
- * "disabled": ["Poner", "hayBolitas", "Literales"]
- * }
- * ```
- *
- * Los IDs de los bloques a utilizar pueden consultarse [en la wiki](https://github.com/Program-AR/gs-element-blockly/wiki/Block-IDs-for-toolbox)
- *
- * Nota: Los bloques de primitiveProcedures siempre aparecen en el toolbox, aunque no figuren
- * en esta propiedad.
- */
- toolbox: {
- type: Object,
- observer: '_onUpdateToolbox'
- },
-
- /*
- * `workspaceXml` Código XML de los bloques en el workspace.
- */
- workspaceXml: {
- type: String,
- observer: '_onWorkspaceUpdate',
- notify: true
- },
-
- /*
- * `workspaceCanEdit` Indica si el usuario puede modificar el workspace.
- */
- workspaceCanEdit: {
- type: Boolean,
- },
-
- /*
- * `width` Ancho del elemento.
- */
- width: {
- type: Number,
- observer: '_fixSize'
- },
-
- /*
- * `height` Alto del elemento.
- */
- height: {
- type: Number,
- observer: '_fixSize'
- },
-
- /*
- * `media` path a media de blockly.
- */
- media: {
- type: String,
- value: "../bower_components/blockly-package/media/"
- },
- },
-
- _onPrimitiveProceduresChange: function() {
- if(typeof this.primitiveProcedures == 'string') {
- this.primitiveProcedures = JSON.parse(this.primitiveProcedures);
- return;
- }
- this._definePrimitiveProcedures();
- this._onUpdateToolbox();
- },
-
- _onPrimitiveFunctionsChange: function() {
- if(typeof this.primitiveFunctions == 'string') {
- this.primitiveFunctions = JSON.parse(this.primitiveFunctions);
- return;
- }
- this._definePrimitiveFunctions();
- this._onUpdateToolbox();
- },
-
- _onUpdateToolbox: function() {
- if(typeof this.toolbox == 'string') {
- if(this.toolbox == '') {
- this.toolbox = undefined;
- }
- else {
- this.toolbox = JSON.parse(this.toolbox);
- }
- return;
- }
- let toolbox = this._createToolbox();
- this.workspace.updateToolbox(toolbox);
- },
-
- /**
- * Retorna el arbol por defecto de bloques y categorias
- */
- _defaultToolboxTree : function() {
- let tree = []
- let toolboxDefault = this.$$('#toolbox');
- let toolboxDefaultLines = toolboxDefault.innerHTML.split("\n");
- let ignore_last = false;
- let parent = tree;
- let stack = [];
- for(var i in toolboxDefaultLines) {
- let line = toolboxDefaultLines[i];
- if(line.indexOf('<block') >= 0) {
- let m = line.match('type="([^"]*)"')
- if(m.length == 2) {
- parent.push({
- type: 'block',
- name: m[1],
- });
- }
- }
- else if(line.indexOf('<category') >= 0) {
- let m = line.match('name="([^"]*)"')
- if(m.length == 2 && (
- (m[1] == 'Procedimientos primitivos' && this.primitiveProcedures && this.primitiveProcedures.length > 0) ||
- (m[1] == 'Funciones primitivas' && this.primitiveFunctions && this.primitiveFunctions.length > 0) ||
- (m[1] != 'Procedimientos primitivos' && m[1] != 'Funciones primitivas'))) {
- parent.push({
- type: 'category',
- name: m[1],
- child: [],
- xml: line
- });
- stack.push(parent);
- parent = parent[parent.length - 1].child;
- if(m[1] == 'Procedimientos primitivos')
- {
- for(var i in this.primitiveProcedures) {
- parent.push({
- type: 'block',
- name: this.primitiveProcedures[i],
- });
- }
- }
- if(m[1] == 'Funciones primitivas')
- {
- for(var i in this.primitiveFunctions) {
- parent.push({
- type: 'block',
- name: this.primitiveFunctions[i],
- });
- }
- }
- }
- else {
- ignore_last = true;
- }
- }
- else if(line.indexOf('</category') >= 0) {
- if(ignore_last) {
- ignore_last = false;
- }
- else {
- parent = stack.pop();
- }
- }
- }
-
- return tree;
- },
-
- /**
- * Sirve para manejar los alias de los ID que vengan por la interfaz.
- * Recibe una lista que puede ser de IDs de bloques ó de categorías.
- * Devuelve una lista similar, intercambiando los alias por los ids "oficiales".
- * Mi idea acá también es que a futuro se haga la internacionalización de ids.
- */
- _homogenizeIDs: function(ids){
- return ids ? ids.map(id => Blockly.GobstonesLanguage.aliasForBlockID(id)) : [];
- },
-
- /**
- * Crea el toolbox a partir de la propiedad blocks, bloques visibles, y
- * customblocks.
- *
- * Funciona podando el toolbox por defecto.
- */
- _createToolbox: function() {
- let tree = this._defaultToolboxTree();
- let toolbox = [];
-
- if(this.toolbox) {
- let visibles = this._homogenizeIDs(this.toolbox.visible);
- let disabled = this._homogenizeIDs(this.toolbox.disabled);
- if(visibles.length > 0) {
- visibles = visibles.concat(disabled);
- if(this.primitiveProcedures) {
- visibles = visibles.concat(this.primitiveProcedures);
- }
- if(this.primitiveFunctions) {
- visibles = visibles.concat(this.primitiveFunctions);
- }
- }
-
- function filtrarToolboxTree(ttree) {
- let tbox = [];
- for(var i in ttree) {
- if(visibles.length == 0 || visibles.indexOf(ttree[i].name) >= 0) {
- tbox.push(ttree[i]);
- }
- else if(ttree[i].type == 'category') {
- let tmp = filtrarToolboxTree(ttree[i].child);
- if(tmp.length > 0) {
- ttree[i].child = tmp;
- tbox.push(ttree[i]);
- }
- }
- }
- return tbox;
- }
-
- toolbox = filtrarToolboxTree(tree);
-
- // Marcar disabled
- function marcarDisabled(tbox) {
- for(var i in tbox) {
- if(disabled.indexOf(tbox[i].name) >= 0) {
- tbox[i].disabled = true;
- }
- if(tbox[i].child) {
- marcarDisabled(tbox[i].child);
- }
- }
- }
- marcarDisabled(toolbox);
-
- }
- else {
- toolbox = tree;
- }
-
- return this._toolboxTreeToXML(toolbox);
- },
-
- _toolboxBlockXML: function(blockDef, forceDisabled) {
- let args = "";
- if(blockDef.disabled || forceDisabled) {
- args = 'disabled="true"';
- }
- let xml = `<block type="${blockDef.name}" ${args}></block>`;
- return xml;
- },
-
- _toolboxTreeToXML : function(toolbox, noXmlTags, disabled) {
- let xml = [];
- if(!noXmlTags) {
- xml.push('<xml>');
- }
- for(var i in toolbox) {
- if(toolbox[i].type == 'block') {
- xml.push(this._toolboxBlockXML(toolbox[i], disabled));
- }
- else if(toolbox[i].type == 'category') {
- xml.push(toolbox[i].xml);
- xml.push(this._toolboxTreeToXML(toolbox[i].child, true, toolbox[i].disabled || disabled));
- xml.push('</category>');
- }
- }
- if(!noXmlTags) {
- xml.push('</xml>');
- }
- return xml.join('\n');
- },
-
- /**
- * Define un bloque a partir de una definicion tipo Poner_En_
- */
- _definePrimitiveProcedure: function(def) {
- let parts = def.replace(/([A-Z])/g, " $1").toLowerCase();
- parts = parts[1].toUpperCase() + parts.substring(2);
- parts = parts.split("_");
-
- // Bloque
- Blockly.Blocks[def] = {
- init: function () {
- let argsIndex = 1;
- this.setColour(CommandColor);
- for(var i in parts) {
- if(i == (parts.length - 1)) {
- this.appendDummyInput().appendField(parts[i]);
- }
- else {
- this.appendValueInput('arg' + argsIndex).appendField(parts[i]);
- argsIndex++;
- }
- }
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setInputsInline(true);
- }
- };
- let argsList = [];
- for(var i=1;i<parts.length;i++) {
- argsList.push('arg' + i);
- }
- // Generator
- Blockly.GobstonesLanguage[def] = procBlockCodeGenerator(def, argsList);
- },
-
- _definePrimitiveProcedures: function () {
- if(!this.primitiveProcedures) {
- return;
- }
-
- for(var i in this.primitiveProcedures) {
- this._definePrimitiveProcedure(this.primitiveProcedures[i]);
- }
- },
-
-
- /**
- * Define un bloque a partir de una definicion de funcion primitiva tipo hayFlores_en_
- */
- _definePrimitiveFunction: function(def) {
- let parts = def.replace(/([A-Z])/g, " $1").toLowerCase();
- //let parts = def;
- //parts = parts[1].toUpperCase() + parts.substring(2);
- parts = parts.split("_");
-
- // Bloque
- Blockly.Blocks[def] = {
- init: function () {
- let argsIndex = 1;
- this.setColour(CommandColor);
- for(var i in parts) {
- if(i == (parts.length - 1)) {
- this.appendDummyInput().appendField(parts[i]);
- }
- else {
- this.appendValueInput('arg' + argsIndex).appendField(parts[i]);
- argsIndex++;
- }
- }
- this.setPreviousStatement(false);
- this.setNextStatement(false);
- this.setInputsInline(true);
- this.setOutput('var');
- }
- };
- let argsList = [];
- for(var i=1;i<parts.length;i++) {
- argsList.push('arg' + i);
- }
- // Generator
- Blockly.GobstonesLanguage[def] = functionBlockCodeGenerator(def, argsList);
- },
-
- _definePrimitiveFunctions: function () {
- if(!this.primitiveFunctions) {
- return;
- }
-
- for(var i in this.primitiveFunctions) {
- this._definePrimitiveFunction(this.primitiveFunctions[i]);
- }
- },
-
- _onBlocklyWorkspaceUpdate: function () {
- let xml = Blockly.Xml.workspaceToDom(this.workspace);
- this._blocklyWorkspaceXML = Blockly.Xml.domToText(xml);
- this.workspaceXml = this._blocklyWorkspaceXML;
- this._keepOnlyAProgram(xml);
- this._checkParameterBounds(xml);
- },
-
- _keepOnlyAProgram(xml) {
- const findProgram = (programType) => {
- const children = xml.children;
- const items = [];
- for (var i=0; i < children.length; i++)
- if (children[i].getAttribute("type") === programType)
- items.push(children[i]);
- return items;
- };
-
- const block = (blockXml) => this.workspace.getBlockById(blockXml.getAttribute("id"));
-
- const programXmls = findProgram("Program");
- const interactiveProgramXmls = findProgram("InteractiveProgram");
-
- // repeated blocks
- if (programXmls.length > 1) {
- // (en vez de .undo() se podría hacerle .dispose() al bloque)
-
- if (block(programXmls[0]).$timestamp > block(programXmls[1]).$timestamp) {
- // delete programXmls[0]
- this.workspace.undo();
- programXmls.shift();
- } else {
- // delete programXmls[1]
- this.workspace.undo();
- programXmls.pop();
- }
- }
- if (interactiveProgramXmls.length > 1) {
- if (block(interactiveProgramXmls[0]).$timestamp > block(interactiveProgramXmls[1]).$timestamp) {
- // delete interactiveProgramXmls[0]
- this.workspace.undo();
- interactiveProgramXmls.shift();
- } else {
- // delete interactiveProgramXmls[1]
- this.workspace.undo();
- interactiveProgramXmls.pop();
- }
- }
-
- const program = programXmls[0] && block(programXmls[0]);
- const interactiveProgram = interactiveProgramXmls[0] && block(interactiveProgramXmls[0]);
-
- if (program && interactiveProgram) {
- program.setDeletable(true);
- interactiveProgram.setDeletable(true);
-
- const important = program.$timestamp > interactiveProgram.$timestamp ? program : interactiveProgram;
- const unimportant = program.$timestamp > interactiveProgram.$timestamp ? interactiveProgram : program;
-
- const twoActivePrograms = !program.disabled && !interactiveProgram.disabled;
- const twoDisabledPrograms = program.disabled && interactiveProgram.disabled;
-
- if (twoActivePrograms || twoDisabledPrograms) {
- important.setDisabledAndUpdateTimestamp(false);
- unimportant.setDisabledAndUpdateTimestamp(true);
- }
- } else if (program && !interactiveProgram) {
- program.setDisabledAndUpdateTimestamp(false);
- program.setDeletable(false);
- } else if (interactiveProgram && !program) {
- interactiveProgram.setDisabledAndUpdateTimestamp(false);
- interactiveProgram.setDeletable(false);
- }
- },
-
- _checkParameterBounds(xml) {
- const blocks = this.workspace.getAllBlocks();
- for (block of blocks) {
- if (block.type === "variables_get" && block.$parent !== null) {
- const parentBlock = this.workspace.getBlockById(block.$parent);
- const varField = block.getField("VAR");
-
- if (
- parentBlock &&
- (
- !parentBlock.type.startsWith("procedures") ||
- parentBlock.arguments_.some(it => it === varField.getValue())
- )
- ) {
- var parent = block;
- while ((parent = parent.getSurroundParent()) !== null) {
- if (parent.id === block.$parent) break;
- }
-
- block.setDisabled(parent === null);
- } else {
- block.dispose();
- }
- }
- }
- },
-
- _onWorkspaceUpdate: function () {
- if(this.workspaceXml != this._blocklyWorkspaceXML) {
- this.workspace.clear();
- let dom = Blockly.Xml.textToDom(this.workspaceXml);
- try {
- Blockly.Xml.domToWorkspace(dom, this.workspace);
- } catch (e) {
- if(e.message.includes("nextConnection is null")){
- throw {
- name: "BlockTypeError",
- message: "There is at least one block type declared in the 'type' property of the block tag of the xml, which is incorrect or belongs to a block that is not in the Gobstones Language. Maybe you have forgotten any primitiveProcedure?",
- toString: function(){ return this.name + ': ' + this.message; }
- };
- } else {
- throw e;
- }
- }
- }
- },
-
- /**
- * Reinicializa el workspace, dejando solo el bloque programa
- */
- resetWorkspace: function() {
- let xmlInicial = '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="Program" x="30" y="30"></block></xml>';
- this.workspaceXml = xmlInicial;
- },
-
- /**
- * Retorna una lista de objetos, uno por cada categoría y bloque.
- * Representa la jerarquía del toolbox.
- *
- * Para los bloques se indica:
- *
- * * `blockXMLID`: El ID del bloque que estará en el XML exportado por el método
- * `generateCode`.
- * * `blockAliases`: La lista de aliases que refieren a ese bloque, y que pueden
- * ser usados para definir el toolbox mediante la propiedad `toolbox`.
- *
- * Para las categorías se indica:
- *
- * * `categoryName`: El nombre de la categoría que es visible en el toolbox.
- * * `categoryAliases`: La lista de aliases que refieren a esa categoría, y que pueden
- * ser usados para definir el toolbox mediante la propiedad `toolbox`.
- * * `categoryContents`: La lista de objetos categoría/bloque dentro de esta categoría.
- *
- * @return {Array} Los objetos.
- */
- validToolboxIDs: function(){
- return this._validToolboxIDsFrom(this._defaultToolboxTree());
- },
-
- /**
- * Recursively defined. Gets a list of user-readable objects describing the
- * toolboxTree hierarchy and aliases.
- */
- _validToolboxIDsFrom: function(toolboxTree) {
- var myThis = this;
- return this._mapToolboxTree(toolboxTree, function(toolboxElement){
- if(toolboxElement.type === "category"){
- return {
- categoryName: toolboxElement.name,
- categoryAliases: Blockly.GobstonesLanguage.aliasesFor(toolboxElement.name),
- categoryContents: myThis._validToolboxIDsFrom(toolboxElement.child)
- };
- } else if(toolboxElement.type === "block") {
- return {
- blockXMLID: toolboxElement.name,
- blockAliases: Blockly.GobstonesLanguage.aliasesFor(toolboxElement.name),
- };
- } else {
- /*Do nothing for other types*/
- }
- });
- },
-
- _mapToolboxTree: function(toolboxTree, f){
- var resultingList = [];
- for (var i in toolboxTree){
- resultingList.push(f(toolboxTree[i]));
- }
- return resultingList;
- },
-
- /**
- * Reinicializa el estado del componente,
- */
- cleanup: function() {
- this.primitiveProcedures = [];
- this.primitiveFunctions = [];
- this.toolbox = {};
- this.resetWorkspace();
- },
-
- // Element Lifecycle
- ready: function() {
- },
-
- attached: function() {
- this._definePrimitiveProcedures();
- this._definePrimitiveFunctions();
- // create workspace
- var blocklyDiv = this.$$('#blocklyDiv');
- this.blocklyDiv = blocklyDiv;
- this._fixSize();
- this.workspace = Blockly.inject(blocklyDiv, {
- toolbox: this._createToolbox(),
- media: this.get("media"),
- toolboxPosition: "start",
- scrollbars: true,
- horizontalLayout: false,
- collapse: true,
- css: true,
- zoom: {
- controls: true,
- wheel: true
- }
- });
- var _this = this;
- this.workspace.addChangeListener(function (a, b, c) {
- Blockly.Events.disableOrphans(a, b, c);
- _this._onBlocklyWorkspaceUpdate();
- });
- this.resetWorkspace()
- this._onresize();
- if(window.jQuery !== undefined) {
- $(window).resize(() => this._onresize());
- };
-
- Blockly.ErrorInforming.addToWorkspace(this.workspace);
- },
-
- _onresize() {
- Blockly.svgResize(this.workspace);
- },
-
- _fixSize() {
- let style = "";
- if(this.width && this.height) {
- style += "width:"+ this.width + "px;";
- style += "height:"+ this.height + "px;";
- this.blocklyDiv.style = style;
- this._onresize();
- }
- },
-
- detached: function() {
- // The analog to `attached`, `detached` fires when the element has been
- // removed from a document.
- //
- // Use this to clean up anything you did in `attached`.
- },
-
- // Element Behavior
-
- /**
- * Generate gobstones code from the blocks in the workspace.
- * It removes all highlighting and errors if already present.
- *
- * There is an additional option which if provided adds pragma BEGIN/END REGION
- * to the output.
- * * `element.generateCode( {withRegions: true} );`
- * @return {string} The code.
- */
- generateCode: function(options = {}) {
- this.workspace.highlightBlock(); // No parameters means reset highlighting
- this.workspace.removeBlockErrors();
- Blockly.GobstonesLanguage.shouldAddRegionPragma = options.withRegions;
- return Blockly.GobstonesLanguage.workspaceToCode(this.workspace);
- },
-
- /**
- * Append blocks to workspace. Se agrega al contenido actual del workspace
- * los bloques especificados en el xml (descartando el program si es que viene).
- *
- */
- appendBlocksToWorkspace: function(xml) {
- },
-
- /**
- * Highlight a given block by its ID. Se hace highlight del bloque indicado.
- *
- */
- highlightBlock: function(blockId) {
- this.workspace.highlightBlock(blockId)
- },
-
- /**
- * Highlight a given block, telling the user it has an error
- *
- * `blockId` is the block ID where the error should appear
- *
- * `errorKind` is either a string with the description or an object with a kind of error.
- * Next are examples with the existent error kinds.
- *
- * Examples:
- * * `element.showBlockError('a1s2', 'Hey, here is an error')`
- * * `element.showBlockError('a1s2', { kind: 'INCOMPLETE_ERROR'} )`
- * * `element.showBlockError('a1s2', { kind: 'TYPE_ERROR', expectedType:'string', actualType: 'boolean' })`
- * * `element.showBlockError('a1s2', { kind: 'PRECONDITION_ERROR', description: "Susan can't move right" })`
- */
- showBlockError: function(blockId, errorKind) {
- this.workspace.showBlockError(blockId, errorKind);
- },
- });
- </script>
-
<script>// Do not edit this file; automatically generated by build.py.
'use strict';
var COMPILED=!0,goog=goog||{};goog.global=this;goog.isDef=function(a){return void 0!==a};goog.isString=function(a){return"string"==typeof a};goog.isBoolean=function(a){return"boolean"==typeof a};goog.isNumber=function(a){return"number"==typeof 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]!==Object.prototype[d]?c[d]:c[d]={}};
goog.define=function(a,b){var c=b;COMPILED||(goog.global.CLOSURE_UNCOMPILED_DEFINES&&void 0===goog.global.CLOSURE_UNCOMPILED_DEFINES.nodeType&&Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_UNCOMPILED_DEFINES,a)?c=goog.global.CLOSURE_UNCOMPILED_DEFINES[a]:goog.global.CLOSURE_DEFINES&&void 0===goog.global.CLOSURE_DEFINES.nodeType&&Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_DEFINES,a)&&(c=goog.global.CLOSURE_DEFINES[a]));goog.exportPath_(a,c)};goog.DEBUG=!1;goog.LOCALE="en";
@@ -4003,47 +3306,267 @@
* To add a new function type add this to your code:
* Blockly.Blocks['procedures_ifreturn'].FUNCTION_TYPES.push('custom_func');
*/
FUNCTION_TYPES: ['procedures_defnoreturn', 'procedures_defreturn']
};</script>
- <script>function initProcedsBlockly(customStatementType) {
+ <script>var PLUS = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAQAAAD2e2DtAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAHdElNRQfhDAUCCjFLV0NqAAAC60lEQVR42u3dQW7aQABA0Wl7MMjJICeDnIwuqm4qVQrYjMH/Pa/jsfFnTJDwjAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBUP7Y+gClOY4zznX9zHmN8bn3gLHcYtwXbYevDZ5nLost/G7dx2foUeNzyyy+BN7Zs8ncjeHvrvP/NAW9qvff/rueAn1sfwNMcX3hvL2S/3wPcVt7fTl+p/c4AfIsA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4mYHcBinVRdz+v+2tjlHfdrv8lRjHFZcyG3P22VmBPOWQrrsd+WtJ7iOjzkDzQrA5b/XpATmBHAY1ynj7MtxfD1/kDkBrP+RrGHC1ZnxX8Bpwhj7NOGV8z1A3IxbgBvA455+fcwAcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcT9mjLKcevTfFPn5/860AwQ58ehr2wnPw51C3jMccYgcwL48nyAu11nPB3AI2Je1bRHxMz7EPgxjuaBb7mO46zLP3MG+OMwjuM8ecx3cp419f81O4B51v7PY6evlO8B4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQN2fp2G0cV9zXhEVct7HfGeD6wntjisu4rbRdtj4VHnFYLYDD1qfCY9aZA7z/39jyBFz+N7fsRrD7yX+n62H+4zTG3QvWnscYn1sfOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALvzG8Ijm7EmMQYoAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTEyLTA1VDAyOjEwOjQ5LTA1OjAwJa2zowAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMi0wNVQwMjoxMDo0OS0wNTowMFTwCx8AAAAASUVORK5CYII=";
+var MINUS = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAQAAAD2e2DtAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAHdElNRQfhDAUCCi+xWH4JAAABcUlEQVR42u3c7ZGCMBSG0etuYcTKls7AyrSEVWd4+bjnUECMeSbhD6kCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBzu4XHm2rUvPekD2yutR57/4itTLXU0/Pvs9SUW5TcDrDUyE3r9Na6ZwZKBWD5PxVKIBPAVGtknGsZibeBTADPyCjXE1idn8A0/gJjXFPgn0sEwIEljgAHwPc2Xx87QHMCaE4AzQmgOQE0J4DmBNCcAJoTQHMCaE4AzQmgOQE0J4DmBNCcAJoTQHMCaE4AzQmgOQE0J4DmBNDcb2SUsfc0T2re/utAO0BzPg49sot8HOoI+M5IDJIJ4OF+gI+F7gpyRcwxxa6Iyb0E3mvYB96y1kgtv2vijubS18QBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAWXq7xrTQhKAi3AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTEyLTA1VDAyOjEwOjQ3LTA1OjAwdZLI/gAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMi0wNVQwMjoxMDo0Ny0wNTowMATPcEIAAAAASUVORK5CYII=";
+var WAND = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB41BMVEUAAACrhwmsiArFmQTtxjftxjjOnwThrQrirQvOmQToxETpxkfUnwS2hQLYohPYoxW/igOXagHKjgPNkQXUnxzfsjngtDzVoR/NkgbLjwOkcwHIiwbov1PJjQnAggPiskDkulXBgwO8fATnuEjpwVq9fgeCUwC3eAW4eAOLWQCQVwDGjBLIiQKWWwC9fgHBhAFlOgBhNQDCggHHigFwPQBoNQDIhgHDqADBqQDOkAF4PgBjLwDIhwLFjAGUUgCJRACIQgCSTwDCiwHNkAJ2OABSJgCcWgqPQgCCOgB8OACPQgCcWgthLQD06Xj16nn47YT14Hv35YD14Xzx1G744Hv44Xvy1m/00nX31nj32Hb323b43Xb43nb43Hb32nb313f103bpwlz2zG/30W/31W/42W/422/43W/43G/42m/412/302/3z2/3zmj402j42Gj43Gj53mj53Wj42mj41mj30WjswEr401/52V/53V/54F/64V/531/522H4zErtsir4yD350j352Tn63TL63Cj61Bn4xQL3twD3tgD4xgD61QD73wD74QD72wD5zgD3vgD3uAD5ywD73AD86wD97wD85AD61AD4wgD2tQD5yQD01gDz3AD60wD4vwDsrgHqswH////vUbxMAAAATXRSTlMABAQHwMIIen0c8fIeCa6yCgI5e7Hp67WAPwOD/otJ/PxLev7+fAKSlQIG3OEIy9IBA+HpBAj1/v77DAr58ZMtKo3u/Q4BcGcTEGJzAcqMNfMAAAABYktHRKBe076gAAAAB3RJTUUH4QwFFBQHSAc27gAAANRJREFUGNNjYIAARiYGFMDMwsqGIsDu68eBzOfk8vfn5kHwefkCAgOD+AXAHEEhYRHR4JDQ0JAwMXEJSSkGaZnwiMio6JiY6Ni4+IREWQY5+aTklNS09PSMzKzsHAVFBgYl5dy8/ILCwqLiklIVVaAhaupl5RWVVdU1tXX1GpogY7W0Gxqbmlta29p1dCG26nV0dnX39Pb16xuA+YZGEyZOmjxl6rTpxiZgAVOzGTNnmVvMnjPX0gosYG0zz9bO3sHRab6zC1jA1c3dA0R7enn7MDAAABs9NIkRbjxSAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTEyLTA1VDIwOjIwOjA3LTA1OjAw6QjR4wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMi0wNVQyMDoyMDowNy0wNTowMJhVaV8AAAAASUVORK5CYII=";
+
+function initProcedsBlockly(customStatementType) {
Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = 'Describe el procedimiento...';
Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE = "Hacer algo";
Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE = "Definir";
Blockly.Msg.PROCEDURES_DEFNORETURN_NOPARAMS = "";
Blockly.Msg.PROCEDURES_DEFRETURN_NOPARAMS = "";
Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT = 'Describe la función...';
Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE = "devolver algo";
Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = "Definir";
- Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "con:";
+ Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "con";
Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP = "Crea un procedimiento.";
Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP = "Crea una función.";
Blockly.Msg.PROCEDURES_ADD_PARAMETER = "Agregar parámetro";
Blockly.Msg.PROCEDURES_ADD_PARAMETER_PROMPT = "Ingresa el nombre del parámetro";
Blockly.Msg.PROCEDURES_REMOVE_PARAMETER = "Quitar parámetro";
+ Blockly.Msg.PROCEDURES_PARAMETER = "parámetro";
- // --------------------------------
- // [!] Adding defaultName parameter
- // --------------------------------
+ // -------------------------------------
+ // [!] Fixing FieldImage's click handler
+ // -------------------------------------
- var makeProcedureInit = function(withReturn, withStatements = true, withParametersMutator = false, defaultName, title, comment, tooltip, helpUrl) {
+ Blockly.FieldImage.prototype.init = function() {
+ if (this.fieldGroup_) {
+ // Image has already been initialized once.
+ return;
+ }
+ // Build the DOM.
+ /** @type {SVGElement} */
+ this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
+ if (!this.visible_) {
+ this.fieldGroup_.style.display = 'none';
+ }
+ /** @type {SVGElement} */
+ this.imageElement_ = Blockly.utils.createSvgElement(
+ 'image',
+ {
+ 'height': this.height_ + 'px',
+ 'width': this.width_ + 'px'
+ },
+ this.fieldGroup_);
+ this.setValue(this.src_);
+ this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
+
+ // Configure the field to be transparent with respect to tooltips.
+ this.setTooltip(this.sourceBlock_);
+ Blockly.Tooltip.bindMouseEvents(this.imageElement_);
+
+ var self = this;
+
+ if (this.clickHandler_) { // [!]
+ this.imageElement_.addEventListener("mousedown", function(event) {
+ event.stopPropagation();
+ event.preventDefault();
+
+ if (event.button === 0) {
+ self.clickHandler_();
+ }
+ });
+ }
+ };
+
+ // [!] Modifying callbackFactory to return the created block
+ Blockly.ContextMenu.callbackFactory = function(block, xml) {
return function() {
- var nameField = new Blockly.FieldTextInput(defaultName, // [!]
- Blockly.Procedures.rename);
+ Blockly.Events.disable();
+ try {
+ var newBlock = Blockly.Xml.domToBlock(xml, block.workspace);
+ // Move the new block next to the old block.
+ var xy = block.getRelativeToSurfaceXY();
+ if (block.RTL) {
+ xy.x -= Blockly.SNAP_RADIUS;
+ } else {
+ xy.x += Blockly.SNAP_RADIUS;
+ }
+ xy.y += Blockly.SNAP_RADIUS * 2;
+ newBlock.moveBy(xy.x, xy.y);
+ } finally {
+ Blockly.Events.enable();
+ }
+ if (Blockly.Events.isEnabled() && !newBlock.isShadow()) {
+ Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock));
+ }
+ newBlock.select();
+
+ return newBlock; // [!]
+ };
+ };
+
+ // --------------------------------------
+ // [!] Adding custom procedure parameters
+ // --------------------------------------
+
+ var createParameterCaller = function(procedureBlock, name) {
+ var xmlField = goog.dom.createDom('field', null, name);
+ xmlField.setAttribute('name', 'VAR');
+ var xmlBlock = goog.dom.createDom('block', null, xmlField);
+ xmlBlock.setAttribute('type', 'variables_get');
+
+ var callback = Blockly.ContextMenu.callbackFactory(procedureBlock, xmlBlock);
+
+ return function() {
+ var block = callback();
+ block.$parent = procedureBlock.id;
+ };
+ };
+
+ var getAvailableName = function(self, name) {
+ var result = name;
+ while (self.arguments_.some(it => it === result))
+ result += "_";
+ return result;
+ }
+
+ var addParameter = function(self, index, name) {
+ var i = index === undefined ? self.arguments_.length : index;
+ var tmpName = name === undefined ? Blockly.Msg.PROCEDURES_PARAMETER + " " + (i + 1) : name;
+ var name = index === undefined ? getAvailableName(self, tmpName) : tmpName;
+ var id = "INPUTARG" + i;
+
+ if (index === undefined) {
+ self.arguments_.push(name);
+ self.updateParams_();
+
+ var blocks = self.workspace.getAllBlocks();
+ for (block of blocks)
+ if (block.type === self.callType_ && block.getProcedureCall() === self.getProcedureDef()[0]) {
+ block.arguments_.push(name);
+ block.updateShape_();
+ }
+ }
+
+ var createCallButton = new Blockly.FieldImage(
+ WAND,
+ 16,
+ 16,
+ Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name),
+ function() {
+ createParameterCaller(self, self.arguments_[i])();
+ }
+ );
+
+ var removeParameterButton = new Blockly.FieldImage(
+ MINUS,
+ 16,
+ 16,
+ Blockly.Msg.PROCEDURES_REMOVE_PARAMETER,
+ function() {
+ for (var j = 0; j < self.arguments_.length; j++)
+ self.removeInput("INPUTARG" + j);
+ self.arguments_.splice(i, 1);
+ self.arguments_.forEach(function(name, i) {
+ addParameter(this, i, name, true);
+ }.bind(self));
+
+ var blocks = self.workspace.getAllBlocks();
+ for (block of blocks)
+ if (block.type === self.callType_ && block.getProcedureCall() === self.getProcedureDef()[0]) {
+ block.arguments_.splice(i, 1);
+ block.updateShape_();
+ }
+ }
+ );
+
+ var nameField = new Blockly.FieldTextInput(name, function(newName) {
+ var oldName = self.arguments_[i];
+
+ if (oldName !== newName)
+ newName = getAvailableName(self, newName);
+
+ self.arguments_[i] = newName;
+
+ var blocks = self.workspace.getAllBlocks();
+ for (block of blocks) {
+ if (block.type === self.callType_ && block.getProcedureCall() === self.getProcedureDef()[0]) {
+ block.arguments_ = block.arguments_.map(function(it) {
+ return it === oldName ? newName : it;
+ });
+ block.updateShape_();
+ }
+
+ if (block.type === "variables_get" && block.$parent === self.id) {
+ var varField = block.getField("VAR");
+ if (varField.getValue() === oldName) {
+ varField.setValue(newName);
+ }
+ }
+ }
+
+ return newName;
+ });
+
+ self
+ .appendDummyInput(id)
+ .appendField(Blockly.Msg.PROCEDURES_BEFORE_PARAMS)
+ .appendField(nameField, 'ARG' + i)
+ .appendField(createCallButton)
+ .appendField(removeParameterButton);
+
+ self.moveInputBefore(id, 'STACK');
+ };
+
+ var makeProcedureDomToMutation = function() {
+ return function(xmlElement) {
+ this.arguments_ = [];
+ for (var i = 0, childNode; childNode = xmlElement.childNodes[i]; i++) {
+ if (childNode.nodeName.toLowerCase() == 'arg') {
+ this.arguments_.push(childNode.getAttribute('name'));
+ }
+ }
+ this.updateParams_();
+ Blockly.Procedures.mutateCallers(this);
+
+ // Show or hide the statement input.
+ this.setStatements_(xmlElement.getAttribute('statements') !== 'false');
+
+ this.arguments_.forEach(function(name, i) { // [!]
+ addParameter(this, i, name);
+ }.bind(this));
+ };
+ }
+
+ var makeProcedureInit = function(withReturn, withStatements = true, withParameters = false, defaultName, title, comment, tooltip, helpUrl) {
+ return function() {
+ var defaultLegalName = Blockly.Procedures.findLegalName(defaultName, this);
+ var nameField = new Blockly.FieldTextInput(defaultLegalName, Blockly.Procedures.rename);
nameField.setSpellcheck(false);
- this.appendDummyInput()
+
+ var self = this;
+
+ // [!]
+ var addParameterButton = new Blockly.FieldImage(
+ PLUS,
+ 16,
+ 16,
+ Blockly.Msg.PROCEDURES_ADD_PARAMETER,
+ function() { addParameter(self); }
+ );
+
+ var input = this.appendDummyInput()
.appendField(title)
.appendField(nameField, 'NAME')
.appendField('', 'PARAMS');
+ if (withParameters)
+ input.appendField(addParameterButton);
+
if (withReturn)
this.appendValueInput('RETURN')
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
- if (withParametersMutator)
- this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
+ // if (withParametersMutator)
+ // this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
if ((this.workspace.options.comments ||
(this.workspace.options.parentWorkspace &&
this.workspace.options.parentWorkspace.options.comments)) &&
comment) {
@@ -4055,60 +3578,57 @@
this.setHelpUrl(helpUrl);
this.arguments_ = [];
this.setStatements_(withStatements);
this.statementConnection_ = null;
- if (!withParametersMutator) this.updateParams_();
+ // if (!withParametersMutator) this.updateParams_();
};
};
- // ---------------------------------------------------
- // [!] Using .unshift instead of .push for new options
- // ---------------------------------------------------
+ // -----------------------
+ // [!] Custom context menu
+ // -----------------------
var makeProcedureCustomMenu = function(withParametersOptions = true) {
return function(options) {
// Add options to create getters for each parameter.
if (!this.isCollapsed()) {
for (var i = this.arguments_.length - 1; i >= 0; i--) {
var option = {enabled: true};
var name = this.arguments_[i];
option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name);
- var xmlField = goog.dom.createDom('field', null, name);
- xmlField.setAttribute('name', 'VAR');
- var xmlBlock = goog.dom.createDom('block', null, xmlField);
- xmlBlock.setAttribute('type', 'variables_get');
- option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
+ option.callback = createParameterCaller(this, name);
+
options.unshift(option);
}
}
// [!]
- if (withParametersOptions) {
- options.unshift({
- enabled: this.arguments_.length > 0,
- text: Blockly.Msg.PROCEDURES_REMOVE_PARAMETER,
- callback: function() {
- this.arguments_.pop();
- this.updateParams_();
- }.bind(this)
- });
+ // if (withParametersOptions) {
+ // options.unshift({
+ // enabled: this.arguments_.length > 0,
+ // text: Blockly.Msg.PROCEDURES_REMOVE_PARAMETER,
+ // callback: function() {
+ // this.arguments_.pop();
+ // this.updateParams_();
+ // }.bind(this)
+ // });
- options.unshift({
- enabled: true,
- text: Blockly.Msg.PROCEDURES_ADD_PARAMETER,
- callback: function() {
- var name = "";
- while (name === "") // Rompe encapsulamiento
- name = Blockly.Blocks['procedures_mutatorarg'].validator_(prompt(Blockly.Msg.PROCEDURES_ADD_PARAMETER_PROMPT));
- if (name === null) return;
+ // options.unshift({
+ // enabled: true,
+ // text: Blockly.Msg.PROCEDURES_ADD_PARAMETER,
+ // callback: function() {
+ // var name = "";
+ // while (name === "") // Rompe encapsulamiento
+ // name = Blockly.Blocks['procedures_mutatorarg'].validator_(prompt(Blockly.Msg.PROCEDURES_ADD_PARAMETER_PROMPT));
+ // if (name === null) return;
- this.arguments_.push(name);
- this.updateParams_();
- }.bind(this)
- });
- }
+ // this.arguments_.push(name);
+ // this.updateParams_();
+ // }.bind(this)
+ // });
+ // }
// Add option to create caller.
var option = {enabled: true};
var name = this.getFieldValue('NAME');
option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name);
@@ -4142,50 +3662,39 @@
nextStatement: customStatementType
});
}
};
- // -----------------------------------------
- // [!] Using PROCEDURES_BEFORE_PARAMS always
- // -----------------------------------------
+ // -------------------------------
+ // [!] Patching default procedures
+ // -------------------------------
- var makeUpdateParams = function() {
- return function() {
- var paramsString = this.arguments_.length > 0
- ? Blockly.Msg.PROCEDURES_BEFORE_PARAMS + ' ' + this.arguments_.join(", ")
- : Blockly.Msg.PROCEDURES_BEFORE_PARAMS; // [!]
+ var makeUpdateParams = function() { return function() { }; };
- Blockly.Events.disable();
- try {
- this.setFieldValue(paramsString, 'PARAMS');
- } finally {
- Blockly.Events.enable();
- }
- };
- };
-
Blockly.Blocks['procedures_defnoreturn'].init = makeProcedureInit(
- false, true, false,
+ false, true, true,
Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE,
Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE,
Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT,
Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP,
Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL
);
Blockly.Blocks['procedures_defnoreturn'].customContextMenu = makeProcedureCustomMenu();
Blockly.Blocks['procedures_defnoreturn'].updateParams_ = makeUpdateParams();
+ Blockly.Blocks['procedures_defnoreturn'].domToMutation = makeProcedureDomToMutation();
Blockly.Blocks['procedures_defreturn'].init = makeProcedureInit(
- true, true, false,
+ true, true, true,
Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE,
Blockly.Msg.PROCEDURES_DEFRETURN_TITLE,
Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT,
Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP,
Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL
);
Blockly.Blocks['procedures_defreturn'].customContextMenu = makeProcedureCustomMenu();
Blockly.Blocks['procedures_defreturn'].updateParams_ = makeUpdateParams();
+ Blockly.Blocks['procedures_defreturn'].domToMutation = makeProcedureDomToMutation();
// -------------------------------------------------
// [!] Adding a new type of procedure with no params
// -------------------------------------------------
@@ -4197,20 +3706,13 @@
Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT,
Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP,
Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL
),
setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_,
- updateParams_: function() {
- Blockly.Events.disable();
- try {
- this.setFieldValue(Blockly.Msg.PROCEDURES_DEFNORETURN_NOPARAMS, 'PARAMS');
- } finally {
- Blockly.Events.enable();
- }
- },
+ updateParams_: makeUpdateParams(),
mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom,
- domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation,
+ domToMutation: makeProcedureDomToMutation(),
decompose: Blockly.Blocks['procedures_defnoreturn'].decompose,
compose: Blockly.Blocks['procedures_defnoreturn'].compose,
getProcedureDef: Blockly.Blocks['procedures_defnoreturn'].getProcedureDef,
getVars: Blockly.Blocks['procedures_defnoreturn'].getVars,
renameVar: Blockly.Blocks['procedures_defnoreturn'].renameVar,
@@ -4236,21 +3738,21 @@
// [!] Adding a new type of function only with return value (and params)
// ---------------------------------------------------------------------
Blockly.Blocks['procedures_defreturnsimplewithparams'] = {
init: makeProcedureInit(
- true, false, false,
+ true, false, true,
Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE,
Blockly.Msg.PROCEDURES_DEFRETURN_TITLE,
Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT,
Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP,
Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL
),
setStatements_: Blockly.Blocks['procedures_defreturn'].setStatements_,
- updateParams_: Blockly.Blocks['procedures_defreturn'].updateParams_,
+ updateParams_: makeUpdateParams(),
mutationToDom: Blockly.Blocks['procedures_defreturn'].mutationToDom,
- domToMutation: Blockly.Blocks['procedures_defreturn'].domToMutation,
+ domToMutation: makeProcedureDomToMutation(),
decompose: Blockly.Blocks['procedures_defreturn'].decompose,
compose: Blockly.Blocks['procedures_defreturn'].compose,
getProcedureDef: Blockly.Blocks['procedures_defreturn'].getProcedureDef,
getVars: Blockly.Blocks['procedures_defreturn'].getVars,
renameVar: Blockly.Blocks['procedures_defreturn'].renameVar,
@@ -4284,20 +3786,13 @@
Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT,
Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP,
Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL
),
setStatements_: Blockly.Blocks['procedures_defreturn'].setStatements_,
- updateParams_: function() {
- Blockly.Events.disable();
- try {
- this.setFieldValue(Blockly.Msg.PROCEDURES_DEFRETURN_NOPARAMS, 'PARAMS');
- } finally {
- Blockly.Events.enable();
- }
- },
+ updateParams_: makeUpdateParams(),
mutationToDom: Blockly.Blocks['procedures_defreturn'].mutationToDom,
- domToMutation: Blockly.Blocks['procedures_defreturn'].domToMutation,
+ domToMutation: makeProcedureDomToMutation(),
decompose: Blockly.Blocks['procedures_defreturn'].decompose,
compose: Blockly.Blocks['procedures_defreturn'].compose,
getProcedureDef: Blockly.Blocks['procedures_defreturn'].getProcedureDef,
getVars: Blockly.Blocks['procedures_defreturn'].getVars,
renameVar: Blockly.Blocks['procedures_defreturn'].renameVar,
@@ -4328,11 +3823,11 @@
function populateProcedures(procedureList) { // [!]
for (var i = 0; i < procedureList.length; i++) {
var name = procedureList[i][0];
var args = procedureList[i][1];
- const templateName = Blockly.Procedures.getDefinition(name, workspace).callType_; // [!]
+ var templateName = Blockly.Procedures.getDefinition(name, workspace).callType_; // [!]
// <block type="procedures_callnoreturn" gap="16">
// <mutation name="do something">
// <arg name="x"></arg>
// </mutation>
// </block>
@@ -4361,11 +3856,11 @@
function populateProcedures(procedureList) { // [!]
for (var i = 0; i < procedureList.length; i++) {
var name = procedureList[i][0];
var args = procedureList[i][1];
- const templateName = Blockly.Procedures.getDefinition(name, workspace).callType_; // [!]
+ var templateName = Blockly.Procedures.getDefinition(name, workspace).callType_; // [!]
// <block type="procedures_callnoreturn" gap="16">
// <mutation name="do something">
// <arg name="x"></arg>
// </mutation>
// </block>
@@ -4491,15 +3986,24 @@
<script>initProcedsBlockly("Statement");</script>
<script>var MINUS = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAQAAAD2e2DtAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAHdElNRQfhDAUCCi+xWH4JAAABcUlEQVR42u3c7ZGCMBSG0etuYcTKls7AyrSEVWd4+bjnUECMeSbhD6kCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBzu4XHm2rUvPekD2yutR57/4itTLXU0/Pvs9SUW5TcDrDUyE3r9Na6ZwZKBWD5PxVKIBPAVGtknGsZibeBTADPyCjXE1idn8A0/gJjXFPgn0sEwIEljgAHwPc2Xx87QHMCaE4AzQmgOQE0J4DmBNCcAJoTQHMCaE4AzQmgOQE0J4DmBNCcAJoTQHMCaE4AzQmgOQE0J4DmBNDcb2SUsfc0T2re/utAO0BzPg49sot8HOoI+M5IDJIJ4OF+gI+F7gpyRcwxxa6Iyb0E3mvYB96y1kgtv2vijubS18QBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAWXq7xrTQhKAi3AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTEyLTA1VDAyOjEwOjQ3LTA1OjAwdZLI/gAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMi0wNVQwMjoxMDo0Ny0wNTowMATPcEIAAAAASUVORK5CYII=";
/* global Blockly */
-var ControlColor = 60;
-var CommandColor = 200;
-var ExpressionColor = 180;
-var BindingColor = 30;
-var CompletarColor = 0;
+Blockly.GOBSTONES_COLORS = {
+ globalHsvSaturation: Blockly.HSV_SATURATION,
+ globalHsvValue: Blockly.HSV_VALUE,
+ primitiveCommand: 80,
+ assignation: 230,
+ controlStructure: 60,
+ literalExpression: 180,
+ expression: 260,
+ program: 100,
+ interactiveProgram: 30,
+ interactiveBinding: 50,
+ procedure: Blockly.Blocks.procedures.HUE,
+ complete: 0,
+};
/**
* Create the svg representation of a block and render
* @name {!string} name of the parameter.
* @this Blockly.Block
@@ -4530,11 +4034,11 @@
"name": "program",
"check": ["Statement"]
}
]
})
- this.setColour(100);
+ this.setColour(Blockly.GOBSTONES_COLORS.program);
this.setDeletable(true);
this.setEditable(true);
this.setMovable(true);
},
@@ -4574,11 +4078,11 @@
"name": "interactiveprogram",
"check": ["InteractiveBinding"]
}
]
});
- this.setColour(BindingColor);
+ this.setColour(Blockly.GOBSTONES_COLORS.interactiveProgram);
this.setDeletable(true);
this.setEditable(true);
this.setMovable(true);
},
@@ -4716,11 +4220,11 @@
type: "field_dropdown",
name: "InteractiveBindingDropdownKey",
options: keys.map(it => [it.name, it.code]),
}
],
- colour: BindingColor,
+ colour: Blockly.GOBSTONES_COLORS.interactiveBinding,
tooltip: "Escoger una entrada",
});
this.appendStatementInput('block').setCheck(["Statement"]);
},
@@ -4817,11 +4321,11 @@
type: "Statement",
previousStatement: "Statement",
nextStatement: "Statement",
});
- this.setColour(ControlColor);
+ this.setColour(Blockly.GOBSTONES_COLORS.controlStructure);
this.appendValueInput('count')
.appendField('repetir');
this.appendDummyInput()
.appendField('veces');
this.appendStatementInput('block').setCheck(["Statement"]);
@@ -4835,11 +4339,11 @@
type: "Statement",
previousStatement: "Statement",
nextStatement: "Statement",
});
- this.setColour(ControlColor);
+ this.setColour(Blockly.GOBSTONES_COLORS.controlStructure);
this.appendValueInput('condicion')
.setCheck('Bool')
.appendField('repetir hasta que');
this.appendStatementInput('block').setCheck(["Statement"]);
this.setInputsInline(true);
@@ -4852,11 +4356,11 @@
type: "Statement",
previousStatement: "Statement",
nextStatement: "Statement",
});
- this.setColour(ControlColor);
+ this.setColour(Blockly.GOBSTONES_COLORS.controlStructure);
this.appendValueInput('condicion')
.appendField(Blockly.Msg["CONTROLS_IF_MSG_IF"]);
this.appendStatementInput('block').setCheck(["Statement"]);
this.setInputsInline(true);
}
@@ -4924,11 +4428,11 @@
"helpUrl": "%{BKY_CONTROLS_IF_HELPURL}",
"mutator": "controls_if_mutator_without_ui",
"extensions": ["controls_if_tooltip"]
});
- this.setColour(ControlColor);
+ this.setColour(Blockly.GOBSTONES_COLORS.controlStructure);
this.setInputsInline(true);
this.elseCount_++;
this.updateShape_();
},
@@ -4987,11 +4491,11 @@
{
type: 'input_value',
name: 'COLOR'
}
],
- colour: CommandColor,
+ colour: Blockly.GOBSTONES_COLORS.primitiveCommand,
tooltip: 'Poner color en casillero.',
inputsInline: true
});
}
};
@@ -5007,11 +4511,11 @@
{
type: 'input_value',
name: 'COLOR'
}
],
- colour: CommandColor,
+ colour: Blockly.GOBSTONES_COLORS.primitiveCommand,
tooltip: 'Sacar color de casillero.',
inputsInline: true
});
}
};
@@ -5027,11 +4531,11 @@
{
type: 'input_value',
name: 'DIRECCION'
}
],
- colour: CommandColor,
+ colour: Blockly.GOBSTONES_COLORS.primitiveCommand,
tooltip: 'Mover en una dirección.',
inputsInline: true
});
}
};
@@ -5047,11 +4551,11 @@
{
type: 'input_value',
name: 'DIRECCION'
}
],
- colour: CommandColor,
+ colour: Blockly.GOBSTONES_COLORS.primitiveCommand,
tooltip: 'Ir al borde del tablero.',
inputsInline: true
});
}
};
@@ -5061,11 +4565,11 @@
this.jsonInit({
type: "Statement",
previousStatement: "Statement",
nextStatement: "Statement",
message0: 'Vaciar tablero',
- colour: CommandColor,
+ colour: Blockly.GOBSTONES_COLORS.primitiveCommand,
tooltip: 'Vaciar el tablero.',
inputsInline: true
});
}
};
@@ -5087,11 +4591,11 @@
"name": "boomDescription",
"text": "Ingresar motivo..."
}
],
"inputsInline": false,
- "colour": CommandColor,
+ "colour": Blockly.GOBSTONES_COLORS.primitiveCommand,
"tooltip": "Este comando hace que estalle todo."
});
}
};
@@ -5107,11 +4611,11 @@
"type": "Statement",
"previousStatement": "Statement",
"nextStatement": "Statement",
"lastDummyAlign0": "RIGHT",
"message0": "COMPLETAR",
- "colour": CompletarColor,
+ "colour": Blockly.GOBSTONES_COLORS.complete,
"tooltip": "Tenés que reemplazar este bloque por tu solución"
});
},
onchange: Blockly.Blocks.makeShadowEventListener
@@ -5123,11 +4627,11 @@
"type": "InteractiveBinding",
"previousStatement": "InteractiveBinding",
"nextStatement": "InteractiveBinding",
"lastDummyAlign0": "RIGHT",
"message0": "COMPLETAR",
- "colour": CompletarColor,
+ "colour": Blockly.GOBSTONES_COLORS.complete,
"tooltip": "Tenés que reemplazar este bloque por tu solución"
});
},
onchange: Blockly.Blocks.makeShadowEventListener
@@ -5141,11 +4645,11 @@
init: function () {
this.jsonInit({
"type": "completar_expression",
"message0": "COMPLETAR",
"output": "any",
- "colour": CompletarColor,
+ "colour": Blockly.GOBSTONES_COLORS.complete,
"tooltip": "Tenés que reemplazar este bloque por tu solución"
});
},
onchange: Blockly.Blocks.makeShadowEventListener
@@ -5161,11 +4665,11 @@
type: "field_dropdown",
name: type + "Dropdown",
options: values.map(value => [value,value]),
}],
output: type,
- colour: ExpressionColor,
+ colour: Blockly.GOBSTONES_COLORS.literalExpression,
tooltip: "Escoger " + type,
});
}
};
}
@@ -5183,11 +4687,11 @@
{
type: 'input_value',
name: 'VALUE'
}
],
- colour: ExpressionColor,
+ colour: Blockly.GOBSTONES_COLORS.expression,
inputsInline: true,
output: returnType
})
}
};
@@ -5221,11 +4725,11 @@
{
type: 'input_value',
name: 'arg2'
}
],
- colour: ExpressionColor,
+ colour: Blockly.GOBSTONES_COLORS.expression,
inputsInline: false,
output: 'Bool'
});
}
};
@@ -5250,11 +4754,11 @@
{
type: 'input_value',
name: 'arg2'
}
],
- colour: ExpressionColor,
+ colour: Blockly.GOBSTONES_COLORS.expression,
inputsInline: false,
output: 'Number'
});
}
};
@@ -5279,11 +4783,11 @@
{
type: 'input_value',
name: 'arg2'
}
],
- colour: ExpressionColor,
+ colour: Blockly.GOBSTONES_COLORS.expression,
inputsInline: false,
output: 'Bool'
});
}
};
@@ -5312,11 +4816,11 @@
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
- "colour": 230,
+ "colour": Blockly.GOBSTONES_COLORS.assignation,
"tooltip": "",
"helpUrl": ""
});
this.getters = [];
},
@@ -5328,24 +4832,11 @@
this.createVariableBlock(name);
}});
},
createVariableBlock: function(name) {
- var parent = this;
-
- while (parent.getSurroundParent() !== null) {
- if (parent.type.startsWith("Interactive") && parent.type.endsWith("Binding")) break;
-
- const next = parent.getSurroundParent();
- if (next === null) break;
- parent = next;
- }
-
- var id = parent.id;
-
return Blockly.createBlockSvg(this.workspace, 'variables_get', b => {
- b.$parent = id;
b.setFieldValue(name, 'VAR');
b.moveBy(10,5);
b.parentAssignmentBlock = this;
this.getters.push(b);
});
@@ -5479,11 +4970,11 @@
Blockly.GobstonesLanguage.addPragma = function(block, str){
if(!str || !Blockly.GobstonesLanguage.shouldAddRegionPragma) return str;
// add newLine if block is a command or definition
const newLine = str[str.length-1] === '\n' ? '\n' : '';
- return `/@BEGIN_REGION@${block.id}@/${newLine}${str}/@END_REGION@/${newLine}`;
+ return `/*@BEGIN_REGION@${block.id}@*/${newLine}${str}/*@END_REGION@*/${newLine}`;
};
// Gobstones pragma BEGIN_REGION should avoid char 'at' ( @ )
Blockly.utils.genUid.soup_ = Blockly.utils.genUid.soup_.replace(/@/g,"a");
/**
@@ -5745,12 +5236,12 @@
};
Blockly.GobstonesLanguage.ComandoCompletar = b => 'BOOM("El programa todavía no está completo")\n';
Blockly.GobstonesLanguage.ExpresionCompletar = b => ['boom("El programa todavía no está completo")',Blockly.GobstonesLanguage.ORDER_FUNCTION_CALL];
+Blockly.GobstonesLanguage.AsociacionDeTeclaCompletar = b => ''
-
Blockly.GobstonesLanguage.ColorSelector = literalSelectorBlockCodeGenerator('Color');
Blockly.GobstonesLanguage.DireccionSelector = literalSelectorBlockCodeGenerator('Direccion');
Blockly.GobstonesLanguage.BoolSelector = literalSelectorBlockCodeGenerator('Bool');
Blockly.GobstonesLanguage.OperadorDeComparacion = function (block) {
@@ -6162,7 +5653,797 @@
'.blocklyPreconditionError>.blocklyPath {',
'stroke: #f88;',
'stroke-width: 3px;',
'}',
];</script>
+
+ <script>
+ Polymer({
+ is: 'gs-element-blockly',
+
+ properties: {
+ /*
+ * `primitiveProcedures` lista de definiciones de procedimientos primitivos.
+ * Ej: ["Poner_FloresAl_"] Se transormara en un bloque con dos inputs inline, cuyo
+ * codigo sera Poner_FloresAl_(arg1, arg2).
+ */
+ primitiveProcedures: {
+ type: Array,
+ observer: '_onPrimitiveProceduresChange'
+ },
+
+ /*
+ * `primitiveFunctions` lista de definiciones de funciones primitivas.
+ * Ej: ["cantidadDeFlores_en_"] Se transormara en un bloque con dos inputs inline, cuyo
+ * codigo sera cantidadDeFlores_en_(arg1, arg2).
+ */
+ primitiveFunctions: {
+ type: Array,
+ observer: '_onPrimitiveFunctionsChange'
+ },
+
+ /*
+ * `toolbox` Definicion de visibilidad y habilitación de los bloques en el toolbox.
+ * Pueden nombrarse bloques individuales o categorias.
+ *
+ * Ej:
+ *
+ * ```json
+ * {
+ * "visible": ["Comandos primitivos", "Literales", "Expresiones"],
+ * "disabled": ["Poner", "hayBolitas", "Literales"]
+ * }
+ * ```
+ *
+ * Los IDs de los bloques a utilizar pueden consultarse [en la wiki](https://github.com/Program-AR/gs-element-blockly/wiki/Block-IDs-for-toolbox)
+ *
+ * Nota: Los bloques de primitiveProcedures siempre aparecen en el toolbox, aunque no figuren
+ * en esta propiedad.
+ */
+ toolbox: {
+ type: Object,
+ observer: '_onUpdateToolbox'
+ },
+
+ /*
+ * `workspaceXml` Código XML de los bloques en el workspace.
+ */
+ workspaceXml: {
+ type: String,
+ observer: '_onWorkspaceUpdate',
+ notify: true
+ },
+
+ /*
+ * `workspaceCanEdit` Indica si el usuario puede modificar el workspace.
+ */
+ workspaceCanEdit: {
+ type: Boolean,
+ },
+
+ /*
+ * `width` Ancho del elemento.
+ */
+ width: {
+ type: Number,
+ observer: '_fixSize'
+ },
+
+ /*
+ * `height` Alto del elemento.
+ */
+ height: {
+ type: Number,
+ observer: '_fixSize'
+ },
+
+ /*
+ * `media` path a media de blockly.
+ */
+ media: {
+ type: String,
+ value: "../bower_components/blockly-package/media/"
+ },
+ },
+
+ _onPrimitiveProceduresChange: function() {
+ if(typeof this.primitiveProcedures == 'string') {
+ this.primitiveProcedures = JSON.parse(this.primitiveProcedures);
+ return;
+ }
+ this._definePrimitiveProcedures();
+ this._onUpdateToolbox();
+ },
+
+ _onPrimitiveFunctionsChange: function() {
+ if(typeof this.primitiveFunctions == 'string') {
+ this.primitiveFunctions = JSON.parse(this.primitiveFunctions);
+ return;
+ }
+ this._definePrimitiveFunctions();
+ this._onUpdateToolbox();
+ },
+
+ _onUpdateToolbox: function() {
+ if(typeof this.toolbox == 'string') {
+ if(this.toolbox == '') {
+ this.toolbox = undefined;
+ }
+ else {
+ this.toolbox = JSON.parse(this.toolbox);
+ }
+ return;
+ }
+ let toolbox = this._createToolbox();
+ this.workspace.updateToolbox(toolbox);
+ },
+
+ /**
+ * Convierte primitiveProcedures y primitiveFunctions a array de strings para que sea retrocompatible con la versión original.
+ * La versión actual también acepta cosas como:
+ * { name: "unNombre", attributes: { block_icon: "base64..." } }
+ */
+ _getPrimitiveNames: function(primitiveActions) {
+ return Array.isArray(primitiveActions)
+ ? primitiveActions.map(this._getPrimitiveName)
+ : primitiveActions;
+ },
+
+ _getPrimitiveName(primitive) {
+ return primitive.name || primitive;
+ },
+
+ /**
+ * Retorna el arbol por defecto de bloques y categorias
+ */
+ _defaultToolboxTree : function() {
+ const primitiveProcedures = this._getPrimitiveNames(this.primitiveProcedures);
+ const primitiveFunctions = this._getPrimitiveNames(this.primitiveFunctions);
+
+ let tree = []
+ let toolboxDefault = this.$$('#toolbox');
+ let toolboxDefaultLines = toolboxDefault.innerHTML.split("\n");
+ let ignore_last = false;
+ let parent = tree;
+ let stack = [];
+ for(var i in toolboxDefaultLines) {
+ let line = toolboxDefaultLines[i];
+ if(line.indexOf('<block') >= 0) {
+ let m = line.match('type="([^"]*)"')
+ if(m.length == 2) {
+ parent.push({
+ type: 'block',
+ name: m[1],
+ });
+ }
+ }
+ else if(line.indexOf('<category') >= 0) {
+ let m = line.match('name="([^"]*)"')
+ if(m.length == 2 && (
+ (m[1] == 'Procedimientos primitivos' && primitiveProcedures && primitiveProcedures.length > 0) ||
+ (m[1] == 'Funciones primitivas' && primitiveFunctions && primitiveFunctions.length > 0) ||
+ (m[1] != 'Procedimientos primitivos' && m[1] != 'Funciones primitivas'))) {
+ parent.push({
+ type: 'category',
+ name: m[1],
+ child: [],
+ xml: line
+ });
+ stack.push(parent);
+ parent = parent[parent.length - 1].child;
+ if(m[1] == 'Procedimientos primitivos')
+ {
+ for(var i in primitiveProcedures) {
+ parent.push({
+ type: 'block',
+ name: primitiveProcedures[i],
+ });
+ }
+ }
+ if(m[1] == 'Funciones primitivas')
+ {
+ for(var i in primitiveFunctions) {
+ parent.push({
+ type: 'block',
+ name: primitiveFunctions[i],
+ });
+ }
+ }
+ }
+ else {
+ ignore_last = true;
+ }
+ }
+ else if(line.indexOf('</category') >= 0) {
+ if(ignore_last) {
+ ignore_last = false;
+ }
+ else {
+ parent = stack.pop();
+ }
+ }
+ }
+
+ return tree;
+ },
+
+ /**
+ * Sirve para manejar los alias de los ID que vengan por la interfaz.
+ * Recibe una lista que puede ser de IDs de bloques ó de categorías.
+ * Devuelve una lista similar, intercambiando los alias por los ids "oficiales".
+ * Mi idea acá también es que a futuro se haga la internacionalización de ids.
+ */
+ _homogenizeIDs: function(ids){
+ return ids ? ids.map(id => Blockly.GobstonesLanguage.aliasForBlockID(id)) : [];
+ },
+
+ /**
+ * Crea el toolbox a partir de la propiedad blocks, bloques visibles, y
+ * customblocks.
+ *
+ * Funciona podando el toolbox por defecto.
+ */
+ _createToolbox: function() {
+ const primitiveProcedures = this._getPrimitiveNames(this.primitiveProcedures);
+ const primitiveFunctions = this._getPrimitiveNames(this.primitiveFunctions);
+
+ let tree = this._defaultToolboxTree();
+ let toolbox = [];
+
+ if(this.toolbox) {
+ let visibles = this._homogenizeIDs(this.toolbox.visible);
+ let disabled = this._homogenizeIDs(this.toolbox.disabled);
+ if(visibles.length > 0) {
+ visibles = visibles.concat(disabled);
+ if(primitiveProcedures) {
+ visibles = visibles.concat(primitiveProcedures);
+ }
+ if(primitiveFunctions) {
+ visibles = visibles.concat(primitiveFunctions);
+ }
+ }
+
+ function filtrarToolboxTree(ttree) {
+ let tbox = [];
+ for(var i in ttree) {
+ if(visibles.length == 0 || visibles.indexOf(ttree[i].name) >= 0) {
+ tbox.push(ttree[i]);
+ }
+ else if(ttree[i].type == 'category') {
+ let tmp = filtrarToolboxTree(ttree[i].child);
+ if(tmp.length > 0) {
+ ttree[i].child = tmp;
+ tbox.push(ttree[i]);
+ }
+ }
+ }
+ return tbox;
+ }
+
+ toolbox = filtrarToolboxTree(tree);
+
+ // Marcar disabled
+ function marcarDisabled(tbox) {
+ for(var i in tbox) {
+ if(disabled.indexOf(tbox[i].name) >= 0) {
+ tbox[i].disabled = true;
+ }
+ if(tbox[i].child) {
+ marcarDisabled(tbox[i].child);
+ }
+ }
+ }
+ marcarDisabled(toolbox);
+
+ }
+ else {
+ toolbox = tree;
+ }
+
+ return this._toolboxTreeToXML(toolbox);
+ },
+
+ _toolboxBlockXML: function(blockDef, forceDisabled) {
+ let args = "";
+ if(blockDef.disabled || forceDisabled) {
+ args = 'disabled="true"';
+ }
+ let xml = `<block type="${blockDef.name}" ${args}></block>`;
+ return xml;
+ },
+
+ _toolboxTreeToXML : function(toolbox, noXmlTags, disabled) {
+ let xml = [];
+ if(!noXmlTags) {
+ xml.push('<xml>');
+ }
+ for(var i in toolbox) {
+ if(toolbox[i].type == 'block') {
+ xml.push(this._toolboxBlockXML(toolbox[i], disabled));
+ }
+ else if(toolbox[i].type == 'category') {
+ xml.push(toolbox[i].xml);
+ xml.push(this._toolboxTreeToXML(toolbox[i].child, true, toolbox[i].disabled || disabled));
+ xml.push('</category>');
+ }
+ }
+ if(!noXmlTags) {
+ xml.push('</xml>');
+ }
+ return xml.join('\n');
+ },
+
+ _getPartsByConvention(name) {
+ let parts = name.replace(/([A-Z])/g, " $1").toLowerCase();
+ parts = parts[1].toUpperCase() + parts.substring(2);
+ parts = this._getParts(parts);
+
+ return parts;
+ },
+
+ _getParts(name) {
+ return name.split("_");
+ },
+
+ /**
+ * Define un bloque a partir de una definicion tipo Poner_En_
+ */
+ _definePrimitiveProcedure: function(definition) {
+ const name = this._getPrimitiveName(definition);
+ const customName = definition.attributes && definition.attributes.block_name;
+ const icon = definition.attributes && definition.attributes.block_icon;
+
+ const finalName = customName || name;
+ const parts = (customName ? this._getParts : this._getPartsByConvention)
+ .bind(this)(finalName);
+
+ // Bloque
+ Blockly.Blocks[name] = {
+ init: function () {
+ let argsIndex = 1;
+ this.setColour(Blockly.GOBSTONES_COLORS.primitiveCommand);
+
+ if (icon) {
+ this.appendDummyInput().appendField(new Blockly.FieldImage(
+ icon,
+ 16,
+ 16,
+ finalName
+ ));
+ }
+
+ for(var i in parts) {
+ if(i == (parts.length - 1)) {
+ this.appendDummyInput().appendField(parts[i]);
+ }
+ else {
+ this.appendValueInput('arg' + argsIndex).appendField(parts[i]);
+ argsIndex++;
+ }
+ }
+ this.setPreviousStatement(true);
+ this.setNextStatement(true);
+ this.setInputsInline(true);
+ }
+ };
+ let argsList = [];
+ for(var i=1;i<parts.length;i++) {
+ argsList.push('arg' + i);
+ }
+ // Generator
+ Blockly.GobstonesLanguage[name] = procBlockCodeGenerator(name, argsList);
+ },
+
+ _definePrimitiveProcedures: function () {
+ if(!this.primitiveProcedures) {
+ return;
+ }
+
+ for(var i in this.primitiveProcedures) {
+ this._definePrimitiveProcedure(this.primitiveProcedures[i]);
+ }
+ },
+
+
+ /**
+ * Define un bloque a partir de una definicion de funcion primitiva tipo hayFlores_en_
+ */
+ _definePrimitiveFunction: function(definition) {
+ const name = this._getPrimitiveName(definition);
+ const customName = definition.attributes && definition.attributes.block_name;
+ const icon = definition.attributes && definition.attributes.block_icon;
+
+ const finalName = customName || name;
+ const parts = (customName ? this._getParts : this._getPartsByConvention)
+ .bind(this)(finalName);
+
+ // Bloque
+ Blockly.Blocks[name] = {
+ init: function () {
+ let argsIndex = 1;
+ this.setColour(Blockly.GOBSTONES_COLORS.primitiveCommand);
+
+ if (icon) {
+ this.appendDummyInput().appendField(new Blockly.FieldImage(
+ icon,
+ 16,
+ 16,
+ finalName
+ ));
+ }
+
+ for(var i in parts) {
+ if(i == (parts.length - 1)) {
+ this.appendDummyInput().appendField(parts[i]);
+ }
+ else {
+ this.appendValueInput('arg' + argsIndex).appendField(parts[i]);
+ argsIndex++;
+ }
+ }
+ this.setPreviousStatement(false);
+ this.setNextStatement(false);
+ this.setInputsInline(true);
+ this.setOutput('var');
+ }
+ };
+ let argsList = [];
+ for(var i=1;i<parts.length;i++) {
+ argsList.push('arg' + i);
+ }
+ // Generator
+ Blockly.GobstonesLanguage[name] = functionBlockCodeGenerator(name, argsList);
+ },
+
+ _definePrimitiveFunctions: function () {
+ if(!this.primitiveFunctions) {
+ return;
+ }
+
+ for(var i in this.primitiveFunctions) {
+ this._definePrimitiveFunction(this.primitiveFunctions[i]);
+ }
+ },
+
+ _onBlocklyWorkspaceUpdate: function () {
+ let xml = Blockly.Xml.workspaceToDom(this.workspace);
+ this._blocklyWorkspaceXML = Blockly.Xml.domToText(xml);
+ this.workspaceXml = this._blocklyWorkspaceXML;
+ this._keepOnlyAProgram(xml);
+ this._checkParameterBounds(xml);
+ },
+
+ _keepOnlyAProgram(xml) {
+ const findProgram = (programType) => {
+ const children = xml.children;
+ const items = [];
+ for (var i=0; i < children.length; i++)
+ if (children[i].getAttribute("type") === programType)
+ items.push(children[i]);
+ return items;
+ };
+
+ const block = (blockXml) => this.workspace.getBlockById(blockXml.getAttribute("id"));
+
+ const programXmls = findProgram("Program");
+ const interactiveProgramXmls = findProgram("InteractiveProgram");
+
+ // repeated blocks
+ if (programXmls.length > 1) {
+ // (en vez de .undo() se podría hacerle .dispose() al bloque)
+
+ if (block(programXmls[0]).$timestamp > block(programXmls[1]).$timestamp) {
+ // delete programXmls[0]
+ this.workspace.undo();
+ programXmls.shift();
+ } else {
+ // delete programXmls[1]
+ this.workspace.undo();
+ programXmls.pop();
+ }
+ }
+ if (interactiveProgramXmls.length > 1) {
+ if (block(interactiveProgramXmls[0]).$timestamp > block(interactiveProgramXmls[1]).$timestamp) {
+ // delete interactiveProgramXmls[0]
+ this.workspace.undo();
+ interactiveProgramXmls.shift();
+ } else {
+ // delete interactiveProgramXmls[1]
+ this.workspace.undo();
+ interactiveProgramXmls.pop();
+ }
+ }
+
+ const program = programXmls[0] && block(programXmls[0]);
+ const interactiveProgram = interactiveProgramXmls[0] && block(interactiveProgramXmls[0]);
+
+ if (program && interactiveProgram) {
+ program.setDeletable(true);
+ interactiveProgram.setDeletable(true);
+
+ const important = program.$timestamp > interactiveProgram.$timestamp ? program : interactiveProgram;
+ const unimportant = program.$timestamp > interactiveProgram.$timestamp ? interactiveProgram : program;
+
+ const twoActivePrograms = !program.disabled && !interactiveProgram.disabled;
+ const twoDisabledPrograms = program.disabled && interactiveProgram.disabled;
+
+ if (twoActivePrograms || twoDisabledPrograms) {
+ important.setDisabledAndUpdateTimestamp(false);
+ unimportant.setDisabledAndUpdateTimestamp(true);
+ }
+ } else if (program && !interactiveProgram) {
+ program.setDisabledAndUpdateTimestamp(false);
+ program.setDeletable(false);
+ } else if (interactiveProgram && !program) {
+ interactiveProgram.setDisabledAndUpdateTimestamp(false);
+ interactiveProgram.setDeletable(false);
+ }
+ },
+
+ _checkParameterBounds(xml) {
+ const blocks = this.workspace.getAllBlocks();
+ for (block of blocks) {
+ if (block.type === "variables_get" && block.$parent) {
+ const parentBlock = this.workspace.getBlockById(block.$parent);
+ const varField = block.getField("VAR");
+
+ if (
+ parentBlock &&
+ (
+ !parentBlock.type.startsWith("procedures") ||
+ parentBlock.arguments_.some(it => it === varField.getValue())
+ )
+ ) {
+ var parent = block;
+ while ((parent = parent.getSurroundParent()) !== null) {
+ if (parent.id === block.$parent) break;
+ }
+
+ block.setDisabled(parent === null);
+ } else {
+ block.dispose();
+ }
+ }
+ }
+ },
+
+ _onWorkspaceUpdate: function () {
+ if(this.workspaceXml != this._blocklyWorkspaceXML) {
+ this.workspace.clear();
+ let dom = Blockly.Xml.textToDom(this.workspaceXml);
+ try {
+ Blockly.Xml.domToWorkspace(dom, this.workspace);
+ } catch (e) {
+ if(e.message.includes("nextConnection is null")){
+ throw {
+ name: "BlockTypeError",
+ message: "There is at least one block type declared in the 'type' property of the block tag of the xml, which is incorrect or belongs to a block that is not in the Gobstones Language. Maybe you have forgotten any primitiveProcedure?",
+ toString: function(){ return this.name + ': ' + this.message; }
+ };
+ } else {
+ throw e;
+ }
+ }
+ }
+ },
+
+ /**
+ * Reinicializa el workspace, dejando solo el bloque programa
+ */
+ resetWorkspace: function() {
+ let xmlInicial = '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="Program" x="30" y="30"></block></xml>';
+ this.workspaceXml = xmlInicial;
+ },
+
+ /**
+ * Retorna una lista de objetos, uno por cada categoría y bloque.
+ * Representa la jerarquía del toolbox.
+ *
+ * Para los bloques se indica:
+ *
+ * * `blockXMLID`: El ID del bloque que estará en el XML exportado por el método
+ * `generateCode`.
+ * * `blockAliases`: La lista de aliases que refieren a ese bloque, y que pueden
+ * ser usados para definir el toolbox mediante la propiedad `toolbox`.
+ *
+ * Para las categorías se indica:
+ *
+ * * `categoryName`: El nombre de la categoría que es visible en el toolbox.
+ * * `categoryAliases`: La lista de aliases que refieren a esa categoría, y que pueden
+ * ser usados para definir el toolbox mediante la propiedad `toolbox`.
+ * * `categoryContents`: La lista de objetos categoría/bloque dentro de esta categoría.
+ *
+ * @return {Array} Los objetos.
+ */
+ validToolboxIDs: function(){
+ return this._validToolboxIDsFrom(this._defaultToolboxTree());
+ },
+
+ /**
+ * Recursively defined. Gets a list of user-readable objects describing the
+ * toolboxTree hierarchy and aliases.
+ */
+ _validToolboxIDsFrom: function(toolboxTree) {
+ var myThis = this;
+ return this._mapToolboxTree(toolboxTree, function(toolboxElement){
+ if(toolboxElement.type === "category"){
+ return {
+ categoryName: toolboxElement.name,
+ categoryAliases: Blockly.GobstonesLanguage.aliasesFor(toolboxElement.name),
+ categoryContents: myThis._validToolboxIDsFrom(toolboxElement.child)
+ };
+ } else if(toolboxElement.type === "block") {
+ return {
+ blockXMLID: toolboxElement.name,
+ blockAliases: Blockly.GobstonesLanguage.aliasesFor(toolboxElement.name),
+ };
+ } else {
+ /*Do nothing for other types*/
+ }
+ });
+ },
+
+ _mapToolboxTree: function(toolboxTree, f){
+ var resultingList = [];
+ for (var i in toolboxTree){
+ resultingList.push(f(toolboxTree[i]));
+ }
+ return resultingList;
+ },
+
+ /**
+ * Reinicializa el estado del componente,
+ */
+ cleanup: function() {
+ this.primitiveProcedures = [];
+ this.primitiveFunctions = [];
+ this.toolbox = {};
+ this.resetWorkspace();
+ },
+
+ // Element Lifecycle
+ ready: function() {
+ },
+
+ attached: function() {
+ this._definePrimitiveProcedures();
+ this._definePrimitiveFunctions();
+ // create workspace
+ var blocklyDiv = this.$$('#blocklyDiv');
+ this.blocklyDiv = blocklyDiv;
+ this._fixSize();
+ this._fixScroll();
+ this.workspace = Blockly.inject(blocklyDiv, {
+ toolbox: this._createToolbox(),
+ media: this.get("media"),
+ toolboxPosition: "start",
+ scrollbars: true,
+ horizontalLayout: false,
+ collapse: true,
+ css: true,
+ zoom: {
+ controls: true,
+ wheel: true
+ }
+ });
+ var _this = this;
+ this.workspace.addChangeListener(function (a, b, c) {
+ Blockly.Events.disableOrphans(a, b, c);
+ _this._onBlocklyWorkspaceUpdate();
+ });
+ this.resetWorkspace()
+ this._onresize();
+ if(window.jQuery !== undefined) {
+ $(window).resize(() => this._onresize());
+ };
+
+ Blockly.ErrorInforming.addToWorkspace(this.workspace);
+ },
+
+ _onresize() {
+ Blockly.svgResize(this.workspace);
+ },
+
+ _fixSize() {
+ let style = "";
+ if(this.width && this.height) {
+ style += "width:"+ this.width + "px;";
+ style += "height:"+ this.height + "px;";
+ this.blocklyDiv.style = style;
+ this._onresize();
+ }
+ },
+
+ _fixScroll() {
+ this.blocklyDiv.addEventListener("wheel", (event) => {
+ if (!event.ctrlKey) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const { deltaX, deltaY } = event;
+ const metrics = this.workspace.getMetrics();
+ const currentScrollX = (metrics.viewLeft - metrics.contentLeft);
+ const currentScrollY = (metrics.viewTop - metrics.contentTop);
+ const newScrollX = Math.min(currentScrollX + deltaX / 2, metrics.contentWidth - metrics.viewWidth);
+ const newScrollY = Math.min(currentScrollY + deltaY / 2, metrics.contentHeight - metrics.viewHeight);
+
+ this.workspace.scrollbar.set(Math.max(newScrollX, 0), Math.max(newScrollY, 0));
+ }
+ }, true);
+ },
+
+ detached: function() {
+ // The analog to `attached`, `detached` fires when the element has been
+ // removed from a document.
+ //
+ // Use this to clean up anything you did in `attached`.
+ },
+
+ // Element Behavior
+
+ /**
+ * Generate gobstones code from the blocks in the workspace.
+ * It removes all highlighting and errors if already present.
+ *
+ * There is an additional option which if provided adds pragma BEGIN/END REGION
+ * to the output.
+ * * `element.generateCode( {withRegions: true, clearErrors: false} );`
+ * @return {string} The code.
+ */
+ generateCode: function(options = {}) {
+ this.workspace.highlightBlock(); // No parameters means reset highlighting
+ if (this.workspace.removeBlockErrors && options.clearErrors !== false)
+ this.workspace.removeBlockErrors();
+ Blockly.GobstonesLanguage.shouldAddRegionPragma = options.withRegions;
+ return Blockly.GobstonesLanguage.workspaceToCode(this.workspace);
+ },
+
+ /**
+ * Append blocks to workspace. Se agrega al contenido actual del workspace
+ * los bloques especificados en el xml (descartando el program si es que viene).
+ *
+ */
+ appendBlocksToWorkspace: function(xml) {
+ },
+
+ /**
+ * Highlight a given block by its ID. Se hace highlight del bloque indicado.
+ *
+ */
+ highlightBlock: function(blockId) {
+ this.workspace.highlightBlock(blockId)
+ },
+
+ /**
+ * Highlight a given block, telling the user it has an error
+ *
+ * `blockId` is the block ID where the error should appear
+ *
+ * `errorKind` is either a string with the description or an object with a kind of error.
+ * Next are examples with the existent error kinds.
+ *
+ * Examples:
+ * * `element.showBlockError('a1s2', 'Hey, here is an error')`
+ * * `element.showBlockError('a1s2', { kind: 'INCOMPLETE_ERROR'} )`
+ * * `element.showBlockError('a1s2', { kind: 'TYPE_ERROR', expectedType:'string', actualType: 'boolean' })`
+ * * `element.showBlockError('a1s2', { kind: 'PRECONDITION_ERROR', description: "Susan can't move right" })`
+ */
+ showBlockError: function(blockId, errorKind) {
+ this.workspace.showBlockError(blockId, errorKind);
+ },
+
+ testColors(colors) {
+ Blockly.GOBSTONES_COLORS = colors;
+ Blockly.HSV_SATURATION = colors.globalHsvSaturation;
+ Blockly.HSV_VALUE = colors.globalHsvValue;
+ Blockly.Constants.Math.HUE = colors.literalExpression;
+ Blockly.Msg.MATH_HUE = colors.literalExpression.toString();
+ Blockly.Blocks.procedures.HUE = colors.procedure;
+
+ var xml = Blockly.Xml.workspaceToDom(this.workspace);
+ this.resetWorkspace();
+ Blockly.Xml.domToWorkspace(xml, this.workspace);
+ }
+ });
+ </script>
</dom-module>
</body></html>
\ No newline at end of file