/** * @license Licensed under the Apache License, Version 2.0 (the "License"): * http://www.apache.org/licenses/LICENSE-2.0 */ /** * @fileoverview Utility functions for handling Instances. */ 'use strict'; goog.provide('Blockly.Instances'); goog.require('Blockly.Workspace'); goog.require('goog.string'); /** * Category to separate instance names from instances, procedures and * generated functions. */ Blockly.Instances.NAME_TYPE = 'INSTANCE'; /** * Find all user-created instances. * @param {?string} * @param {!Blockly.Workspace} workspace Workspace to transverse for instances. * @return {!Array.} Array of instance names. */ Blockly.Instances.allInstancesOf = function(instanceType, workspace) { var blocks; if (workspace.getAllBlocks) { blocks = workspace.getAllBlocks(); } else { throw 'Not a valid Workspace: ' + workspace; } var instanceHash = Object.create(null); // Iterate through every block and add each instance to the hash. for (var i = 0; i < blocks.length; i++) { var blockInstances = blocks[i].getInstances(instanceType); for (var j = 0; j < blockInstances.length; j++) { var instanceName = blockInstances[j]; // Instance name may be null if the block is only half-built. if (instanceName) { instanceHash[instanceName.toLowerCase()] = instanceName; } } } // Flatten the hash into a list. var instanceList = []; for (var name in instanceHash) { instanceList.push(instanceHash[name]); } return instanceList; }; /** Returns all instances of all types. */ Blockly.Instances.allInstances = function(workspace) { return Blockly.Instances.allInstancesOf(undefined, workspace); }; /** Returns the first Instance name of a given type. */ Blockly.Instances.getAnyInstanceOf = function(instanceType, workspace) { var blocks; if (workspace.getAllBlocks) { blocks = workspace.getAllBlocks(); } else { throw 'Not a valid Workspace: ' + workspace; } for (var i = 0; i < blocks.length; i++) { var blockInstances = blocks[i].getInstances(instanceType); if (blockInstances.length) { return blockInstances[0]; } } }; /** Indicates if the given instance is present in the workspace. */ Blockly.Instances.isInstancePresent = function( instanceName, instanceType, block) { var blocks; if (block.workspace && block.workspace.getAllBlocks) { blocks = block.workspace.getAllBlocks(); } else { throw 'Not a valid block: ' + block; } for (var i = 0; i < blocks.length; i++) { var blockInstances = blocks[i].getInstances(instanceType); for (var j = 0; j < blockInstances.length; j++) { if ((blockInstances[j] === instanceName) && (blocks[i] !== block)) { return true; } } } return false; }; /** * Find all instances of the specified name and type and rename them. * @param {string} oldName Instance to rename. * @param {string} newName New Instance name. * @param {!Blockly.Workspace} workspace Workspace rename instances in. */ Blockly.Instances.renameInstance = function( oldName, newName, instanceType, workspace) { Blockly.Events.setGroup(true); var blocks = workspace.getAllBlocks(); // Iterate through every block. for (var i = 0; i < blocks.length; i++) { blocks[i].renameInstance(oldName, newName, instanceType); } Blockly.Events.setGroup(false); }; /** * Return a new instance name that is not yet being used as an instance or as a * variable name. This will try to generate single letter names in the range * 'i' to 'z' to start with. * If no unique name is located it will try 'i' to 'z', 'a' to 'h', * then 'i2' to 'z2' etc. Skip 'l'. * @param {!Blockly.Workspace} workspace The workspace to be unique in. * @return {string} New instance name. */ Blockly.Instances.generateUniqueName = function(workspace) { var combinedList = Blockly.Variables.allVariables(workspace).concat( Blockly.Instances.allInstances(workspace)); var newName = ''; if (combinedList.length) { var nameSuffix = 1; var letters = 'ijkmnopqrstuvwxyzabcdefgh'; // No 'l'. var letterIndex = 0; var potName = letters.charAt(letterIndex); while (!newName) { var inUse = false; for (var i = 0; i < combinedList.length; i++) { if (combinedList[i].toLowerCase() == potName) { // This potential name is already used. inUse = true; break; } } if (inUse) { // Try the next potential name. letterIndex++; if (letterIndex == letters.length) { // Reached the end of the character sequence so back to 'i'. // a new suffix. letterIndex = 0; nameSuffix++; } potName = letters.charAt(letterIndex); if (nameSuffix > 1) { potName += nameSuffix; } } else { // We can use the current potential name. newName = potName; } } } else { newName = 'i'; } return newName; }; /** * Return a version of the instance name that has not yet been used. * It does so by adding a number at the end of the name. * @param {string} instanceName Instance name to make unique. * @param {!Blockly.Workspace|Blockly.Block} workspace The workspace to be unique in. * @return {string} Unique instance name based on name input. */ Blockly.Instances.convertToUniqueName = function(instanceName, workspace) { var combinedList = Blockly.Variables.allVariables(workspace).concat( Blockly.Instances.allInstances(workspace)); return Blockly.Instances.appendToName_(instanceName, combinedList); }; /** */ Blockly.Instances.convertToUniqueNameBlock = function(instanceName, block) { var blocks; if (block.workspace) { blocks = block.workspace.getAllBlocks(); } else { throw 'Not a valid Workspace: ' + workspace; } var instanceHash = Object.create(null); // Iterate through every block and add each instance to the hash. for (var i = 0; i < blocks.length; i++) { // Do not add to the list this block instance if (blocks[i] !== block) { var blockInstances = blocks[i].getInstances(); for (var j = 0; j < blockInstances.length; j++) { var blockInstanceName = blockInstances[j]; // Instance name may be null if the block is only half-built. if (blockInstanceName) { instanceHash[blockInstanceName.toLowerCase()] = blockInstanceName; } } } } // Flatten the hash into a list. var instanceList = []; for (var name in instanceHash) { instanceList.push(instanceHash[name]); } var combinedList = Blockly.Variables.allVariables(block.workspace).concat( instanceList); return Blockly.Instances.appendToName_(instanceName, combinedList); }; /** */ Blockly.Instances.appendToName_ = function(instanceName, nameList) { if (!instanceName) { return Blockly.Instances.generateUniqueName(workspace); } else { var newName = instanceName; var nameSuffix = 2; if (instanceName.match(/_\d+$/)) { // instanceName ends with and underscore and a number, so increase count var instanceNameSuffix = instanceName.match(/\d+$/)[0]; instanceName = instanceName.slice( 0, (instanceNameSuffix.length * -1) - 1); nameSuffix = parseInt(instanceNameSuffix, 10) + 1; newName = instanceName + '_' + nameSuffix; } while (nameList.indexOf(newName) !== -1) { newName = instanceName + '_' + nameSuffix++; } return newName; } };