cra16/cake-core

View on GitHub
blocks/structure.js

Summary

Maintainability
F
1 wk
Test Coverage
/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://blockly.googlecode.com/
 *
 * 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 Procedure blocks for Blockly.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.Blocks.structure');

goog.require('Blockly.Blocks');


Blockly.Blocks['structure_define'] = {
    init: function() {
        this.setColour(370);
        var name = Blockly.Procedures.findLegalName(
            Blockly.Msg.STRUCTURE_DEFINE_NAME, this);
        this.appendDummyInput()
            .appendField(Blockly.Msg.STRUCTURE_DEFINE_TITLE)
            .appendField(new Blockly.FieldTextInput(name,
                Blockly.Procedures.rename), 'NAME')
            .appendField('', 'PARAMS');
        this.setMutator(new Blockly.Mutator(['structure_mutatormem', 'structure_mutatormem_pointer', 'structure_mutatormem_array']));
        this.setTooltip(Blockly.Msg.STRUCTURE_DEFINE_TOOPTIP);
        this.members_ = [];
        this.types_ = [];
        this.dist_ = [];
        this.spec_ =[];
        this.statementConnection_ = null;
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.tag = Blockly.Msg.TAG_STRUCTURE_DEFINE;
    },
    initName: function() {
        this.setFieldValue('', 'NAME');
    },
    updateParams_: function() {
        // Check for duplicated arguments.
        var badArg = false;
        var hash = {};
        for (var x = 0; x < this.members_.length; x++) {
            if (hash['arg_' + this.members_[x].toLowerCase()]) {
                badArg = true;
                break;
            }
            hash['arg_' + this.members_[x].toLowerCase()] = true;
        }
        if (badArg) {
            this.setWarningText(Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING);
        } else {
            this.setWarningText(null);
        }
        // Merge the arguments into a human-readable list.
        var paramString = '';
        if (this.members_.length) {
            paramString = Blockly.Msg.PROCEDURES_BEFORE_PARAMS;
            for (var x = 0; x < this.members_.length; x++) {
                if (x == 0) {
                    if(this.dist_[x]=='v'){
                        paramString = paramString + ' ' + this.types_[x] + ' ' + this.members_[x];
                    }
                    else if(this.dist_[x]=='a'){
                        paramString = paramString + ' ' + this.types_[x] + ' '+ this.members_[x] + '[' + this.spec_[x] + ']';
                    }
                    else if(this.dist_[x]=='p'){
                        paramString = paramString + ' ' + this.types_[x] + this.spec_[x] + ' ' + this.members_[x];
                    }
                }
                else {
                    if(this.dist_[x]=='v'){
                        paramString = paramString + ', ' + this.types_[x] + ' ' + this.members_[x];
                    }
                    else if(this.dist_[x]=='a'){
                        paramString = paramString + ', ' + this.types_[x] + ' '+ this.members_[x] + '[' + this.spec_[x] + ']';
                    }
                    else if(this.dist_[x]=='p'){
                        paramString = paramString + ', ' + this.types_[x] + this.spec_[x] + ' ' + this.members_[x];
                    }
                }
            }
        }
        this.setFieldValue(paramString, 'PARAMS');
    },
    /**
     * Create XML to represent the argument inputs.
     * @return {Element} XML storage element.
     * @this Blockly.Block
     */
    mutationToDom: function() {
        var container = document.createElement('mutation');
        for (var x = 0; x < this.members_.length; x++) {
            var element = document.createElement('arg');
            element.setAttribute('name', this.members_[x]);
            element.setAttribute('types', this.types_[x]);
            element.setAttribute('dist', this.dist_[x]);
            if(this.dist_[x]=='a'){
                element.setAttribute('length', this.spec_[x]);
            }
            else if(this.dist_[x]=='p'){
                element.setAttribute('iteration', this.spec_[x]);
            }
            container.appendChild(element);
        }

        // Save whether the statement input is visible.
        return container;
    },
    /**
     * Parse XML to restore the argument inputs.
     * @param {!Element} xmlElement XML storage element.
     * @this Blockly.Block
     */
    domToMutation: function(xmlElement) {
        this.members_ = [];
        for (var x = 0, childNode; childNode = xmlElement.childNodes[x]; x++) {
            if (childNode.nodeName.toLowerCase() == 'arg') {
                this.members_.push(childNode.getAttribute('name'));
                this.types_.push(childNode.getAttribute('types'));
                this.dist_.push(childNode.getAttribute('dist'));
                if(childNode.getAttribute('dist')=='v'){
                    this.spec_.push(null);
                }
                else if(childNode.getAttribute('dist')=='a'){
                    this.spec_.push(childNode.getAttribute('length'));
                }
                else if(childNode.getAttribute('dist')=='p'){
                    this.spec_.push(childNode.getAttribute('iteration'));
                }
            }
        }
        this.updateParams_();
    },
    /**
     * Populate the mutator's dialog with this block's components.
     * @param {!Blockly.Workspace} workspace Mutator's workspace.
     * @return {!Blockly.Block} Root block in mutator.
     * @this Blockly.Block
     */
    decompose: function(workspace) {
        var containerBlock = Blockly.Block.obtain(workspace,
            'structure_mutatorcontainer');
        containerBlock.initSvg();


        // Parameter list.
        var connection = containerBlock.getInput('STACK').connection;
        for (var x = 0; x < this.members_.length; x++) {
            var paramBlock;
            if(this.dist_[x]=='v'){
                paramBlock = Blockly.Block.obtain(workspace, 'structure_mutatormem');
                paramBlock.initSvg();
                paramBlock.setFieldValue(this.members_[x], 'NAME');
                paramBlock.setFieldValue(this.types_[x], 'TYPES');
            }
            else if(this.dist_[x]=='a'){
                paramBlock = Blockly.Block.obtain(workspace, 'structure_mutatormem_array');
                paramBlock.initSvg();
                paramBlock.setFieldValue(this.members_[x], 'NAME');
                paramBlock.setFieldValue(this.types_[x], 'TYPES');
                paramBlock.setFieldValue(this.spec_[x], 'LENGTH');
            }
            else if(this.dist_[x]=='p'){
                paramBlock = Blockly.Block.obtain(workspace, 'structure_mutatormem_pointer');
                paramBlock.initSvg();
                paramBlock.setFieldValue(this.members_[x], 'NAME');
                paramBlock.setFieldValue(this.types_[x], 'TYPES');
                paramBlock.setFieldValue(this.spec_[x], 'ITERATION');
            }
            // Store the old location.
            paramBlock.oldLocation = x;
            connection.connect(paramBlock.previousConnection);
            connection = paramBlock.nextConnection;
        }
        // Initialize procedure's callers with blank IDs.
        Blockly.Procedures.mutateCallers(this.getFieldValue('NAME'), this.getFieldValue('TYPES'),
            this.workspace, this.members_, this.types_, this.dist_, this.spec_, null);
        return containerBlock;
    },
    /**
     * Reconfigure this block based on the mutator dialog's components.
     * @param {!Blockly.Block} containerBlock Root block in mutator.
     * @this Blockly.Block
     */
    compose: function(containerBlock) {
        // Parameter list.
        this.members_ = [];
        this.types_ = [];
        this.paramIds_ = [];
        this.dist_ = [];
        this.spec_ = [];
        var paramBlock = containerBlock.getInputTargetBlock('STACK');
        while (paramBlock) {
            this.members_.push(paramBlock.getFieldValue('NAME'));
            this.types_.push(paramBlock.getFieldValue('TYPES'));
            this.dist_.push(paramBlock.getDist());
            if(paramBlock.getDist()=='v'){
                this.spec_.push(paramBlock.getFieldValue());
            }
            else if(paramBlock.getDist()=='a') {
                this.spec_.push(paramBlock.getFieldValue('LENGTH'));
            }
            else if(paramBlock.getDist()=='p'){
                this.spec_.push(paramBlock.getFieldValue('ITERATION'));
            }
            this.paramIds_.push(paramBlock.id);
            paramBlock = paramBlock.nextConnection &&
            paramBlock.nextConnection.targetBlock();
        }
        this.updateParams_();
        Blockly.Procedures.mutateCallers(this.getFieldValue('NAME'), this.getFieldValue('TYPES'),
            this.workspace, this.members_, this.types_, this.dist_, this.spec_, this.paramIds_);

        // Show/hide the statement input.
        var hasStatements = containerBlock.getFieldValue('STATEMENTS');
        if (hasStatements !== null) {
            hasStatements = hasStatements == 'TRUE';
            var stackInput = this.getInput('STACK');
            if (stackInput.isVisible() != hasStatements) {
                if (hasStatements) {
                    // Restore the stack, if one was saved.
                    if (stackInput.connection.targetConnection ||
                        !this.statementConnection_ ||
                        this.statementConnection_.targetConnection ||
                        this.statementConnection_.sourceBlock_.workspace !=
                        this.workspace) {
                        // Block no longer exists or has been attached elsewhere.
                        this.statementConnection_ = null;
                    } else {
                        stackInput.connection.connect(this.statementConnection_);
                    }
                } else {
                    // Save the stack, then disconnect it.
                    this.statementConnection_ = stackInput.connection.targetConnection;
                    if (this.statementConnection_) {
                        var stackBlock = stackInput.connection.targetBlock();
                        stackBlock.setParent(null);
                        stackBlock.bumpNeighbours_();
                    }
                }
                stackInput.setVisible(hasStatements);
            }
        }
    },
    /**
     * Dispose of any callers.
     * @this Blockly.Block
     */
    dispose: function() {
        var name = this.getFieldValue('NAME');
        var type = this.getFieldValue('TYPES');
        Blockly.Procedures.disposeCallers(name, this.workspace);
        // Call parent's destructor.
        Blockly.Block.prototype.dispose.apply(this, arguments);
    },
    /**
     * Return all variables referenced by this block.
     * @return {!Array.<string>} List of variable names.
     * @this Blockly.Block
     */
    getMems: function() {
        return this.members_;
    },
    getTypes: function() {
        return this.types_;
    },
    /**
     * Notification that a variable is renaming.
     * If the name matches one of this block's variables, rename it.
     * @param {string} oldName Previous name of variable.
     * @param {string} newName Renamed variable.
     * @this Blockly.Block
     */
    renameVar: function(oldName, newName) {
        var change = false;
        for (var x = 0; x < this.members_.length; x++) {
            if (Blockly.Names.equals(oldName, this.members_[x])) {
                this.members_[x] = newName;
                change = true;
            }
        }
        if (change) {
            this.updateParams_();
            // Update the mutator's variables if the mutator is open.
            if (this.mutator.isVisible_()) {
                var blocks = this.mutator.workspace_.getAllBlocks();
                for (var x = 0, block; block = blocks[x]; x++) {
                    if (block.type == 'structure_mutatormem' &&
                        Blockly.Names.equals(oldName, block.getFieldValue('NAME'))) {
                        block.setFieldValue(newName, 'NAME');
                    }
                }
            }
        }
    },
    getDist: function() {
        return ['sd'];
    },
    getStructDefine: function() {
        return ['sd', this.getFieldValue('NAME'), this.types_, this.members_, this.dist_, this.spec_];
    },
    getName: function() {
        return [this.getFieldValue('NAME')];
    },
    callType_: 'procedures_callnoreturn'
};

