variables.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /**
  2. * @license
  3. * Visual Blocks Editor
  4. *
  5. * Copyright 2012 Google Inc.
  6. * https://developers.google.com/blockly/
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. /**
  21. * @fileoverview Utility functions for handling variables.
  22. * @author fraser@google.com (Neil Fraser)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.Variables');
  26. goog.require('Blockly.Blocks');
  27. goog.require('Blockly.Workspace');
  28. goog.require('goog.string');
  29. /**
  30. * Category to separate variable names from procedures and generated functions.
  31. */
  32. Blockly.Variables.NAME_TYPE = 'VARIABLE';
  33. /**
  34. * Find all user-created variables.
  35. * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace.
  36. * @return {!Array.<string>} Array of variable names.
  37. */
  38. Blockly.Variables.allVariables = function(root) {
  39. var blocks;
  40. if (root.getDescendants) {
  41. // Root is Block.
  42. blocks = root.getDescendants();
  43. } else if (root.getAllBlocks) {
  44. // Root is Workspace.
  45. blocks = root.getAllBlocks();
  46. } else {
  47. throw 'Not Block or Workspace: ' + root;
  48. }
  49. var variableHash = Object.create(null);
  50. // Iterate through every block and add each variable to the hash.
  51. for (var i = 0; i < blocks.length; i++) {
  52. var blockVariables = blocks[i].getVars();
  53. for (var j = 0; j < blockVariables.length; j++) {
  54. var varName = blockVariables[j];
  55. // Variable name may be null if the block is only half-built.
  56. if (varName) {
  57. variableHash[varName.toLowerCase()] = varName;
  58. }
  59. }
  60. }
  61. // Flatten the hash into a list.
  62. var variableList = [];
  63. for (var name in variableHash) {
  64. variableList.push(variableHash[name]);
  65. }
  66. return variableList;
  67. };
  68. /**
  69. * Find all instances of the specified variable and rename them.
  70. * @param {string} oldName Variable to rename.
  71. * @param {string} newName New variable name.
  72. * @param {!Blockly.Workspace} workspace Workspace rename variables in.
  73. */
  74. Blockly.Variables.renameVariable = function(oldName, newName, workspace) {
  75. Blockly.Events.setGroup(true);
  76. var blocks = workspace.getAllBlocks();
  77. // Iterate through every block.
  78. for (var i = 0; i < blocks.length; i++) {
  79. blocks[i].renameVar(oldName, newName);
  80. }
  81. Blockly.Events.setGroup(false);
  82. };
  83. // getInstances is used for BlocksCAD - keep it!
  84. /**
  85. * Find all instances of a particular variable and return the list of blocks.
  86. * @param {string} name Name of the variable to find instances of
  87. * @param {!Blockly.Workspace} workspace Workspace to find variables in.
  88. */
  89. Blockly.Variables.getInstances = function(name, workspace) {
  90. if (!workspace)
  91. return;
  92. var blocks = workspace.getAllBlocks();
  93. var instances = [];
  94. // Iterate through every block.
  95. for (var i = 0; i < blocks.length; i++) {
  96. if (blocks[i].getFieldValue('VAR')) {
  97. if (blocks[i].getFieldValue('VAR') == name)
  98. instances.push(blocks[i]);
  99. }
  100. }
  101. if (instances.length) return instances;
  102. return null;
  103. }
  104. /**
  105. * Construct the blocks required by the flyout for the variable category.
  106. * @param {!Blockly.Workspace} workspace The workspace contianing variables.
  107. * @return {!Array.<!Element>} Array of XML block elements.
  108. */
  109. Blockly.Variables.flyoutCategory = function(workspace) {
  110. var variableList = Blockly.Variables.allVariables(workspace);
  111. variableList.sort(goog.string.caseInsensitiveCompare);
  112. // In addition to the user's variables, we also want to display the default
  113. // variable name at the top. We also don't want this duplicated if the
  114. // user has created a variable of the same name.
  115. goog.array.remove(variableList, Blockly.Msg.VARIABLES_DEFAULT_NAME);
  116. variableList.unshift(Blockly.Msg.VARIABLES_DEFAULT_NAME);
  117. var xmlList = [];
  118. for (var i = 0; i < variableList.length; i++) {
  119. if (Blockly.Blocks['variables_set']) {
  120. // <block type="variables_set" gap="8">
  121. // <field name="VAR">item</field>
  122. // </block>
  123. var block = goog.dom.createDom('block');
  124. block.setAttribute('type', 'variables_set');
  125. if (Blockly.Blocks['variables_get']) {
  126. block.setAttribute('gap', 8);
  127. }
  128. var field = goog.dom.createDom('field', null, variableList[i]);
  129. field.setAttribute('name', 'VAR');
  130. block.appendChild(field);
  131. xmlList.push(block);
  132. }
  133. if (Blockly.Blocks['variables_get']) {
  134. // <block type="variables_get" gap="24">
  135. // <field name="VAR">item</field>
  136. // </block>
  137. var block = goog.dom.createDom('block');
  138. block.setAttribute('type', 'variables_get');
  139. if (Blockly.Blocks['variables_set']) {
  140. block.setAttribute('gap', 24);
  141. }
  142. var field = goog.dom.createDom('field', null, variableList[i]);
  143. field.setAttribute('name', 'VAR');
  144. block.appendChild(field);
  145. xmlList.push(block);
  146. }
  147. }
  148. return xmlList;
  149. };
  150. /**
  151. * Return a new variable name that is not yet being used. This will try to
  152. * generate single letter variable names in the range 'i' to 'z' to start with.
  153. * If no unique name is located it will try 'i' to 'z', 'a' to 'h',
  154. * then 'i2' to 'z2' etc. Skip 'l'.
  155. * @param {!Blockly.Workspace} workspace The workspace to be unique in.
  156. * @return {string} New variable name.
  157. */
  158. Blockly.Variables.generateUniqueName = function(workspace) {
  159. var variableList = Blockly.Variables.allVariables(workspace);
  160. var newName = '';
  161. if (variableList.length) {
  162. var nameSuffix = 1;
  163. var letters = 'ijkmnopqrstuvwxyzabcdefgh'; // No 'l'.
  164. var letterIndex = 0;
  165. var potName = letters.charAt(letterIndex);
  166. while (!newName) {
  167. var inUse = false;
  168. for (var i = 0; i < variableList.length; i++) {
  169. if (variableList[i].toLowerCase() == potName) {
  170. // This potential name is already used.
  171. inUse = true;
  172. break;
  173. }
  174. }
  175. if (inUse) {
  176. // Try the next potential name.
  177. letterIndex++;
  178. if (letterIndex == letters.length) {
  179. // Reached the end of the character sequence so back to 'i'.
  180. // a new suffix.
  181. letterIndex = 0;
  182. nameSuffix++;
  183. }
  184. potName = letters.charAt(letterIndex);
  185. if (nameSuffix > 1) {
  186. potName += nameSuffix;
  187. }
  188. } else {
  189. // We can use the current potential name.
  190. newName = potName;
  191. }
  192. }
  193. } else {
  194. newName = 'i';
  195. }
  196. return newName;
  197. };