ardublockly_blockly.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /**
  2. * @license Licensed under the Apache License, Version 2.0 (the "License"):
  3. * http://www.apache.org/licenses/LICENSE-2.0
  4. *
  5. * @fileoverview Ardublockly JavaScript for the Blockly resources and bindings.
  6. */
  7. 'use strict';
  8. /** Create a namespace for the application. */
  9. var Ardublockly = Ardublockly || {};
  10. /**
  11. * Blockly main workspace.
  12. * @type Blockly.WorkspaceSvg
  13. */
  14. Ardublockly.workspace = null;
  15. /**
  16. * Blockly workspace toolbox XML.
  17. * @type Element
  18. */
  19. Ardublockly.xmlTree = null;
  20. Ardublockly.injectBlockly = function (blocklyEl, toolboxXml, blocklyPath) {
  21. // Remove any trailing slashes in the blockly path
  22. if (blocklyPath.substr(-1) === '/') {
  23. blocklyPath = blocklyPath.slice(0, -1);
  24. }
  25. // Ardublockly.xmlTree = Blockly.Xml.textToDom(toolboxXml);
  26. // The Toolbox menu language is edited directly from the XML nodes.
  27. Ardublockly.updateToolboxLanguage(toolboxXml);
  28. Ardublockly.workspace = Blockly.inject(blocklyEl, {
  29. collapse: true,
  30. comments: true,
  31. css: true,
  32. disable: true,
  33. grid: false,
  34. maxBlocks: Infinity,
  35. media: blocklyPath + '/media/',
  36. rtl: false,
  37. scrollbars: true,
  38. sounds: true,
  39. toolbox: Ardublockly.xmlTree,
  40. trashcan: true,
  41. zoom: {
  42. controls: true,
  43. wheel: false,
  44. startScale: 1.0,
  45. maxScale: 2,
  46. minScale: 0.2,
  47. scaleSpeed: 1.2
  48. }
  49. });
  50. };
  51. /** Binds the event listeners relevant to Blockly. */
  52. Ardublockly.bindBlocklyEventListeners = function () {
  53. blockpy.components.editor.blockly.addChangeListener(Ardublockly.renderContent);
  54. // Ensure the Blockly workspace resizes accordingly
  55. window.addEventListener('resize',
  56. function () { Blockly.asyncSvgResize(blockpy.components.editor.blockly); }, false);
  57. };
  58. /** @return {!string} Generated Arduino code from the Blockly workspace. */
  59. Ardublockly.generateArduino = function () {
  60. return Blockly.Python.workspaceToCode(blockpy.components.editor.blockly);
  61. };
  62. /** @return {!string} Generated XML code from the Blockly workspace. */
  63. Ardublockly.generateXml = function () {
  64. var xmlDom = Blockly.Xml.workspaceToDom(blockpy.components.editor.blockly);
  65. //add tag for different mode
  66. var tag = CCB.mode ? "main" : "wifi"
  67. var xmlText = tag + "$" + Blockly.Xml.domToPrettyText(xmlDom);
  68. return xmlText;
  69. };
  70. Ardublockly.loadXmlBlockFile = function (xmlFile, cbSuccess, cbError) {
  71. var request = Ardublockly.ajaxRequest();
  72. var requestCb = function () {
  73. if (request.readyState == 4) {
  74. if (request.status == 200) {
  75. var result_xml = request.responseText;
  76. //if file has tag(main/wifi)
  77. if (request.responseText.indexOf('$') == 4) {
  78. result_xml = request.responseText.substr(5);
  79. }
  80. var success = Ardublockly.replaceBlocksfromXml(result_xml);
  81. cbSuccess(success);
  82. } else {
  83. cbError();
  84. }
  85. }
  86. };
  87. try {
  88. request.open('GET', xmlFile, true);
  89. request.onreadystatechange = requestCb;
  90. request.send(null);
  91. } catch (e) {
  92. cbError();
  93. }
  94. };
  95. Ardublockly.replaceBlocksfromXml = function (blocksXml) {
  96. var xmlDom = null;
  97. try {
  98. xmlDom = Blockly.Xml.textToDom(blocksXml);
  99. } catch (e) {
  100. return false;
  101. }
  102. blockpy.components.editor.blockly.clear();
  103. var sucess = false;
  104. if (xmlDom) {
  105. sucess = Ardublockly.loadBlocksfromXmlDom(xmlDom);
  106. }
  107. return sucess;
  108. };
  109. /**
  110. * Parses the XML from its argument input to generate and add blocks to the
  111. * Blockly workspace.
  112. * @param {!string} blocksXmlDom String of XML DOM code for the blocks.
  113. * @return {!boolean} Indicates if the XML into blocks parse was successful.
  114. */
  115. Ardublockly.loadBlocksfromXmlDom = function (blocksXmlDom) {
  116. // try {
  117. // Blockly.Xml.domToWorkspace(blocksXmlDom, blockpy.components.editor.blockly);
  118. // } catch (e) {
  119. // return false;
  120. // }
  121. // return true;
  122. Blockly.Xml.domToWorkspace(blocksXmlDom, blockpy.components.editor.blockly);
  123. return !Blockly.Xml.getLoadingXmlErr();
  124. };
  125. /**
  126. * Save blocks into session storage. Note that MSIE 11 does not support
  127. * sessionStorage on file:// URLs.
  128. */
  129. Ardublockly.saveSessionStorageBlocks = function () {
  130. if (window.sessionStorage) {
  131. var xml = Blockly.Xml.workspaceToDom(blockpy.components.editor.blockly);
  132. var text = Blockly.Xml.domToText(xml);
  133. window.sessionStorage.loadOnceBlocks = text;
  134. }
  135. };
  136. /** Load blocks saved on session storage and deletes them from storage. */
  137. Ardublockly.loadSessionStorageBlocks = function () {
  138. try {
  139. var loadOnce = window.sessionStorage.loadOnceBlocks;
  140. } catch (e) {
  141. // Firefox sometimes throws a SecurityError when accessing sessionStorage.
  142. // Restarting Firefox fixes this, so it looks like a bug.
  143. var loadOnce = null;
  144. }
  145. if (loadOnce) {
  146. delete window.sessionStorage.loadOnceBlocks;
  147. var xml = Blockly.Xml.textToDom(loadOnce);
  148. Blockly.Xml.domToWorkspace(xml, blockpy.components.editor.blockly);
  149. }
  150. };
  151. /**
  152. * Save blocks into local storage. Note that MSIE 11 does not support
  153. * LocalStorage on file:// URLs.
  154. */
  155. Ardublockly.saveLocalStorageBlocks = function () {
  156. if (window.sessionStorage) {
  157. var xml = Blockly.Xml.workspaceToDom(blockpy.components.editor.blockly);
  158. var text = Blockly.Xml.domToText(xml);
  159. window.localStorage.loadOnceBlocks = text;
  160. }
  161. };
  162. /** Load blocks saved on loacl storage and deletes them from storage. */
  163. Ardublockly.loadLocalStorageBlocks = function () {
  164. try {
  165. var loadOnce = window.localStorage.loadOnceBlocks;
  166. } catch (e) {
  167. // Firefox sometimes throws a SecurityError when accessing sessionStorage.
  168. // Restarting Firefox fixes this, so it looks like a bug.
  169. var loadOnce = null;
  170. }
  171. if (loadOnce) {
  172. delete window.localStorage.loadOnceBlocks;
  173. var xml = Blockly.Xml.textToDom(loadOnce);
  174. Blockly.Xml.domToWorkspace(xml, blockpy.components.editor.blockly);
  175. }
  176. };
  177. /** Discard all blocks from the workspace. */
  178. Ardublockly.discardAllBlocks = function () {
  179. var blockCount = blockpy.components.editor.blockly.getAllBlocks().length;
  180. var is_delete = false;
  181. if (blockCount == 1) {
  182. blockpy.components.editor.blockly.clear();
  183. Ardublockly.renderContent();
  184. } else if (blockCount > 1) {
  185. Ardublockly.alertMessage(
  186. Ardublockly.getLocalStr('discardBlocksTitle'),
  187. Ardublockly.getLocalStr('discardBlocksBody').replace('%1', blockCount),
  188. true,
  189. function () {
  190. blockpy.components.editor.blockly.clear();
  191. Ardublockly.renderContent();
  192. });
  193. }
  194. };
  195. /** @return {!boolean} Indicates if the Blockly workspace has blocks. */
  196. Ardublockly.isWorkspaceEmpty = function () {
  197. return blockpy.components.editor.blockly.getAllBlocks().length ? false : true;
  198. };
  199. /**
  200. * Changes the Arduino board profile if different from the currently set one.
  201. * @param {string} newBoard Name of the new profile to set.
  202. */
  203. Ardublockly.changeBlocklyArduinoBoard = function (newBoard) {
  204. if (Blockly.Python.Boards.selected !== Blockly.Python.Boards[newBoard]) {
  205. Blockly.Python.Boards.changeBoard(blockpy.components.editor.blockly, newBoard);
  206. }
  207. };
  208. /** Update the toolbox categories language. */
  209. Ardublockly.updateToolboxLanguage = function (data) {
  210. var categories = ['catLogic', 'catLoops', 'catMath', 'catText',
  211. 'catVariables', 'catDefine', 'catFunctions', 'catInputOutput',
  212. 'catTime', 'catAudio', 'catMotors', 'catComms', 'catFASTLED', 'catTouch', 'catHCI',
  213. 'catENV', 'catMOTOR', 'catROBOT', 'catLists', 'catMOTION', 'catMainWifi',
  214. 'catCloudData', 'catWifiClient', 'catWifiWebServices', 'catWifiComms', 'catNetwork', 'catEasyMode', 'catBlynk', "ExtendedFunction"
  215. ];
  216. Ardublockly.xmlTree = Blockly.Xml.textToDom(data)
  217. var categoryNodes = Ardublockly.xmlTree.getElementsByTagName('category');
  218. for (var i = 0, cat; cat = categoryNodes[i]; i++) {
  219. var catId = cat.getAttribute('id');
  220. var catText = Ardublockly.getLocalStr(catId);
  221. if (catText) {
  222. cat.setAttribute('name', catText);
  223. }
  224. }
  225. };
  226. /**
  227. * Adds a category to the current toolbox.
  228. * @param {!string} categoryTitle Toolbox category title.
  229. * @param {!Element} categoryDom Toolbox category to add add the end of tree.
  230. */
  231. Ardublockly.addToolboxCategory = function (categoryTitle, categoryDom) {
  232. categoryDom.id = 'cat' + categoryTitle.replace(/\s+/g, '');
  233. categoryDom.setAttribute('name', categoryTitle);
  234. Ardublockly.xmlTree.appendChild(document.createElement('sep'));
  235. Ardublockly.xmlTree.appendChild(categoryDom);
  236. blockpy.components.editor.blockly.updateToolbox(Ardublockly.xmlTree);
  237. };
  238. /**
  239. * Removes a category to the current toolbox.
  240. * @param {!String} categoryTitle Toolbox category name to remove from tree.
  241. */
  242. Ardublockly.removeToolboxCategory = function (categoryTitle) {
  243. var categoryId = 'cat' + categoryTitle.replace(/\s+/g, '');
  244. var categoryNodes = Ardublockly.xmlTree.getElementsByTagName('category');
  245. for (var i = 0; i < categoryNodes.length; i++) {
  246. if (categoryNodes[i].getAttribute('id') === categoryId) {
  247. var previousNode = categoryNodes[i].previousElementSibling;
  248. Ardublockly.xmlTree.removeChild(categoryNodes[i]);
  249. if (previousNode && previousNode.nodeName == 'sep') {
  250. Ardublockly.xmlTree.removeChild(previousNode);
  251. }
  252. }
  253. }
  254. blockpy.components.editor.blockly.updateToolbox(Ardublockly.xmlTree);
  255. };
  256. /** Closes the toolbox block container sub-menu. */
  257. Ardublockly.blocklyCloseToolbox = function () {
  258. blockpy.components.editor.blockly.toolbox_.flyout_.hide();
  259. };
  260. /** @return {!integer} The width of the blockly workspace toolbox. */
  261. Ardublockly.blocklyToolboxWidth = function () {
  262. return blockpy.components.editor.blockly.toolbox_.width;
  263. };
  264. /** @return {!boolean} Indicates if a block is currently being dragged. */
  265. Ardublockly.blocklyIsDragging = function () {
  266. return (Blockly.dragMode_ != 0) ? true : false;
  267. };
  268. Ardublockly.blocklyCut = function () {
  269. if (Blockly.selected) {
  270. Blockly.copy_(Blockly.selected);
  271. Blockly.selected.dispose(true, true);
  272. }
  273. };
  274. Ardublockly.blocklyCopy = function () {
  275. if (Blockly.selected) {
  276. Blockly.copy_(Blockly.selected);
  277. }
  278. };
  279. Ardublockly.blocklyPaste = function () {
  280. if (Blockly.clipboardXml_) {
  281. Blockly.hideChaff();
  282. Blockly.clipboardSource_.paste(Blockly.clipboardXml_);
  283. }
  284. };
  285. Ardublockly.blocklyDelete = function () {
  286. if (Blockly.selected && Blockly.selected.isDeletable()) {
  287. Blockly.hideChaff();
  288. Blockly.selected.dispose(true, true);
  289. }
  290. };
  291. /** @return {XMLHttpRequest} An XML HTTP Request multi-browser compatible. */
  292. Ardublockly.ajaxRequest = function () {
  293. var request;
  294. try {
  295. // Firefox, Chrome, IE7+, Opera, Safari
  296. request = new XMLHttpRequest();
  297. } catch (e) {
  298. try {
  299. // IE6 and earlier
  300. request = new ActiveXObject('Msxml2.XMLHTTP');
  301. } catch (e) {
  302. try {
  303. request = new ActiveXObject('Microsoft.XMLHTTP');
  304. } catch (e) {
  305. throw 'Your browser does not support AJAX';
  306. request = null;
  307. }
  308. }
  309. }
  310. return request;
  311. };
  312. /**
  313. * get a workspace snapshot on blocks
  314. * handle with blocks imgs with onload function twice
  315. * in order to convert img into base64 format data for download
  316. * @param {String} name name of pic to store as
  317. */
  318. Ardublockly.workspace_capture = async function (name) {
  319. let _header = window.location.protocol + "//"
  320. let _href = window.location.origin + "/";
  321. let ws = blockpy.components.editor.blockly.svgBlockCanvas_.cloneNode(true);
  322. ws.removeAttribute("width");
  323. ws.removeAttribute("height");
  324. ws.removeAttribute("transform");
  325. let styleElem = document.createElementNS("http://www.w3.org/2000/svg", "style");
  326. styleElem.textContent = Blockly.Css.CONTENT.join('');
  327. ws.insertBefore(styleElem, ws.firstChild);
  328. let bbox = blockpy.components.editor.blockly.svgBlockCanvas_.getBBox();
  329. let canvas = document.createElement("canvas");
  330. // canvas.width = Math.ceil(bbox.width + 120);
  331. // canvas.height = Math.ceil(bbox.height + 50);
  332. canvas.width = Math.ceil(Math.abs(bbox.x) + bbox.width + 120);
  333. canvas.height = Math.ceil(Math.abs(bbox.y) + bbox.height + 50);
  334. canvas.appendChild(ws);
  335. let ctx = canvas.getContext("2d");
  336. let xml = new XMLSerializer().serializeToString(ws);
  337. let test = /xlink:href="[a-zA-z]+:\/\/[^\s]*"/g;
  338. let test1 = /xlink:href="[^\s]*"/g;
  339. let arr = xml.match(test1)
  340. if (arr != null) {
  341. let loadImage = function () {
  342. return new Promise((resolve) => {
  343. let length = arr.length;
  344. let count = 0;
  345. for (let i = 0; i < length; i++) {
  346. let dataURL;
  347. let canvas_inline = document.createElement("canvas");
  348. let ctx1 = canvas_inline.getContext("2d");
  349. let image_item = new Image();
  350. image_item.setAttribute("src", _header + arr[i].replace("xlink:href=\"", _href).replace("\"", ""));
  351. image_item.onload = function () {
  352. //convert image to base64
  353. canvas_inline.width = image_item.width;
  354. canvas_inline.height = image_item.height;
  355. ctx1.drawImage(image_item, 0, 0);
  356. dataURL = canvas_inline.toDataURL("image/png", 1);
  357. xml = xml.replace(arr[i], "xlink:href=\"" + dataURL + "\"");
  358. count++;
  359. }
  360. }
  361. let timer = setInterval(() => {
  362. if (count == length) {
  363. return resolve(true);
  364. clearInterval(timer);
  365. }
  366. });
  367. });
  368. }
  369. await loadImage();
  370. }
  371. xml = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + canvas.width + '" height="' + canvas.height + '" viewBox="' + bbox.x + ' ' + bbox.y + ' ' + canvas.width + ' ' + canvas.height + '"><rect width="100%" height="100%" fill="white"></rect>' + xml + '</svg>';
  372. var img = new Image();
  373. img.setAttribute("src", 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(xml))));
  374. img.onload = function () {
  375. ctx.drawImage(img, 5, 5);
  376. var canvasdata = canvas.toDataURL("image/png", 1);
  377. var a = document.createElement("a");
  378. a.download = name + ".png";
  379. a.href = canvasdata;
  380. document.body.appendChild(a);
  381. a.click();
  382. }
  383. }