Blockly.Blocks['structure_declare'] = {
    /**
     * Block for pointer setter.
     * @this Blockly.Block
     */
    init: function() {
        this.setColour(370);
        var name = Blockly.Procedures.findLegalName(
            Blockly.Msg.STRUCTURE_DECLARE_NAME, this);
        this.interpolateMsg(
            // TODO: Combine these messages instead of using concatenation.
            Blockly.Msg.STRUCTURE_DECLARE_TITLE + ' %1 ' +
            Blockly.Msg.STRUCTURE_DECLARE_TALE + ' %2',
            ['TYPES', new Blockly.FieldStructure(Blockly.Msg.SELECT_TYPE, null)],
            ['NAME', new Blockly.FieldTextInput(name, Blockly.Procedures.rename), Blockly.ALIGN_RIGHT],
            Blockly.ALIGN_RIGHT);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.STRUCTURE_DECLARE_TOOPTIP);
        this.tag = Blockly.Msg.TAG_STRUCTURE_DECLARE;
        // this.contextMenuMsg_ = Blockly.Msg.VARIABLES_SET_CREATE_GET;
        // this.contextMenuType_ = 'variables_pointer_get';
    },
    initName: Blockly.Blocks['structure_define'].initName,
    /**
     * Return all variables referenced by this block.
     * @return {!Array.<string>} List of variable names.
     * @this Blockly.Block
     */
    getTypes: function() {
        return [this.getFieldValue('TYPES')];
    },
    /**
     * Notification that a variable is renaming.
     * If the name matches one of this block's variables, rename it.
     * @param {string} oldName Previous name of variable.
     * @param {string} newName Renamed variable.
     * @this Blockly.Block
     */
    renameVar: function(oldName, newName) {
        if (Blockly.Names.equals(oldName, this.getFieldValue('NAME'))) {
            this.setFieldValue(newName, 'NAME');
        }
    },
    getDist: function() {
        return ['sn'];
    },
    getStructDeclare: function() {
        return [this.getFieldValue('NAME')];
    },
    //when the block is changed,
    onchange: Blockly.Blocks.variablePlaceCheck
};

