12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037 |
- /**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 Google Inc.
- * https://developers.google.com/blockly/
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * @fileoverview Procedure blocks for Blockly.
- * @author fraser@google.com (Neil Fraser)
- */
- 'use strict';
- goog.provide('Blockly.Blocks.procedures');
- goog.require('Blockly.Blocks');
- Blockly.Blocks.procedures.HUE = 340;
- Blockly.Blocks['procedures_defnoreturn'] = {
- /**
- * Block for defining a procedure with no return value.
- * @this Blockly.Block
- */
- init: function() {
- var nameField = new Blockly.FieldTextInput(
- Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE,
- Blockly.Procedures.rename);
- nameField.setSpellcheck(false);
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.appendDummyInput()
- .appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE)
- .appendField(nameField, 'NAME')
- .appendField('', 'PARAMS');
- this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
- /*if ((this.workspace.options.comments ||
- (this.workspace.options.parentWorkspace &&
- this.workspace.options.parentWorkspace.options.comments)) &&
- Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT) {
- this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);
- }*/
- this.setColour(Blockly.Blocks.procedures.HUE);
- this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);
- this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);
- this.arguments_ = [];
- this.setStatements_(true);
- this.statementConnection_ = null;
- },
- /**
- * Add or remove the statement block from this function definition.
- * @param {boolean} hasStatements True if a statement block is needed.
- * @this Blockly.Block
- */
- setStatements_: function(hasStatements) {
- if (this.hasStatements_ === hasStatements) {
- return;
- }
- if (hasStatements) {
- this.appendStatementInput('STACK')
- .appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO);
- if (this.getInput('RETURN')) {
- this.moveInputBefore('STACK', 'RETURN');
- }
- } else {
- this.removeInput('STACK', true);
- }
- this.hasStatements_ = hasStatements;
- },
- /**
- * Update the display of parameters for this procedure definition block.
- * Display a warning if there are duplicately named parameters.
- * @private
- * @this Blockly.Block
- */
- updateParams_: function() {
- // Check for duplicated arguments.
- var badArg = false;
- var hash = {};
- for (var i = 0; i < this.arguments_.length; i++) {
- if (hash['arg_' + this.arguments_[i].toLowerCase()]) {
- badArg = true;
- break;
- }
- hash['arg_' + this.arguments_[i].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.arguments_.length) {
- paramString = Blockly.Msg.PROCEDURES_BEFORE_PARAMS +
- ' ' + this.arguments_.join(', ');
- }
- // The params field is deterministic based on the mutation,
- // no need to fire a change event.
- Blockly.Events.disable();
- try {
- this.setFieldValue(paramString, 'PARAMS');
- } finally {
- Blockly.Events.enable();
- }
- },
- /**
- * Create XML to represent the argument inputs.
- * @param {=boolean} opt_paramIds If true include the IDs of the parameter
- * quarks. Used by Blockly.Procedures.mutateCallers for reconnection.
- * @return {!Element} XML storage element.
- * @this Blockly.Block
- */
- mutationToDom: function(opt_paramIds) {
- var container = document.createElement('mutation');
- if (opt_paramIds) {
- container.setAttribute('name', this.getFieldValue('NAME'));
- }
- for (var i = 0; i < this.arguments_.length; i++) {
- var parameter = document.createElement('arg');
- parameter.setAttribute('name', this.arguments_[i]);
- if (opt_paramIds && this.paramIds_) {
- parameter.setAttribute('paramId', this.paramIds_[i]);
- }
- container.appendChild(parameter);
- }
- // Save whether the statement input is visible.
- if (!this.hasStatements_) {
- container.setAttribute('statements', 'false');
- }
- return container;
- },
- /**
- * Parse XML to restore the argument inputs.
- * @param {!Element} xmlElement XML storage element.
- * @this Blockly.Block
- */
- domToMutation: 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');
- },
- /**
- * 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 = workspace.newBlock('procedures_mutatorcontainer');
- containerBlock.initSvg();
- // Check/uncheck the allow statement box.
- if (this.getInput('RETURN')) {
- containerBlock.setFieldValue(this.hasStatements_ ? 'TRUE' : 'FALSE',
- 'STATEMENTS');
- } else {
- containerBlock.getInput('STATEMENT_INPUT').setVisible(false);
- }
- // Parameter list.
- var connection = containerBlock.getInput('STACK').connection;
- for (var i = 0; i < this.arguments_.length; i++) {
- var paramBlock = workspace.newBlock('procedures_mutatorarg');
- paramBlock.initSvg();
- paramBlock.setFieldValue(this.arguments_[i], 'NAME');
- // Store the old location.
- paramBlock.oldLocation = i;
- connection.connect(paramBlock.previousConnection);
- connection = paramBlock.nextConnection;
- }
- // Initialize procedure's callers with blank IDs.
- Blockly.Procedures.mutateCallers(this);
- 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.arguments_ = [];
- this.paramIds_ = [];
- var paramBlock = containerBlock.getInputTargetBlock('STACK');
- while (paramBlock) {
- this.arguments_.push(paramBlock.getFieldValue('NAME'));
- this.paramIds_.push(paramBlock.id);
- paramBlock = paramBlock.nextConnection &&
- paramBlock.nextConnection.targetBlock();
- }
- this.updateParams_();
- Blockly.Procedures.mutateCallers(this);
- // Show/hide the statement input.
- var hasStatements = containerBlock.getFieldValue('STATEMENTS');
- if (hasStatements !== null) {
- hasStatements = hasStatements == 'TRUE';
- if (this.hasStatements_ != hasStatements) {
- if (hasStatements) {
- this.setStatements_(true);
- // Restore the stack, if one was saved.
- Blockly.Mutator.reconnect(this.statementConnection_, this, 'STACK');
- this.statementConnection_ = null;
- } else {
- // Save the stack, then disconnect it.
- var stackConnection = this.getInput('STACK').connection;
- this.statementConnection_ = stackConnection.targetConnection;
- if (this.statementConnection_) {
- var stackBlock = stackConnection.targetBlock();
- stackBlock.unplug();
- stackBlock.bumpNeighbours_();
- }
- this.setStatements_(false);
- }
- }
- }
- },
- /**
- * Return the signature of this procedure definition.
- * @return {!Array} Tuple containing three elements:
- * - the name of the defined procedure,
- * - a list of all its arguments,
- * - that it DOES NOT have a return value.
- * @this Blockly.Block
- */
- getProcedureDef: function() {
- return [this.getFieldValue('NAME'), this.arguments_, false];
- },
- /**
- * Return all variables referenced by this block.
- * @return {!Array.<string>} List of variable names.
- * @this Blockly.Block
- */
- getVars: function() {
- return this.arguments_;
- },
- /**
- * 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 i = 0; i < this.arguments_.length; i++) {
- if (Blockly.Names.equals(oldName, this.arguments_[i])) {
- this.arguments_[i] = 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 i = 0, block; block = blocks[i]; i++) {
- if (block.type == 'procedures_mutatorarg' &&
- Blockly.Names.equals(oldName, block.getFieldValue('NAME'))) {
- block.setFieldValue(newName, 'NAME');
- }
- }
- }
- }
- },
- /**
- * Add custom menu options to this block's context menu.
- * @param {!Array} options List of menu options to add to.
- * @this Blockly.Block
- */
- customContextMenu: function(options) {
- // Add option to create caller.
- var option = {enabled: true};
- var name = this.getFieldValue('NAME');
- option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name);
- var xmlMutation = goog.dom.createDom('mutation');
- xmlMutation.setAttribute('name', name);
- for (var i = 0; i < this.arguments_.length; i++) {
- var xmlArg = goog.dom.createDom('arg');
- xmlArg.setAttribute('name', this.arguments_[i]);
- xmlMutation.appendChild(xmlArg);
- }
- var xmlBlock = goog.dom.createDom('block', null, xmlMutation);
- xmlBlock.setAttribute('type', this.callType_);
- option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
- options.push(option);
- // Add options to create getters for each parameter.
- if (!this.isCollapsed()) {
- for (var i = 0; i < this.arguments_.length; 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);
- options.push(option);
- }
- }
- },
- callType_: 'procedures_callnoreturn',
- /**
- * Called whenever anything on the workspace changes.
- * Add warning if this flow block is not nested inside a loop.
- * @this Blockly.Block
- */
- onchange: function() {
- if (!this.workspace) {
- // Block has been deleted.
- return;
- }
-
- // Does it have valid returns?
- var hasReturns = false;
- var descendants = this.getDescendants();
- for (var i = 0; i < descendants.length; i++) {
- if (descendants[i].type == 'procedures_return') {
- hasReturns = true;
- }
- }
-
- // Iterate through every block and check the name.
- var functionName = this.getFieldValue('NAME');
- var blocks = this.workspace.getAllBlocks();
- for (var i = 0; i < blocks.length; i++) {
- if (blocks[i].type == 'procedures_callreturn' ||
- blocks[i].type == 'procedures_callnoreturn') {
- var callName = blocks[i].getFieldValue('NAME');
- // Variable name may be null if the block is only half-built.
- if (callName && Blockly.Names.equals(functionName, callName)) {
- blocks[i].setReturn(hasReturns);
- }
- }
- }
- }
- };
- Blockly.Blocks['procedures_defreturn'] = {
- /**
- * Block for defining a procedure with a return value.
- * @this Blockly.Block
- */
- init: function() {
- var nameField = new Blockly.FieldTextInput(
- Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE,
- Blockly.Procedures.rename);
- nameField.setSpellcheck(false);
- this.appendDummyInput()
- .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE)
- .appendField(nameField, 'NAME')
- .appendField('', 'PARAMS');
- this.appendValueInput('RETURN')
- .setAlign(Blockly.ALIGN_RIGHT)
- .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
- this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
- /*if ((this.workspace.options.comments ||
- (this.workspace.options.parentWorkspace &&
- this.workspace.options.parentWorkspace.options.comments)) &&
- Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT) {
- this.setCommentText(Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT);
- }*/
- this.setColour(Blockly.Blocks.procedures.HUE);
- this.setTooltip(Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP);
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL);
- this.arguments_ = [];
- this.setStatements_(true);
- this.statementConnection_ = null;
- },
- setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_,
- updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_,
- mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom,
- domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation,
- decompose: Blockly.Blocks['procedures_defnoreturn'].decompose,
- compose: Blockly.Blocks['procedures_defnoreturn'].compose,
- onchange: Blockly.Blocks['procedures_defnoreturn'].onchange,
- /**
- * Return the signature of this procedure definition.
- * @return {!Array} Tuple containing three elements:
- * - the name of the defined procedure,
- * - a list of all its arguments,
- * - that it DOES have a return value.
- * @this Blockly.Block
- */
- getProcedureDef: function() {
- return [this.getFieldValue('NAME'), this.arguments_, true];
- },
- getVars: Blockly.Blocks['procedures_defnoreturn'].getVars,
- renameVar: Blockly.Blocks['procedures_defnoreturn'].renameVar,
- customContextMenu: Blockly.Blocks['procedures_defnoreturn'].customContextMenu,
- callType_: 'procedures_callreturn'
- };
- Blockly.Blocks['procedures_mutatorcontainer'] = {
- /**
- * Mutator block for procedure container.
- * @this Blockly.Block
- */
- init: function() {
- this.appendDummyInput()
- .appendField(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE);
- this.appendStatementInput('STACK');
- this.appendDummyInput('STATEMENT_INPUT')
- .appendField(Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS)
- .appendField(new Blockly.FieldCheckbox('TRUE'), 'STATEMENTS');
- this.setColour(Blockly.Blocks.procedures.HUE);
- this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP);
- this.contextMenu = false;
- }
- };
- Blockly.Blocks['procedures_mutatorarg'] = {
- /**
- * Mutator block for procedure argument.
- * @this Blockly.Block
- */
- init: function() {
- var field = new Blockly.FieldTextInput('x', this.validator_);
- this.appendDummyInput()
- .appendField(Blockly.Msg.PROCEDURES_MUTATORARG_TITLE)
- .appendField(field, 'NAME');
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setColour(Blockly.Blocks.procedures.HUE);
- this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP);
- this.contextMenu = false;
- // Create the default variable when we drag the block in from the flyout.
- // Have to do this after installing the field on the block.
- field.onFinishEditing_ = this.createNewVar_;
- field.onFinishEditing_('x');
- },
- /**
- * 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;
- },
- /**
- * Called when focusing away from the text field.
- * Creates a new variable with this name.
- * @param {string} newText The new variable name.
- * @private
- * @this Blockly.FieldTextInput
- */
- createNewVar_: function(newText) {
- var source = this.sourceBlock_;
- if (source && source.workspace && source.workspace.options
- && source.workspace.options.parentWorkspace) {
- source.workspace.options.parentWorkspace.createVariable(newText);
- }
- }
- };
- Blockly.Blocks['procedures_callnoreturn'] = {
- /**
- * Block for calling a procedure with no return value.
- * @this Blockly.Block
- */
- init: function() {
- this.appendDummyInput('TOPROW')
- .appendField(this.id, 'NAME');
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setColour(Blockly.Blocks.procedures.HUE);
- // Tooltip is set in renameProcedure.
- this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL);
- this.arguments_ = [];
- this.quarkConnections_ = {};
- this.quarkIds_ = null;
- },
- /**
- * Returns the name of the procedure this block calls.
- * @return {string} Procedure name.
- * @this Blockly.Block
- */
- getProcedureCall: 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.getProcedureCall())) {
- this.setFieldValue(newName, 'NAME');
- this.setTooltip(
- (this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP :
- Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP)
- .replace('%1', newName));
- }
- },
- /**
- * Notification that the procedure's return state has changed.
- * @param {boolean} returnState New return state
- * @this Blockly.Block
- */
- setReturn: function(returnState) {
- if (returnState) {
- this.setPreviousStatement(false);
- this.setNextStatement(false);
- this.setOutput(true);
- } else {
- this.setOutput(false);
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- }
- if (this.rendered) {
- this.render();
- }
- },
- /**
- * Notification that the procedure's parameters have changed.
- * @param {!Array.<string>} paramNames New param names, e.g. ['x', 'y', 'z'].
- * @param {!Array.<string>} paramIds IDs of params (consistent for each
- * parameter through the life of a mutator, regardless of param renaming),
- * e.g. ['piua', 'f8b_', 'oi.o'].
- * @private
- * @this Blockly.Block
- */
- setProcedureParameters_: function(paramNames, paramIds) {
- // Data structures:
- // this.arguments = ['x', 'y']
- // Existing param names.
- // this.quarkConnections_ {piua: null, f8b_: Blockly.Connection}
- // Look-up of paramIds to connections plugged into the call block.
- // this.quarkIds_ = ['piua', 'f8b_']
- // Existing param IDs.
- // Note that quarkConnections_ may include IDs that no longer exist, but
- // which might reappear if a param is reattached in the mutator.
- var defBlock = Blockly.Procedures.getDefinition(this.getProcedureCall(),
- this.workspace);
- var mutatorOpen = defBlock && defBlock.mutator &&
- defBlock.mutator.isVisible();
- if (!mutatorOpen) {
- this.quarkConnections_ = {};
- this.quarkIds_ = null;
- }
- if (!paramIds) {
- // Reset the quarks (a mutator is about to open).
- return;
- }
- if (goog.array.equals(this.arguments_, paramNames)) {
- // No change.
- this.quarkIds_ = paramIds;
- return;
- }
- if (paramIds.length != paramNames.length) {
- throw 'Error: paramNames and paramIds must be the same length.';
- }
- this.setCollapsed(false);
- if (!this.quarkIds_) {
- // Initialize tracking for this block.
- this.quarkConnections_ = {};
- if (paramNames.join('\n') == this.arguments_.join('\n')) {
- // No change to the parameters, allow quarkConnections_ to be
- // populated with the existing connections.
- this.quarkIds_ = paramIds;
- } else {
- this.quarkIds_ = [];
- }
- }
- // Switch off rendering while the block is rebuilt.
- var savedRendered = this.rendered;
- this.rendered = false;
- // Update the quarkConnections_ with existing connections.
- for (var i = 0; i < this.arguments_.length; i++) {
- var input = this.getInput('ARG' + i);
- if (input) {
- var connection = input.connection.targetConnection;
- this.quarkConnections_[this.quarkIds_[i]] = connection;
- if (mutatorOpen && connection &&
- paramIds.indexOf(this.quarkIds_[i]) == -1) {
- // This connection should no longer be attached to this block.
- connection.disconnect();
- connection.getSourceBlock().bumpNeighbours_();
- }
- }
- }
- // Rebuild the block's arguments.
- this.arguments_ = [].concat(paramNames);
- this.updateShape_();
- this.quarkIds_ = paramIds;
- // Reconnect any child blocks.
- if (this.quarkIds_) {
- for (var i = 0; i < this.arguments_.length; i++) {
- var quarkId = this.quarkIds_[i];
- if (quarkId in this.quarkConnections_) {
- var connection = this.quarkConnections_[quarkId];
- if (!Blockly.Mutator.reconnect(connection, this, 'ARG' + i)) {
- // Block no longer exists or has been attached elsewhere.
- delete this.quarkConnections_[quarkId];
- }
- }
- }
- }
- // Restore rendering and show the changes.
- this.rendered = savedRendered;
- if (this.rendered) {
- this.render();
- }
- },
- /**
- * Modify this block to have the correct number of arguments.
- * @private
- * @this Blockly.Block
- */
- updateShape_: function() {
- for (var i = 0; i < this.arguments_.length; i++) {
- if (this.arguments_[i] != "") {
- var field = this.getField('ARGNAME' + i);
- if (field) {
- // Ensure argument name is up to date.
- // The argument name field is deterministic based on the mutation,
- // no need to fire a change event.
- Blockly.Events.disable();
- try {
- field.setValue(this.arguments_[i]);
- } finally {
- Blockly.Events.enable();
- }
- } else {
- // Add new input.
- field = new Blockly.FieldLabel(this.arguments_[i]);
- var input = this.appendValueInput('ARG' + i)
- .setAlign(Blockly.ALIGN_RIGHT)
- .appendField(field, 'ARGNAME' + i);
- input.init();
- }
- } else {
- if (!this.getInput('ARG' + i)) {
- var input = this.appendValueInput('ARG' + i)
- .setAlign(Blockly.ALIGN_RIGHT);
- //.appendField(field, 'ARGNAME' + i);
- input.init();
- }
- }
- }
- // Remove deleted inputs.
- while (this.getInput('ARG' + i)) {
- this.removeInput('ARG' + i);
- i++;
- }
- // Add 'with:' if there are parameters, remove otherwise.
- /*
- var topRow = this.getInput('TOPROW');
- if (topRow) {
- if (this.arguments_.length) {
- if (!this.getField('WITH')) {
- topRow.appendField(Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS, 'WITH');
- topRow.init();
- }
- } else {
- if (this.getField('WITH')) {
- topRow.removeField('WITH');
- }
- }
- }
- */
- },
- /**
- * 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.getProcedureCall());
- for (var i = 0; i < this.arguments_.length; i++) {
- var parameter = document.createElement('arg');
- parameter.setAttribute('name', this.arguments_[i]);
- container.appendChild(parameter);
- }
- 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.renameProcedure(this.getProcedureCall(), name);
- var args = [];
- var paramIds = [];
- for (var i = 0, childNode; childNode = xmlElement.childNodes[i]; i++) {
- if (childNode.nodeName.toLowerCase() == 'arg') {
- args.push(childNode.getAttribute('name'));
- paramIds.push(childNode.getAttribute('paramId'));
- }
- }
- this.setProcedureParameters_(args, paramIds);
- },
- /**
- * 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) {
- for (var i = 0; i < this.arguments_.length; i++) {
- if (Blockly.Names.equals(oldName, this.arguments_[i])) {
- this.arguments_[i] = newName;
- var argname = this.getField('ARGNAME' + i);
- if (argname) {
- argname.setValue(newName);
- }
- }
- }
- },
- /**
- * Procedure calls cannot exist without the corresponding procedure
- * definition. Enforce this link whenever an event is fired.
- * @this Blockly.Block
- */
- onchange: function(event) {
- if (!this.workspace || this.workspace.isFlyout) {
- // Block is deleted or is in a flyout.
- return;
- }
- /*
- if (event.type == Blockly.Events.CREATE &&
- event.ids.indexOf(this.id) != -1) {
- // Look for the case where a procedure call was created (usually through
- // paste) and there is no matching definition. In this case, create
- // an empty definition block with the correct signature.
- var name = this.getProcedureCall();
- var def = Blockly.Procedures.getDefinition(name, this.workspace);
- if (def && (def.type != this.defType_ ||
- JSON.stringify(def.arguments_) != JSON.stringify(this.arguments_))) {
- // The signatures don't match.
- def = null;
- }
- if (!def) {
- Blockly.Events.setGroup(event.group);
- */ /**
- * Create matching definition block.
- * <xml>
- * <block type="procedures_defreturn" x="10" y="20">
- * <mutation name="test">
- * <arg name="x"></arg>
- * </mutation>
- * <field name="NAME">test</field>
- * </block>
- * </xml>
- */
- /* var xml = goog.dom.createDom('xml');
- var block = goog.dom.createDom('block');
- block.setAttribute('type', this.defType_);
- var xy = this.getRelativeToSurfaceXY();
- var x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1);
- var y = xy.y + Blockly.SNAP_RADIUS * 2;
- block.setAttribute('x', x);
- block.setAttribute('y', y);
- var mutation = this.mutationToDom();
- block.appendChild(mutation);
- var field = goog.dom.createDom('field');
- field.setAttribute('name', 'NAME');
- field.appendChild(document.createTextNode(this.getProcedureCall()));
- block.appendChild(field);
- xml.appendChild(block);
- Blockly.Xml.domToWorkspace(xml, this.workspace);
- Blockly.Events.setGroup(false);
- }
- } else if (event.type == Blockly.Events.DELETE) {
- // Look for the case where a procedure definition has been deleted,
- // leaving this block (a procedure call) orphaned. In this case, delete
- // the orphan.
- var name = this.getProcedureCall();
- var def = Blockly.Procedures.getDefinition(name, this.workspace);
- if (!def) {
- Blockly.Events.setGroup(event.group);
- this.dispose(true, false);
- Blockly.Events.setGroup(false);
- }
- }*/
- },
- /**
- * Add menu option to find the definition block for this call.
- * @param {!Array} options List of menu options to add to.
- * @this Blockly.Block
- */
- customContextMenu: function(options) {
- var option = {enabled: true};
- option.text = Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF;
- var name = this.getProcedureCall();
- var workspace = this.workspace;
- option.callback = function() {
- var def = Blockly.Procedures.getDefinition(name, workspace);
- def && def.select();
- };
- options.push(option);
- },
- defType_: 'procedures_defnoreturn'
- };
- Blockly.Blocks['procedures_callreturn'] = {
- /**
- * Block for calling a procedure with a return value.
- * @this Blockly.Block
- */
- init: function() {
- this.appendDummyInput('TOPROW')
- .appendField('', 'NAME');
- this.setOutput(true);
- this.setColour(Blockly.Blocks.procedures.HUE);
- // Tooltip is set in domToMutation.
- this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL);
- this.arguments_ = [];
- this.quarkConnections_ = {};
- this.quarkIds_ = null;
- },
- getProcedureCall: Blockly.Blocks['procedures_callnoreturn'].getProcedureCall,
- renameProcedure: Blockly.Blocks['procedures_callnoreturn'].renameProcedure,
- setProcedureParameters_:
- Blockly.Blocks['procedures_callnoreturn'].setProcedureParameters_,
- updateShape_: Blockly.Blocks['procedures_callnoreturn'].updateShape_,
- mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom,
- domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation,
- renameVar: Blockly.Blocks['procedures_callnoreturn'].renameVar,
- onchange: Blockly.Blocks['procedures_callnoreturn'].onchange,
- setReturn: Blockly.Blocks['procedures_callnoreturn'].setReturn,
- customContextMenu:
- Blockly.Blocks['procedures_callnoreturn'].customContextMenu,
- defType_: 'procedures_defreturn'
- };
- Blockly.Blocks['procedures_ifreturn'] = {
- /**
- * Block for conditionally returning a value from a procedure.
- * @this Blockly.Block
- */
- init: function() {
- this.appendValueInput('CONDITION')
- .setCheck('Boolean')
- .appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);
- this.appendValueInput('VALUE')
- .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
- this.setInputsInline(true);
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setColour(Blockly.Blocks.procedures.HUE);
- this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);
- this.setHelpUrl(Blockly.Msg.PROCEDURES_IFRETURN_HELPURL);
- this.hasReturnValue_ = true;
- },
- /**
- * Create XML to represent whether this block has a return value.
- * @return {!Element} XML storage element.
- * @this Blockly.Block
- */
- mutationToDom: function() {
- var container = document.createElement('mutation');
- container.setAttribute('value', Number(this.hasReturnValue_));
- return container;
- },
- /**
- * Parse XML to restore whether this block has a return value.
- * @param {!Element} xmlElement XML storage element.
- * @this Blockly.Block
- */
- domToMutation: function(xmlElement) {
- var value = xmlElement.getAttribute('value');
- this.hasReturnValue_ = (value == 1);
- if (!this.hasReturnValue_) {
- this.removeInput('VALUE');
- this.appendDummyInput('VALUE')
- .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
- }
- },
- /**
- * Called whenever anything on the workspace changes.
- * Add warning if this flow block is not nested inside a loop.
- * @param {!Blockly.Events.Abstract} e Change event.
- * @this Blockly.Block
- */
- onchange: function(e) {
- if (this.workspace.isDragging()) {
- return; // Don't change state at the start of a drag.
- }
- var legal = false;
- // Is the block nested in a procedure?
- var block = this;
- do {
- if (this.FUNCTION_TYPES.indexOf(block.type) != -1) {
- legal = true;
- break;
- }
- block = block.getSurroundParent();
- } while (block);
- if (legal) {
- /*
- // If needed, toggle whether this block has a return value.
- if (block.type == 'procedures_defnoreturn' && this.hasReturnValue_) {
- this.removeInput('VALUE');
- this.appendDummyInput('VALUE')
- .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
- this.hasReturnValue_ = false;
- } else if (block.type == 'procedures_defreturn' &&
- !this.hasReturnValue_) {
- this.removeInput('VALUE');
- this.appendValueInput('VALUE')
- .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
- this.hasReturnValue_ = true;
- }
- */
- this.setWarningText(null);
- if (!this.isInFlyout) {
- this.setDisabled(false);
- }
- } else {
- this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING);
- if (!this.isInFlyout && !this.getInheritedDisabled()) {
- this.setDisabled(true);
- }
- }
- },
- /**
- * List of block types that are functions and thus do not need warnings.
- * 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']
- };
- Blockly.Blocks['procedures_return'] = {
- /**
- * Block for returning a value from a procedure.
- * @this Blockly.Block
- */
- init: function() {
- this.setHelpUrl('http://c2.com/cgi/wiki?GuardClause');
- this.setColour(Blockly.Blocks.procedures.HUE);
- this.appendValueInput('VALUE')
- .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
- this.setInputsInline(false);
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);
- this.hasReturnValue_ = true;
- },
- /**
- * Called whenever anything on the workspace changes.
- * Add warning if this flow block is not nested inside a loop.
- * @this Blockly.Block
- */
- onchange: function() {
- if (!this.workspace) {
- // Block has been deleted.
- return;
- }
- var legal = false;
- // Is the block nested in a procedure?
- var block = this;
- do {
- if (block.type == 'procedures_defnoreturn' ||
- block.type == 'procedures_defreturn') {
- legal = true;
- break;
- }
- block = block.getSurroundParent();
- } while (block);
- if (legal) {
- this.setWarningText(null);
- } else {
- this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING);
- }
- }
- };
- Blockly.Blocks['procedures_main'] = {
- /**
- * Block for returning a value from a procedure.
- * @this Blockly.Block
- */
- init: function() {
- this.setColour(Blockly.Blocks.procedures.HUE);
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.appendDummyInput()
- .appendField(Blockly.Msg.PROCEDURES_MAINFUNCTION)
- this.setStatements_(true);
- this.statementConnection_ = null;
- },
- setStatements_: function(hasStatements) {
- if (this.hasStatements_ === hasStatements) {
- return;
- }
- if (hasStatements) {
- this.appendStatementInput('STACK')
- } else {
- this.removeInput('STACK', true);
- }
- this.hasStatements_ = hasStatements;
- },
- };
|