text.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /**
  2. * @license
  3. * Visual Blocks Language
  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 Generating JavaScript for text blocks.
  22. * @author fraser@google.com (Neil Fraser)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.JavaScript.texts');
  26. goog.require('Blockly.JavaScript');
  27. Blockly.JavaScript['text'] = function(block) {
  28. // Text value.
  29. var code = Blockly.JavaScript.quote_(block.getFieldValue('TEXT'));
  30. return [code, Blockly.JavaScript.ORDER_ATOMIC];
  31. };
  32. Blockly.JavaScript['text_join'] = function(block) {
  33. // Create a string made up of any number of elements of any type.
  34. switch (block.itemCount_) {
  35. case 0:
  36. return ['\'\'', Blockly.JavaScript.ORDER_ATOMIC];
  37. case 1:
  38. var element = Blockly.JavaScript.valueToCode(block, 'ADD0',
  39. Blockly.JavaScript.ORDER_NONE) || '\'\'';
  40. var code = 'String(' + element + ')';
  41. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  42. case 2:
  43. var element0 = Blockly.JavaScript.valueToCode(block, 'ADD0',
  44. Blockly.JavaScript.ORDER_NONE) || '\'\'';
  45. var element1 = Blockly.JavaScript.valueToCode(block, 'ADD1',
  46. Blockly.JavaScript.ORDER_NONE) || '\'\'';
  47. var code = 'String(' + element0 + ') + String(' + element1 + ')';
  48. return [code, Blockly.JavaScript.ORDER_ADDITION];
  49. default:
  50. var elements = new Array(block.itemCount_);
  51. for (var i = 0; i < block.itemCount_; i++) {
  52. elements[i] = Blockly.JavaScript.valueToCode(block, 'ADD' + i,
  53. Blockly.JavaScript.ORDER_COMMA) || '\'\'';
  54. }
  55. var code = '[' + elements.join(',') + '].join(\'\')';
  56. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  57. }
  58. };
  59. Blockly.JavaScript['text_append'] = function(block) {
  60. // Append to a variable in place.
  61. var varName = Blockly.JavaScript.variableDB_.getName(
  62. block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
  63. var value = Blockly.JavaScript.valueToCode(block, 'TEXT',
  64. Blockly.JavaScript.ORDER_NONE) || '\'\'';
  65. return varName + ' = String(' + varName + ') + String(' + value + ');\n';
  66. };
  67. Blockly.JavaScript['text_length'] = function(block) {
  68. // String or array length.
  69. var text = Blockly.JavaScript.valueToCode(block, 'VALUE',
  70. Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\'';
  71. return [text + '.length', Blockly.JavaScript.ORDER_MEMBER];
  72. };
  73. Blockly.JavaScript['text_isEmpty'] = function(block) {
  74. // Is the string null or array empty?
  75. var text = Blockly.JavaScript.valueToCode(block, 'VALUE',
  76. Blockly.JavaScript.ORDER_MEMBER) || '\'\'';
  77. return ['!' + text + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT];
  78. };
  79. Blockly.JavaScript['text_indexOf'] = function(block) {
  80. // Search the text for a substring.
  81. var operator = block.getFieldValue('END') == 'FIRST' ?
  82. 'indexOf' : 'lastIndexOf';
  83. var substring = Blockly.JavaScript.valueToCode(block, 'FIND',
  84. Blockly.JavaScript.ORDER_NONE) || '\'\'';
  85. var text = Blockly.JavaScript.valueToCode(block, 'VALUE',
  86. Blockly.JavaScript.ORDER_MEMBER) || '\'\'';
  87. var code = text + '.' + operator + '(' + substring + ')';
  88. // Adjust index if using one-based indices.
  89. if (Blockly.JavaScript.ONE_BASED_INDEXING) {
  90. return [code + ' + 1', Blockly.JavaScript.ORDER_ADDITION];
  91. }
  92. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  93. };
  94. Blockly.JavaScript['text_charAt'] = function(block) {
  95. // Get letter at index.
  96. // Note: Until January 2013 this block did not have the WHERE input.
  97. var where = block.getFieldValue('WHERE') || 'FROM_START';
  98. var textOrder = (where == 'RANDOM') ? Blockly.JavaScript.ORDER_NONE :
  99. Blockly.JavaScript.ORDER_MEMBER;
  100. var text = Blockly.JavaScript.valueToCode(block, 'VALUE',
  101. textOrder) || '\'\'';
  102. switch (where) {
  103. case 'FIRST':
  104. var code = text + '.charAt(0)';
  105. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  106. case 'LAST':
  107. var code = text + '.slice(-1)';
  108. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  109. case 'FROM_START':
  110. var at = Blockly.JavaScript.getAdjusted(block, 'AT');
  111. // Adjust index if using one-based indices.
  112. var code = text + '.charAt(' + at + ')';
  113. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  114. case 'FROM_END':
  115. var at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, true);
  116. var code = text + '.slice(' + at + ').charAt(0)';
  117. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  118. case 'RANDOM':
  119. var functionName = Blockly.JavaScript.provideFunction_(
  120. 'textRandomLetter',
  121. ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
  122. '(text) {',
  123. ' var x = Math.floor(Math.random() * text.length);',
  124. ' return text[x];',
  125. '}']);
  126. var code = functionName + '(' + text + ')';
  127. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  128. }
  129. throw 'Unhandled option (text_charAt).';
  130. };
  131. /**
  132. * Returns an expression calculating the index into a string.
  133. * @private
  134. * @param {string} stringName Name of the string, used to calculate length.
  135. * @param {string} where The method of indexing, selected by dropdown in Blockly
  136. * @param {string=} opt_at The optional offset when indexing from start/end.
  137. * @return {string} Index expression.
  138. */
  139. Blockly.JavaScript.text.getIndex_ = function(stringName, where, opt_at) {
  140. if (where == 'FIRST') {
  141. return '0';
  142. } else if (where == 'FROM_END') {
  143. return stringName + '.length - 1 - ' + opt_at;
  144. } else if (where == 'LAST') {
  145. return stringName + '.length - 1';
  146. } else {
  147. return opt_at;
  148. }
  149. };
  150. Blockly.JavaScript['text_getSubstring'] = function(block) {
  151. // Get substring.
  152. var text = Blockly.JavaScript.valueToCode(block, 'STRING',
  153. Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\'';
  154. var where1 = block.getFieldValue('WHERE1');
  155. var where2 = block.getFieldValue('WHERE2');
  156. if (where1 == 'FIRST' && where2 == 'LAST') {
  157. var code = text;
  158. } else if (text.match(/^'?\w+'?$/) ||
  159. (where1 != 'FROM_END' && where1 != 'LAST' &&
  160. where2 != 'FROM_END' && where2 != 'LAST')) {
  161. // If the text is a variable or literal or doesn't require a call for
  162. // length, don't generate a helper function.
  163. switch (where1) {
  164. case 'FROM_START':
  165. var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1');
  166. break;
  167. case 'FROM_END':
  168. var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, false,
  169. Blockly.JavaScript.ORDER_SUBTRACTION);
  170. at1 = text + '.length - ' + at1;
  171. break;
  172. case 'FIRST':
  173. var at1 = '0';
  174. break;
  175. default:
  176. throw 'Unhandled option (text_getSubstring).';
  177. }
  178. switch (where2) {
  179. case 'FROM_START':
  180. var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 1);
  181. break;
  182. case 'FROM_END':
  183. var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 0, false,
  184. Blockly.JavaScript.ORDER_SUBTRACTION);
  185. at2 = text + '.length - ' + at2;
  186. break;
  187. case 'LAST':
  188. var at2 = text + '.length';
  189. break;
  190. default:
  191. throw 'Unhandled option (text_getSubstring).';
  192. }
  193. code = text + '.slice(' + at1 + ', ' + at2 + ')';
  194. } else {
  195. var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1');
  196. var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2');
  197. var getIndex_ = Blockly.JavaScript.text.getIndex_;
  198. var wherePascalCase = {'FIRST': 'First', 'LAST': 'Last',
  199. 'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'};
  200. var functionName = Blockly.JavaScript.provideFunction_(
  201. 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2],
  202. ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
  203. '(sequence' +
  204. // The value for 'FROM_END' and'FROM_START' depends on `at` so
  205. // we add it as a parameter.
  206. ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', at1' : '') +
  207. ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', at2' : '') +
  208. ') {',
  209. ' var start = ' + getIndex_('sequence', where1, 'at1') + ';',
  210. ' var end = ' + getIndex_('sequence', where2, 'at2') + ' + 1;',
  211. ' return sequence.slice(start, end);',
  212. '}']);
  213. var code = functionName + '(' + text +
  214. // The value for 'FROM_END' and 'FROM_START' depends on `at` so we
  215. // pass it.
  216. ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', ' + at1 : '') +
  217. ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', ' + at2 : '') +
  218. ')';
  219. }
  220. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  221. };
  222. Blockly.JavaScript['text_changeCase'] = function(block) {
  223. // Change capitalization.
  224. var OPERATORS = {
  225. 'UPPERCASE': '.toUpperCase()',
  226. 'LOWERCASE': '.toLowerCase()',
  227. 'TITLECASE': null
  228. };
  229. var operator = OPERATORS[block.getFieldValue('CASE')];
  230. var textOrder = operator ? Blockly.JavaScript.ORDER_MEMBER :
  231. Blockly.JavaScript.ORDER_NONE;
  232. var text = Blockly.JavaScript.valueToCode(block, 'TEXT',
  233. textOrder) || '\'\'';
  234. if (operator) {
  235. // Upper and lower case are functions built into JavaScript.
  236. var code = text + operator;
  237. } else {
  238. // Title case is not a native JavaScript function. Define one.
  239. var functionName = Blockly.JavaScript.provideFunction_(
  240. 'textToTitleCase',
  241. ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
  242. '(str) {',
  243. ' return str.replace(/\\S+/g,',
  244. ' function(txt) {return txt[0].toUpperCase() + ' +
  245. 'txt.substring(1).toLowerCase();});',
  246. '}']);
  247. var code = functionName + '(' + text + ')';
  248. }
  249. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  250. };
  251. Blockly.JavaScript['text_trim'] = function(block) {
  252. // Trim spaces.
  253. var OPERATORS = {
  254. 'LEFT': ".replace(/^[\\s\\xa0]+/, '')",
  255. 'RIGHT': ".replace(/[\\s\\xa0]+$/, '')",
  256. 'BOTH': '.trim()'
  257. };
  258. var operator = OPERATORS[block.getFieldValue('MODE')];
  259. var text = Blockly.JavaScript.valueToCode(block, 'TEXT',
  260. Blockly.JavaScript.ORDER_MEMBER) || '\'\'';
  261. return [text + operator, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  262. };
  263. Blockly.JavaScript['text_print'] = function(block) {
  264. // Print statement.
  265. var msg = Blockly.JavaScript.valueToCode(block, 'TEXT',
  266. Blockly.JavaScript.ORDER_NONE) || '\'\'';
  267. return 'window.alert(' + msg + ');\n';
  268. };
  269. Blockly.JavaScript['text_prompt_ext'] = function(block) {
  270. // Prompt function.
  271. if (block.getField('TEXT')) {
  272. // Internal message.
  273. var msg = Blockly.JavaScript.quote_(block.getFieldValue('TEXT'));
  274. } else {
  275. // External message.
  276. var msg = Blockly.JavaScript.valueToCode(block, 'TEXT',
  277. Blockly.JavaScript.ORDER_NONE) || '\'\'';
  278. }
  279. var code = 'window.prompt(' + msg + ')';
  280. var toNumber = block.getFieldValue('TYPE') == 'NUMBER';
  281. if (toNumber) {
  282. code = 'parseFloat(' + code + ')';
  283. }
  284. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  285. };
  286. Blockly.JavaScript['text_prompt'] = Blockly.JavaScript['text_prompt_ext'];