Blockly.Blocks['structure_get'] = {
    /**
     * Block for variable getter.
     * @this Blockly.Block
     */
    init: function() {
        this.setColour(370);
        this.appendDummyInput('struct')
            .appendField('', 'NAME')
            .appendField(Blockly.Msg.STRUCTURE_GET_MEMBER)
            .appendField(new Blockly.FieldStructureMember(Blockly.Msg.SELECT_MENU, null, this), 'Mem');
        this.setOutput(true);
        this.setTooltip(Blockly.Msg.STRUCTURE_GET_TOOLTIP);
        this.tag = Blockly.Msg.TAG_STRUCTURE_GET;
    },

    getStructureCall: function() {
        // The NAME field is guaranteed to exist, null will never be returned.
        return /** @type {string} */ (this.getFieldValue('NAME'));
    },
    /**
     * Notification that a procedure is renaming.
     * If the name matches this block's procedure, rename it.
     * @param {string} oldName Previous name of procedure.
     * @param {string} newName Renamed procedure.
     * @this Blockly.Block
     */
    renameProcedure: function(oldName, newName) {
        if (Blockly.Names.equals(oldName, this.getStructureCall())) {
            this.setFieldValue(newName, 'NAME');
            this.setTooltip(
                (this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP : Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP)
                    .replace('%1', newName));
        }
    },
    /**
     * Create XML to represent the (non-editable) name and arguments.
     * @return {Element} XML storage element.
     * @this Blockly.Block
     */
    mutationToDom: function() {
        var container = document.createElement('mutation');
        container.setAttribute('name', this.getStructureCall());
        return container;
    },
    /**
     * Parse XML to restore the (non-editable) name and parameters.
     * @param {!Element} xmlElement XML storage element.
     * @this Blockly.Block
     */
    domToMutation: function(xmlElement) {
        var name = xmlElement.getAttribute('name');
        this.setFieldValue(name, 'NAME');
        this.setTooltip(
            (this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP : Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP).replace('%1', name));

    },
    //when the block is changed,
    onchange: Blockly.Blocks.requireInFunction
};

