utilities.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /**
  2. * Determines if the element is in the list.
  3. * @param {anything} needle - The element to look for.
  4. * @param {Array} haystack - The list to search.
  5. * @return {Boolean} Whether the element exists
  6. */
  7. function arrayContains(needle, haystack) {
  8. return haystack.indexOf(needle) > -1;
  9. }
  10. /**
  11. * Remove duplicate values from an array, preserving order.
  12. * Creates a new array, so is non-destructive.
  13. * Courtesy:
  14. * https://stackoverflow.com/questions/1584370/how-to-merge-two-arrays-in-javascript-and-de-duplicate-items
  15. *
  16. * @param {Array} array - The array to uniquify. Elements compared with ===.
  17. */
  18. function arrayUnique(array) {
  19. var a = array.concat();
  20. for(var i=0; i<a.length; ++i) {
  21. for(var j=i+1; j<a.length; ++j) {
  22. if(a[i] === a[j])
  23. a.splice(j--, 1);
  24. }
  25. }
  26. return a;
  27. }
  28. /**
  29. * A helper function for extending an array based
  30. * on an "addArray" and "removeArray". Any element
  31. * found in removeArray is removed from the first array
  32. * and all the elements of addArray are added.
  33. * Any duplicate items are removed.
  34. * Creates a new array, so is non-destructive.
  35. *
  36. * @param {Array} array - the array to manipulate
  37. * @param {Array} addArray - the elements to add to the array
  38. * @param {Array} removeArray - the elements to remove from the array
  39. * @return {Array} The modified array
  40. */
  41. function expandArray(array, addArray, removeArray) {
  42. var copyArray = array.filter(function(item) {
  43. return removeArray.indexOf(item) === -1;
  44. });
  45. return arrayUnique(copyArray.concat(addArray));
  46. }
  47. /**
  48. * Deeply clones a node
  49. * @param {Node} node A node to clone
  50. * @return {Node} A clone of the given node and all its children
  51. */
  52. function cloneNode(node) {
  53. // If the node is a text node, then re-create it rather than clone it
  54. var clone = node.nodeType == 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
  55. // Recurse
  56. var child = node.firstChild;
  57. while(child) {
  58. clone.appendChild(cloneNode(child));
  59. child = child.nextSibling;
  60. }
  61. return clone;
  62. }
  63. /**
  64. * Indents the given string by 4 spaces. This correctly handles multi-line strings.
  65. *
  66. * @param {String} str - The string to be manipulated.
  67. * @returns {String} The string with four spaces added at the start of every new line.
  68. */
  69. function indent(str) {
  70. return str.replace(/^(?=.)/gm, ' ');
  71. }
  72. /**
  73. * Return a random integer between [`min`, `max`].
  74. *
  75. * @param {number} min - The lowest possible integer.
  76. * @param {number} max - The highest possible integer (inclusive).
  77. * @returns {number} A random integer.
  78. */
  79. function randomInteger(min,max) {
  80. return Math.floor(Math.random()*(max-min+1)+min);
  81. }
  82. /**
  83. * Encodes some text so that it can be safely written into an HTML box.
  84. * This includes replacing special HTML characters (&, <, >, etc.).
  85. *
  86. * @param {string} str - The text to be converted.
  87. * @return {string} The HTML-safe text.
  88. */
  89. function encodeHTML(str) {
  90. return str.replace(/&/g, '&amp;')
  91. .replace(/</g, '&lt;')
  92. .replace(/>/g, '&gt;')
  93. .replace(/"/g, '&quot;')
  94. .replace(/'/g, '&apos;');
  95. }
  96. /**
  97. * Shuffle the blocks in the workspace
  98. */
  99. if (typeof Blockly !== "undefined") {
  100. Blockly.WorkspaceSvg.prototype.shuffle = function() {
  101. var metrics = this.getMetrics();
  102. var width = metrics.viewWidth / 2,
  103. height = metrics.viewHeight;
  104. var blocks = this.getTopBlocks(false);
  105. var y = 5, x = 0,
  106. maximal_increase = height/blocks.length;
  107. for (var i = 0; i < blocks.length; i++){
  108. // Get a block
  109. var block = blocks[i];
  110. var properties = block.getRelativeToSurfaceXY();
  111. if (i == 0) {
  112. x = 5;
  113. } else {
  114. x = -properties.x+randomInteger(10, width);
  115. }
  116. block.moveBy(x,
  117. -properties.y+y);
  118. y = y + randomInteger(5, maximal_increase);
  119. }
  120. }
  121. }
  122. /**
  123. * Move elements from one array to another based on a conditional check.
  124. * https://stackoverflow.com/questions/31887967/javascript-move-objects-from-one-array-to-another-best-approach
  125. */
  126. function moveElements(source, target, moveCheck) {
  127. for (var i = 0; i < source.length; i++) {
  128. var element = source[i];
  129. if (moveCheck(element)) {
  130. source.splice(i, 1);
  131. target.push(element);
  132. i--;
  133. }
  134. }
  135. }
  136. /**
  137. * This function checks if the given object is one of the Sk.builtin objects
  138. * TODO: make this so we don't have to explicitly put out every option
  139. * one possible thing we could do is get a string version of the
  140. * of the constructor and look for the substring "return new Sk.builtin"
  141. * But I don't know how reliable that is. Rather, it's kind of hackish.
  142. * Should tehoretically belong in Sk.ffi
  143. * @param {object} obj - the object to be examined
  144. * @return {boolean} true if the object is one of the Sk.builtin types
  145. **/
  146. function isSkBuiltin(obj){
  147. return (obj instanceof Sk.builtin.dict) ||
  148. (obj instanceof Sk.builtin.list) ||
  149. (obj instanceof Sk.builtin.tuple) ||
  150. (obj instanceof Sk.builtin.bool) ||
  151. (obj instanceof Sk.builtin.int_) ||
  152. (obj instanceof Sk.builtin.float_) ||
  153. (obj instanceof Sk.builtin.str) ||
  154. (obj instanceof Sk.builtin.lng);
  155. //var cons_str = obj.constructor + "";
  156. //return cons_str.indexOf("return new Sk.builtin") !== -1;
  157. }
  158. function isAstNode(obj){
  159. return obj instanceof Object && "_astname" in obj;
  160. }
  161. /**
  162. * Should theoretically belong in Sk.ffi, but I put it here instead to not mess up the skulpt files
  163. * like the normal Sk.ffi.remapToPy, it doesn't work for functions or more complex objects, but it handles
  164. * cases where the types in obj are a mix of python SIMPLE objects and SIMPLE normal javascript objects
  165. * @param {object} obj - the object to be converted
  166. * @return {Sk.builtin.???} - returns the corresponding python object, dropping all functions and things it can't convert
  167. **/
  168. function mixedRemapToPy(obj){
  169. var k;
  170. var kvs;
  171. var i;
  172. var arr;
  173. //@TODO: should theoretically check if the object is a pyhon dict or array with js objects
  174. if (isSkBuiltin(obj)){
  175. //object is already python ready
  176. return obj;
  177. } else if (Object.prototype.toString.call(obj) === "[object Array]") {
  178. //object is actually a javascript array
  179. arr = [];
  180. for (i = 0; i < obj.length; ++i) {
  181. //for each object, convert it to a python object if it isn't one already
  182. var subval = obj[i];
  183. if(!isSkBuiltin(subval)){
  184. arr.push(mixedRemapToPy(subval));
  185. }else{
  186. arr.push(subval)
  187. }
  188. }
  189. return new Sk.builtin.list(arr);
  190. } else if (obj === null) {//null object
  191. return Sk.builtin.none.none$;
  192. } else if (typeof obj === "object") {
  193. if(!isSkBuiltin(obj)){
  194. //assuming it's a standard dictionary
  195. kvs = [];//Sk.builtin.dict uses an array of key-value,key-value...
  196. for (k in obj) {
  197. //convert the key if it needs to be converted
  198. kvs.push(mixedRemapToPy(k));
  199. //covert corresponding value if it needs to be converted
  200. kvs.push(mixedRemapToPy(obj[k]));
  201. }
  202. //create the new dictionary
  203. return new Sk.builtin.dict(kvs);
  204. }else{
  205. return obj;
  206. }
  207. } else if (typeof obj === "string") {
  208. return new Sk.builtin.str(obj);
  209. } else if (typeof obj === "number") {
  210. return Sk.builtin.assk$(obj);
  211. } else if (typeof obj === "boolean") {
  212. return new Sk.builtin.bool(obj);
  213. } else if(typeof obj === "function") {
  214. return new Sk.builtin.str(obj.name);
  215. }
  216. }