Blockly.Blocks['structure_set'] = {
    /**
     * Block for variable setter.
     * @this Blockly.Block
     */
    init: function() {
        this.setColour(370);
        this.appendDummyInput('struct')
            .appendField('', 'NAME')
            .appendField(Blockly.Msg.STRUCTURE_SET_MEMBER)
            .appendField(new Blockly.FieldStructureMember(Blockly.Msg.SELECT_MENU, null, this), 'Mem');
        this.appendValueInput('VALUE');
        this.setInputsInline(true);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.STRUCTURE_SET_TOOLTIP);
        this.tag = Blockly.Msg.TAG_STRUCTURE_SET;
    },

    getStructureCall: function() {
        // The NAME field is guaranteed to exist, null will never be returned.
        return /** @type {string} */ (this.getFieldValue('NAME'));
    },
    /**
     * Notification that a procedure is renaming.
     * If the name matches this block's procedure, rename it.
     * @param {string} oldName Previous name of procedure.
     * @param {string} newName Renamed procedure.
     * @this Blockly.Block
     */
    renameProcedure: function(oldName, newName) {
        if (Blockly.Names.equals(oldName, this.getStructureCall())) {
            this.setFieldValue(newName, 'NAME');
            this.setTooltip(
                (this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP : Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP)
                    .replace('%1', newName));
        }
    },
    /**
     * Create XML to represent the (non-editable) name and arguments.
     * @return {Element} XML storage element.
     * @this Blockly.Block
     */
    mutationToDom: function() {
        var container = document.createElement('mutation');
        container.setAttribute('name', this.getStructureCall());
        return container;
    },
    /**
     * Parse XML to restore the (non-editable) name and parameters.
     * @param {!Element} xmlElement XML storage element.
     * @this Blockly.Block
     */
    domToMutation: function(xmlElement) {
        var name = xmlElement.getAttribute('name');
        this.setFieldValue(name, 'NAME');
        this.setTooltip(
            (this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP : Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP).replace('%1', name));

    },
    //when the block is changed,
    onchange: Blockly.Blocks.requireInFunction
};

Blockly.Blocks['structure_mutatorcontainer'] = {
    /**
     * Mutator block for procedure container.
     * @this Blockly.Block
     */
    init: function() {
        this.setColour(370);
        this.appendDummyInput()
            .appendField(Blockly.Msg.STRUCTURE_MUTATORCONTAINER_TITLE);
        this.appendStatementInput('STACK');
        this.setTooltip(Blockly.Msg.STRUCTURE_MUTATORCONTAINER_TOOLTIP);
        this.contextMenu = false;
    }
};

Blockly.Blocks['structure_mutatormem'] = {
    /**
     * Mutator block for procedure argument.
     * @this Blockly.Block
     */
    init: function() {
        var TYPE =
            [
                [Blockly.Msg.VARIABLES_SET_TYPE_INT, 'int'],
                [Blockly.Msg.VARIABLES_SET_TYPE_UNSIGNED_INT, 'unsigned int'],
                [Blockly.Msg.VARIABLES_SET_TYPE_FLOAT, 'float'],
                [Blockly.Msg.VARIABLES_SET_TYPE_DOUBLE, 'double'],
                [Blockly.Msg.VARIABLES_SET_TYPE_CHAR, 'char']];
        this.setColour(370);
        this.appendDummyInput()
            .appendField(Blockly.Msg.STRUCTURE_MUTATORMEM_VAR)
            .appendField(new Blockly.FieldDropdown(TYPE), 'TYPES')
            .appendField(Blockly.Msg.STRUCTURE_MUTATORARG_NAME)
            .appendField(new Blockly.FieldTextInput('x', this.validator_), 'NAME');
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.STRUCTURE_MUTATORARG_TOOLTIP);
        this.contextMenu = false;
    },
    /**
     * Obtain a valid name for the procedure.
     * Merge runs of whitespace.  Strip leading and trailing whitespace.
     * Beyond this, all names are legal.
     * @param {string} newVar User-supplied name.
     * @return {?string} Valid name, or null if a name was not specified.
     * @private
     * @this Blockly.Block
     */
    validator_: function(newVar) {
        newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
        return newVar || null;
    },
    getTypes: function() {
        return [this.getFieldValue('TYPES')];
    },
    getDist: function() {
        return 'v';
    },
    getSpec: function(){
        return null;
    }
};

Blockly.Blocks['structure_mutatormem_array'] = {
    /**
     * Mutator block for procedure argument.
     * @this Blockly.Block
     */
    init: function() {
        var TYPE =
            [
                [Blockly.Msg.VARIABLES_SET_TYPE_INT, 'int'],
                [Blockly.Msg.VARIABLES_SET_TYPE_UNSIGNED_INT, 'unsigned int'],
                [Blockly.Msg.VARIABLES_SET_TYPE_FLOAT, 'float'],
                [Blockly.Msg.VARIABLES_SET_TYPE_DOUBLE, 'double'],
                [Blockly.Msg.VARIABLES_SET_TYPE_CHAR, 'char']];
        this.setColour(370);
        this.interpolateMsg(
            // TODO: Combine these messages instead of using concatenation.
            Blockly.Msg.STRUCTURE_MUTATORMEM_ARRAY +'%1 ' + Blockly.Msg.VARIABLES_ARRAY_DECLARE_LENGTH + ' %2 ' +
            Blockly.Msg.VARIABLES_DECLARE_NAME + ' %3 ', ['TYPES', new Blockly.FieldDropdown(TYPE)], ['LENGTH', new Blockly.FieldTextInput('1')], ['NAME', new Blockly.FieldTextInput('z', Blockly.Blocks.CNameValidator)],
            Blockly.ALIGN_RIGHT);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.STRUCTURE_MUTATORARG_TOOLTIP);
        this.contextMenu = false;
    },
    /**
     * Obtain a valid name for the procedure.
     * Merge runs of whitespace.  Strip leading and trailing whitespace.
     * Beyond this, all names are legal.
     * @param {string} newVar User-supplied name.
     * @return {?string} Valid name, or null if a name was not specified.
     * @private
     * @this Blockly.Block
     */
    validator_: function(newVar) {
        newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
        return newVar || null;
    },
    getTypes: function() {
        return [this.getFieldValue('TYPES')];
    },
    getDist: function() {
        return 'a';
    },
    getSpec: function(){
        return [this.getFieldValue('LENGTH')];
    }
};

Blockly.Blocks['structure_mutatormem_pointer'] = {
    /**
     * Mutator block for procedure argument.
     * @this Blockly.Block
     */
    init: function() {
        var TYPE =
            [
                [Blockly.Msg.VARIABLES_SET_TYPE_INT, 'int'],
                [Blockly.Msg.VARIABLES_SET_TYPE_UNSIGNED_INT, 'unsigned int'],
                [Blockly.Msg.VARIABLES_SET_TYPE_FLOAT, 'float'],
                [Blockly.Msg.VARIABLES_SET_TYPE_DOUBLE, 'double'],
                [Blockly.Msg.VARIABLES_SET_TYPE_CHAR, 'char']];
        var ITERATION =
            [
                [Blockly.Msg.VARIABLES_SET_ITERATION_NORMAL, '*'],
                [Blockly.Msg.VARIABLES_SET_ITERATION_DOUBLE, '**'],
                [Blockly.Msg.VARIABLES_SET_ITERATION_TRIPLE, '***']
            ];
        this.setColour(370);
        this.interpolateMsg(
            // TODO: Combine these messages instead of using concatenation.
            Blockly.Msg.STRUCTURE_MUTATORMEM_POINTER+ '%1 ' + Blockly.Msg.VARIABLES_POINTER_DECLARE_ITERATION + ' %2 ' +
            Blockly.Msg.VARIABLES_DECLARE_NAME + ' %3 ', ['TYPES', new Blockly.FieldDropdown(TYPE)], ['ITERATION', new Blockly.FieldDropdown(ITERATION)], ['NAME', new Blockly.FieldTextInput('y', Blockly.Blocks.CNameValidator)],
            Blockly.ALIGN_RIGHT);
        this.setPreviousStatement(true);
        this.setNextStatement(true);
        this.setTooltip(Blockly.Msg.STRUCTURE_MUTATORARG_TOOLTIP);
        this.contextMenu = false;
    },
    /**
     * Obtain a valid name for the procedure.
     * Merge runs of whitespace.  Strip leading and trailing whitespace.
     * Beyond this, all names are legal.
     * @param {string} newVar User-supplied name.
     * @return {?string} Valid name, or null if a name was not specified.
     * @private
     * @this Blockly.Block
     */
    validator_: function(newVar) {
        newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
        return newVar || null;
    },
    getTypes: function() {
        return [this.getFieldValue('TYPES')];
    },
    getDist: function() {
        return 'p';
    },
    getSpec: function(){
        return [this.getFieldValue('ITERATION')];
    }
};