ardublockly.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  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 General javaScript for Arduino app with material design.
  6. */
  7. 'use strict';
  8. /** Create a namespace for the application. */
  9. var Ardublockly = Ardublockly || {};
  10. /** Initialize function for Ardublockly, to be called on page load. */
  11. Ardublockly.init = function () {
  12. // Lang init must run first for the rest of the page to pick the right msgs
  13. Ardublockly.initLanguage();
  14. // Inject Blockly into content_blocks and fetch additional blocks
  15. Ardublockly.injectBlockly(document.getElementsByClassName('blockpy-editor')[0], Ardublockly.TOOLBOX_XML, 'blockly/');
  16. // // /* Inject wifi Blockly into content_blocks and fetch additional blocks
  17. // // * set it invisiable
  18. // // * visiable only when switch
  19. // // */
  20. Ardublockly.injectBlockly(document.getElementsByClassName('blockpy-editor')[0], Ardublockly.WIFITOOLBOX_XML, 'blockly/');
  21. // Ardublockly.importExtraBlocks();
  22. Ardublockly.designJsInit();
  23. // Ardublockly.renderContent();
  24. // Ardublockly.bindDesignEventListeners();
  25. Ardublockly.bindActionFunctions();
  26. // Ardublockly.bindBlocklyEventListeners();
  27. // blockpy.components.editor.blockly.toolbox_.HtmlDiv = $('.blocklyToolboxDiv')[0];
  28. // $('.blocklyToolboxDiv')[1].style.display = 'none';
  29. // Check if not running locally
  30. // if (document.location.hostname != 'localhost') {
  31. // Ardublockly.openNotConnectedModal();
  32. // }
  33. };
  34. /**
  35. * Binds functions to each of the buttons, nav links, and related.
  36. * or binds functions to dom for drag files from local folder
  37. */
  38. Ardublockly.bindActionFunctions = function () {
  39. // Navigation buttons
  40. // Ardublockly.bindClick_('modal_import_btn', () => {
  41. // Ardublockly.loadUserXmlFile();
  42. // $("#Storage_import_modal").modal("close");
  43. // });
  44. $('#modal_import_btn').click(e => {
  45. $('#xmlFileImport > input').click();
  46. $("#Storage_import_modal").modal("close");
  47. });
  48. // Ardublockly.bindClick_('modal_exportFileBtn', () => {
  49. // Ardublockly.saveXmlFile();
  50. // $("#Storage_export_modal").modal("close");
  51. // });
  52. $('#modal_exportFileBtn').click(() => {
  53. downloadXml();
  54. $("#Storage_export_modal").modal("close");
  55. });
  56. // Ardublockly.bindClick_('button_delete', () => {
  57. // Ardublockly.discardAllBlocks();
  58. // // blockpy.components.editor.clearBlocksFromXml();
  59. // });
  60. $("#button_delete").click(() => {
  61. Ardublockly.alertMessage(
  62. Ardublockly.getLocalStr('discardAllCode'),
  63. '',
  64. true,
  65. function () {
  66. blockpy.components.editor.codeMirror.setValue('');
  67. Ardublockly.renderContent();
  68. });
  69. })
  70. Ardublockly.bindClick_('modal_exportSnapBtn', () => {
  71. let name = $("#sketch_name").val();
  72. Ardublockly.workspace_capture(name);
  73. $("#Storage_export_modal").modal("close");
  74. });
  75. Ardublockly.bindClick_('workspace_screenshot', () => {
  76. let name = $("#sketch_name").val();
  77. Ardublockly.workspace_capture(name);
  78. });
  79. // Side menu buttons, they also close the side menu
  80. // Ardublockly.bindClick_('button_toggle_toolbox', Ardublockly.toogleToolbox);
  81. Ardublockly.binddrag_('main_content', (e) => {
  82. Ardublockly.handleDragEnter(e);
  83. $("#main_shadow").css("display", "block");
  84. if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types[0] == "Files") {
  85. Ardublockly.handleDragEnter(e);
  86. $("#main_shadow").css("display", "block");
  87. }
  88. });
  89. // Ardublockly.binddrag_('main_shadow', (e) => {
  90. // Ardublockly.handleDragEnter(e);
  91. // $("#main_shadow").css("display", "block");
  92. // }, (e) => {
  93. // Ardublockly.handleDragLeave(e);
  94. // $("#main_shadow").css("display", "none");
  95. // });
  96. // Ardublockly.binddrop_('main_shadow', (e) => {
  97. // Ardublockly.handleFileSelect(e);
  98. // $("#main_shadow").css("display", "none");
  99. // });
  100. Ardublockly.binddrag_('shadow_content', (e) => {
  101. Ardublockly.handleDragEnter(e);
  102. $("#main_shadow").css("display", "block");
  103. }, (e) => {
  104. Ardublockly.handleDragLeave(e);
  105. $("#main_shadow").css("display", "none");
  106. });
  107. Ardublockly.binddrop_('shadow_content', (e) => {
  108. Ardublockly.handleFileSelect(e);
  109. $("#main_shadow").css("display", "none");
  110. });
  111. };
  112. /**
  113. * Loads an XML file from the server and replaces the current blocks into the
  114. * Blockly workspace.
  115. * @param {!string} xmlFile Server location of the XML file to load.
  116. */
  117. Ardublockly.loadServerXmlFile = function (xmlFile) {
  118. var loadXmlfileAccepted = function () {
  119. // loadXmlBlockFile loads the file asynchronously and needs a callback
  120. var loadXmlCb = function (sucess) {
  121. if (sucess) {
  122. Ardublockly.renderContent();
  123. } else {
  124. setTimeout(() => {
  125. Ardublockly.alertMessage(
  126. Ardublockly.getLocalStr('ErrorBlockTitle'),
  127. Ardublockly.getLocalStr('ErrorBlockBody'),
  128. false);
  129. }, 500);
  130. }
  131. };
  132. var connectionErrorCb = function () {
  133. // Ardublockly.openNotConnectedModal();
  134. };
  135. Ardublockly.loadXmlBlockFile(xmlFile, loadXmlCb, connectionErrorCb);
  136. };
  137. if (Ardublockly.isWorkspaceEmpty()) {
  138. loadXmlfileAccepted();
  139. } else {
  140. Ardublockly.alertMessage(
  141. Ardublockly.getLocalStr('loadNewBlocksTitle'),
  142. Ardublockly.getLocalStr('loadNewBlocksBody'),
  143. true, loadXmlfileAccepted);
  144. }
  145. };
  146. /**
  147. * Loads an XML file from the users file system and adds the blocks into the
  148. * Blockly workspace.
  149. */
  150. Ardublockly.loadUserXmlFile = function () {
  151. // Create File Reader event listener function
  152. var parseInputXMLfile = function (e) {
  153. Ardublockly.FileReaderLocal(e.target.files[0]);
  154. };
  155. // Create once invisible browse button with event listener, and click it
  156. var selectFile = document.getElementById('select_file');
  157. if (selectFile === null) {
  158. var selectFileDom = document.createElement('INPUT');
  159. selectFileDom.type = 'file';
  160. selectFileDom.id = 'select_file';
  161. var selectFileWrapperDom = document.createElement('DIV');
  162. selectFileWrapperDom.id = 'select_file_wrapper';
  163. selectFileWrapperDom.style.display = 'none';
  164. selectFileWrapperDom.appendChild(selectFileDom);
  165. document.body.appendChild(selectFileWrapperDom);
  166. selectFile = document.getElementById('select_file');
  167. selectFile.addEventListener('change', parseInputXMLfile, false);
  168. }
  169. selectFile.click();
  170. };
  171. /**
  172. * Creates an XML file containing the blocks from the Blockly workspace and
  173. * prompts the users to save it into their local file system.
  174. */
  175. Ardublockly.saveXmlFile = function () {
  176. Ardublockly.saveTextFileAs(
  177. document.getElementById('sketch_name').value + '.xml',
  178. Ardublockly.generateXml());
  179. };
  180. /**
  181. * Creates an Arduino Sketch file containing the Arduino code generated from
  182. * the Blockly workspace and prompts the users to save it into their local file
  183. * system.
  184. */
  185. Ardublockly.saveSketchFile = function () {
  186. Ardublockly.saveTextFileAs(
  187. document.getElementById('sketch_name').value + '.ino',
  188. Ardublockly.generateArduino());
  189. };
  190. /*debug mode on 2018-06-13 */
  191. /**
  192. * Creates an Arduino Sketch file containing the Arduino code generated from
  193. * the Blockly workspace and prompts the users to save it into cloud file
  194. * system.
  195. */
  196. Ardublockly.saveSketchFileCloud = function () {
  197. Ardublockly.saveTextFileCloudAs(
  198. document.getElementById('sketch_name').value + '.ino',
  199. Ardublockly.generateArduino());
  200. };
  201. /**
  202. * Creates an text file with the input content and files name, and prompts the
  203. * users to save it into their local file system.
  204. * @param {!string} fileName Name for the file to be saved.
  205. * @param {!string} content Text datd to be saved in to the file.
  206. */
  207. Ardublockly.saveTextFileAs = function (fileName, content) {
  208. var blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
  209. saveAs(blob, fileName);
  210. };
  211. /*debug mode on 2018-06-13 */
  212. /**
  213. * Creates an text file with the input content and files name, and prompts the
  214. * users to save it into cloud file system.
  215. * @param {!string} fileName Name for the file to be saved.
  216. * @param {!string} content Text datd to be saved in to the file.
  217. */
  218. Ardublockly.saveTextFileAs = function (fileName, content) {
  219. var blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
  220. saveAs(blob, fileName);
  221. };
  222. /** Populate the workspace blocks with the XML written in the XML text area. */
  223. Ardublockly.XmlTextareaToBlocks = function () {
  224. var success = Ardublockly.replaceBlocksfromXml(
  225. document.getElementById('content_xml').value);
  226. if (success) {
  227. Ardublockly.renderContent();
  228. } else {
  229. Ardublockly.alertMessage(
  230. Ardublockly.getLocalStr('invalidXmlTitle'),
  231. Ardublockly.getLocalStr('invalidXmlBody'),
  232. false);
  233. }
  234. };
  235. /**
  236. * Private variable to save the previous version of the Arduino Code.
  237. * @type {!String}
  238. * @private
  239. */
  240. Ardublockly.PREV_ARDUINO_CODE_ = 'void setup() {\n\n}\n\n\nvoid loop() {\n\n}';
  241. /**
  242. * Populate the Arduino Code and Blocks XML panels with content generated from
  243. * the blocks.
  244. */
  245. Ardublockly.renderContent = function () {
  246. // Only regenerate the code if a block is not being dragged
  247. if (Ardublockly.blocklyIsDragging()) return;
  248. // Render Arduino Code with latest change highlight and syntax highlighting
  249. var arduinoCode = Ardublockly.generateArduino();
  250. if (arduinoCode !== Ardublockly.PREV_ARDUINO_CODE_) {
  251. var diff = JsDiff.diffWords(Ardublockly.PREV_ARDUINO_CODE_, arduinoCode);
  252. var resultStringArray = [];
  253. for (var i = 0; i < diff.length; i++) {
  254. if (!diff[i].removed) {
  255. var escapedCode = diff[i].value.replace(/</g, '&lt;')
  256. .replace(/>/g, '&gt;');
  257. if (diff[i].added) {
  258. resultStringArray.push(
  259. '<span class="code_highlight_new">' + escapedCode + '</span>');
  260. } else {
  261. resultStringArray.push(escapedCode);
  262. }
  263. }
  264. }
  265. // document.getElementById('content_arduino').innerHTML =
  266. // prettyPrintOne(resultStringArray.join(''), 'cpp', false);
  267. Ardublockly.PREV_ARDUINO_CODE_ = arduinoCode;
  268. }
  269. // Generate plain XML into element
  270. // document.getElementById('content_xml').value = Ardublockly.generateXml();
  271. };
  272. /**
  273. * Private variable to indicate if the toolbox is meant to be shown.
  274. * @type {!boolean}
  275. * @private
  276. */
  277. Ardublockly.TOOLBAR_SHOWING_ = true;
  278. /**
  279. * Toggles the blockly toolbox and the Ardublockly toolbox button On and Off.
  280. * Uses namespace member variable TOOLBAR_SHOWING_ to toggle state.
  281. */
  282. Ardublockly.toogleToolbox = function () {
  283. if (Ardublockly.TOOLBAR_SHOWING_) {
  284. Ardublockly.blocklyCloseToolbox();
  285. Ardublockly.displayToolbox(false);
  286. } else {
  287. Ardublockly.displayToolbox(true);
  288. }
  289. Ardublockly.TOOLBAR_SHOWING_ = !Ardublockly.TOOLBAR_SHOWING_;
  290. };
  291. /** @return {boolean} Indicates if the toolbox is currently visible. */
  292. Ardublockly.isToolboxVisible = function () {
  293. return Ardublockly.TOOLBAR_SHOWING_;
  294. };
  295. /**
  296. * Lazy loads the additional block JS files from the ./block directory.
  297. * Initialises any additional Ardublockly extensions.
  298. * TODO: Loads the examples into the examples modal
  299. */
  300. Ardublockly.importExtraBlocks = function () {
  301. /**
  302. * Parses the JSON data to find the block and languages js files.
  303. * @param {jsonDataObj} jsonDataObj JSON in JavaScript object format, null
  304. * indicates an error occurred.
  305. * @return {undefined} Might exit early if response is null.
  306. */
  307. var jsonDataCb = function (jsonDataObj) {
  308. if (jsonDataObj === null) return Ardublockly.openNotConnectedModal();
  309. if (jsonDataObj.categories !== undefined) {
  310. var head = document.getElementsByTagName('head')[0];
  311. for (var catDir in jsonDataObj.categories) {
  312. var blocksJsLoad = document.createElement('script');
  313. blocksJsLoad.src = '../blocks/' + catDir + '/blocks.js';
  314. head.appendChild(blocksJsLoad);
  315. var blocksLangJsLoad = document.createElement('script');
  316. blocksLangJsLoad.src = '../blocks/' + catDir + '/msg/' + 'messages.js';
  317. //'lang/' + Ardublockly.LANG + '.js';
  318. head.appendChild(blocksLangJsLoad);
  319. var blocksGeneratorJsLoad = document.createElement('script');
  320. blocksGeneratorJsLoad.src = '../blocks/' + catDir +
  321. '/generator_arduino.js';
  322. head.appendChild(blocksGeneratorJsLoad);
  323. // Check if the blocks add additional Ardublockly functionality
  324. var extensions = jsonDataObj.categories[catDir].extensions;
  325. if (extensions) {
  326. for (var i = 0; i < extensions.length; i++) {
  327. var blockExtensionJsLoad = document.createElement('script');
  328. blockExtensionJsLoad.src = '../blocks/' + catDir + '/extensions.js';
  329. head.appendChild(blockExtensionJsLoad);
  330. // Add function to scheduler as lazy loading has to complete first
  331. setTimeout(function (category, extension) {
  332. var extensionNamespaces = extension.split('.');
  333. var extensionCall = window;
  334. var invalidFunc = false;
  335. for (var j = 0; j < extensionNamespaces.length; j++) {
  336. extensionCall = extensionCall[extensionNamespaces[j]];
  337. if (extensionCall === undefined) {
  338. invalidFunc = true;
  339. break;
  340. }
  341. }
  342. if (typeof extensionCall != 'function') {
  343. invalidFunc = true;
  344. }
  345. if (invalidFunc) {
  346. throw 'Blocks ' + category.categoryName + ' extension "' +
  347. extension + '" is not a valid function.';
  348. } else {
  349. extensionCall();
  350. }
  351. }, 800, jsonDataObj.categories[catDir], extensions[i]);
  352. }
  353. }
  354. }
  355. }
  356. };
  357. // Reads the JSON data containing all block categories from ./blocks directory
  358. // TODO: Now reading a local file, to be replaced by server generated JSON
  359. Ardublockly.getJsonData('../blocks/blocks_data.json', jsonDataCb);
  360. };
  361. /** Opens a modal with a list of categories to add or remove to the toolbox */
  362. Ardublockly.openExtraCategoriesSelect = function () {
  363. /**
  364. * Parses the JSON data from the server into a list of additional categories.
  365. * @param {jsonDataObj} jsonDataObj JSON in JavaScript object format, null
  366. * indicates an error occurred.
  367. * @return {undefined} Might exit early if response is null.
  368. */
  369. var jsonDataCb = function (jsonDataObj) {
  370. if (jsonDataObj === null) return Ardublockly.openNotConnectedModal();
  371. var htmlContent = document.createElement('div');
  372. if (jsonDataObj.categories !== undefined) {
  373. for (var catDir in jsonDataObj.categories) {
  374. // Function required to maintain each loop variable scope separated
  375. (function (cat) {
  376. var clickBind = function (tickValue) {
  377. if (tickValue) {
  378. var catDom = (new DOMParser()).parseFromString(
  379. cat.toolbox.join(''), 'text/xml').firstChild;
  380. Ardublockly.addToolboxCategory(cat.toolboxName, catDom);
  381. } else {
  382. Ardublockly.removeToolboxCategory(cat.toolboxName);
  383. }
  384. };
  385. htmlContent.appendChild(Ardublockly.createExtraBlocksCatHtml(
  386. cat.categoryName, cat.description, clickBind));
  387. })(jsonDataObj.categories[catDir]);
  388. }
  389. }
  390. Ardublockly.openAdditionalBlocksModal(htmlContent);
  391. };
  392. // Reads the JSON data containing all block categories from ./blocks directory
  393. // TODO: Now reading a local file, to be replaced by server generated JSON
  394. Ardublockly.getJsonData('../blocks/blocks_data.json', jsonDataCb);
  395. };
  396. /** Informs the user that the selected function is not yet implemented. */
  397. Ardublockly.functionNotImplemented = function () {
  398. Ardublockly.shortMessage('Function not yet implemented');
  399. };
  400. /**
  401. * Interface to display messages with a possible action.
  402. * @param {!string} title HTML to include in title.
  403. * @param {!element} body HTML to include in body.
  404. * @param {boolean=} confirm Indicates if the user is shown a single option (ok)
  405. * or an option to cancel, with an action applied to the "ok".
  406. * @param {string=|function=} callback If confirm option is selected this would
  407. * be the function called when clicked 'OK'.
  408. */
  409. Ardublockly.alertMessage = function (title, body, confirm, callback) {
  410. Ardublockly.materialAlert(title, body, confirm, function () {
  411. callback();
  412. $("#gen_alert_ok_link").unbind("click");
  413. });
  414. };
  415. Ardublockly.alertExampleMessage = function (title, body, confirm, callback) {
  416. Ardublockly.exampleAlert(title, body, confirm, function () {
  417. });
  418. };
  419. Ardublockly.customAlertMessage = function (title, body, customText, callback) {
  420. Ardublockly.customAlert(title, body, customText, function () {
  421. callback();
  422. $("#cus_alert_button").unbind("click");
  423. });
  424. };
  425. /**
  426. * Interface to displays a short message, which disappears after a time out.
  427. * @param {!string} message Text to be temporarily displayed.
  428. */
  429. Ardublockly.shortMessage = function (message) {
  430. Ardublockly.MaterialToast(message);
  431. };
  432. /**
  433. * Bind a function to a button's click event.
  434. * On touch enabled browsers, ontouchend is treated as equivalent to onclick.
  435. * @param {!Element|string} el Button element or ID thereof.
  436. * @param {!function} func Event handler to bind.
  437. */
  438. Ardublockly.bindClick_ = function (el, func) {
  439. if (typeof el == 'string') {
  440. el = document.getElementById(el);
  441. }
  442. // Need to ensure both, touch and click, events don't fire for the same thing
  443. var propagateOnce = function (e) {
  444. e.stopPropagation();
  445. e.preventDefault();
  446. func();
  447. };
  448. el.addEventListener('ontouchend', propagateOnce);
  449. el.addEventListener('click', propagateOnce);
  450. };
  451. /**
  452. * Bind a function to enable Toolbox scroll by touch on mobile media device.
  453. * @param {!Element|string} e element of touch node.
  454. */
  455. Ardublockly.bindTouchMove = function (e) {
  456. let startY = e.originalEvent.targetTouches[0].pageY;
  457. $(e.currentTarget).on('touchmove', moveCallback);
  458. $(e.currentTarget).on('touchend', touchEndCallback);
  459. function moveCallback(e) {
  460. let endY = e.originalEvent.targetTouches[0].pageY;
  461. let val = $(e.currentTarget).scrollTop() + (startY - endY) / 2;
  462. val = val < 0 ? 0 : val;
  463. $(e.currentTarget).scrollTop(val);
  464. startY = endY;
  465. }
  466. function touchEndCallback() {
  467. $(e.currentTarget).off('touchmove', moveCallback);
  468. $(e.currentTarget).off('touchend', touchEndCallback);
  469. }
  470. };
  471. /**
  472. * Bind a function to a drag event.
  473. * drag a file into the webpage
  474. * @param {!Element|string} el drag element or ID thereof.
  475. * @param {!function} func1 Drag over handler to bind.
  476. */
  477. Ardublockly.binddrag_ = function (el, func1, func2) {
  478. if (typeof el == 'string') {
  479. el = document.getElementById(el);
  480. }
  481. el.addEventListener('dragenter', func1);
  482. el.addEventListener('dragover', (e) => {
  483. e.stopPropagation();
  484. e.preventDefault();
  485. });
  486. el.addEventListener('dragleave', func2);
  487. }
  488. /**
  489. * Bind a function to a drop event.
  490. * drop a file into the webpage
  491. * @param {!Element|string} el drag element or ID thereof.
  492. * @param {!function} func Event handler to bind.
  493. */
  494. Ardublockly.binddrop_ = function (el, func) {
  495. if (typeof el == 'string') {
  496. el = document.getElementById(el);
  497. }
  498. el.addEventListener('drop', func);
  499. }
  500. /**
  501. * handle File Select by drag from local folder
  502. */
  503. Ardublockly.handleFileSelect = function (e, files) {
  504. e.stopPropagation();
  505. e.preventDefault();
  506. var files = files ? files : e.dataTransfer.files;
  507. //检测是否是拖拽文件到页面的操作
  508. if (files.length == 0) {
  509. return false;
  510. }
  511. // //检测文件是不是py
  512. if (files[0].name.indexOf('py') === -1) {
  513. Ardublockly.alertMessage(
  514. Ardublockly.getLocalStr('ErrorFileDragTitle'),
  515. Ardublockly.getLocalStr('ErrorPyDragBody'),
  516. false);
  517. return false;
  518. }
  519. Ardublockly.FileReaderPyLocal(files[0]);
  520. $('#sketch_name').val(files[0].name.match(/(.+).py$/)[1]);
  521. }
  522. /**
  523. * handle drag over function of local file
  524. */
  525. Ardublockly.handleDragEnter = function (e) {
  526. e.stopPropagation();
  527. e.preventDefault();
  528. e.dataTransfer.dropEffect = "copy";
  529. }
  530. /**
  531. * handle drag file leave the designated area
  532. */
  533. Ardublockly.handleDragLeave = function (e) {
  534. e.stopPropagation();
  535. e.preventDefault();
  536. }
  537. Ardublockly.FileReaderPyLocal = function (pyFile) {
  538. var filename = pyFile.name;
  539. var extensionPosition = filename.lastIndexOf('.');
  540. if (extensionPosition !== -1) {
  541. filename = filename.substr(0, extensionPosition);
  542. }
  543. var reader = new FileReader();
  544. reader.onload = function () {
  545. if (blockpy.components.editor.codeMirror.getValue()) {
  546. Ardublockly.alertExampleMessage(
  547. '',
  548. Ardublockly.getLocalStr('loadPyBody'),
  549. true, {});
  550. } else {
  551. Ardublockly.alertExampleMessage(
  552. '',
  553. Ardublockly.getLocalStr('loadPyBody'),
  554. true, {});
  555. }
  556. var result_xml = reader.result;
  557. try {
  558. // var xmlDom = Blockly.Xml.textToDom(result_xml);
  559. // if (result_xml.split("\n")[0].indexOf("ai") > -1) {
  560. // $("#mode")[0].selectedIndex = 1;
  561. // $("#mode")[0].onchange();
  562. // $('.selectMode_input')[0].value = Ardublockly.LOCALISED_TEXT.ai_module;
  563. // selectmode($("#mode")[0], true);
  564. // document.getElementById("list").getElementsByTagName("li")[1].onclick();
  565. // }
  566. // else if (result_xml.split("\n")[0].indexOf("lite") > -1) {
  567. // $("#mode")[0].selectedIndex = 2;
  568. // $("#mode")[0].onchange();
  569. // $('.selectMode_input')[0].value = Ardublockly.LOCALISED_TEXT.iot_lite_module;
  570. // selectmode($("#mode")[0], true);
  571. // document.getElementById("list").getElementsByTagName("li")[2].onclick();
  572. // }
  573. // else {
  574. // $("#mode")[0].selectedIndex = 0;
  575. // $("#mode")[0].onchange();
  576. // $('.selectMode_input')[0].value = Ardublockly.LOCALISED_TEXT.iot_module;
  577. // selectmode($("#mode")[0], true);
  578. // document.getElementById("list").getElementsByTagName("li")[0].onclick();
  579. // }
  580. // blockpy.components.editor.codeMirror.setValue('');
  581. blockpy.components.editor.codeMirror.setValue(result_xml);
  582. Ardublockly.renderContent();
  583. if (!blockpy.components.editor.codeMirror.getValue()) {
  584. Ardublockly.alertMessage(Ardublockly.getLocalStr('ErrorBlockTitle'), Ardublockly.getLocalStr('ErrorPyBody'), false);
  585. }
  586. } catch (e) {
  587. return false;
  588. }
  589. };
  590. reader.readAsText(pyFile);
  591. setTimeout(() => {
  592. $('#loading').css({ 'display': 'none' });
  593. $('#example_alert').modal('close');
  594. }, 500)
  595. }
  596. /**
  597. * FileReader : read file and render it into workspace
  598. * @param {Element} xmlFile an object abtain filename and xml.
  599. */
  600. Ardublockly.FileReaderLocal = function (xmlFile) {
  601. var filename = xmlFile.name;
  602. var extensionPosition = filename.lastIndexOf('.');
  603. if (extensionPosition !== -1) {
  604. filename = filename.substr(0, extensionPosition);
  605. }
  606. var reader = new FileReader();
  607. reader.onload = function () {
  608. if (Ardublockly.isWorkspaceEmpty()) {
  609. Ardublockly.alertExampleMessage(
  610. '',
  611. Ardublockly.getLocalStr('loadBlockBody'),
  612. true, {});
  613. } else {
  614. Ardublockly.alertExampleMessage(
  615. '',
  616. Ardublockly.getLocalStr('loadBlockBody'),
  617. true, {});
  618. }
  619. var result_xml = reader.result;
  620. $("#mode")[0].selectedIndex = 0;
  621. $("#mode")[0].onchange();
  622. $('.selectMode_input')[0].value = Ardublockly.LOCALISED_TEXT.ai_module;
  623. // document.getElementById("list").getElementsByTagName("li")[0].onclick();
  624. var success = Ardublockly.replaceBlocksfromXml(result_xml);
  625. if (!success) {
  626. Ardublockly.alertMessage(Ardublockly.getLocalStr('ErrorBlockTitle'), Ardublockly.getLocalStr('ErrorBlockBody'), false);
  627. }
  628. };
  629. reader.readAsText(xmlFile);
  630. setTimeout(() => {
  631. $('#loading').css({ 'display': 'none' });
  632. $('#example_alert').modal('close');
  633. }, 500)
  634. }
  635. /** Resizes the container for the Blockly workspace. */
  636. Ardublockly.resizeBlocklyWorkspace = function () {
  637. var contentBlocks = document.getElementsByClassName('blockpy-editor')[0];
  638. var wrapperPanelSize =
  639. Ardublockly.getBBox_(document.getElementById('blockpy-content'));
  640. contentBlocks.style.top = wrapperPanelSize.y + 'px';
  641. contentBlocks.style.left = wrapperPanelSize.x + 'px';
  642. // Height and width need to be set, read back, then set again to
  643. // compensate for scrollbars.
  644. contentBlocks.style.height = wrapperPanelSize.height + 'px';
  645. contentBlocks.style.height =
  646. (2 * wrapperPanelSize.height - contentBlocks.offsetHeight) + 'px';
  647. contentBlocks.style.width = wrapperPanelSize.width + 'px';
  648. contentBlocks.style.width =
  649. (2 * wrapperPanelSize.width - contentBlocks.offsetWidth) + 'px';
  650. };
  651. /**
  652. * Compute the absolute coordinates and dimensions of an HTML element.
  653. * @param {!Element} element Element to match.
  654. * @return {!Object} Contains height, width, x, and y properties.
  655. * @private
  656. */
  657. Ardublockly.getBBox_ = function (element) {
  658. var height = element.offsetHeight;
  659. var width = element.offsetWidth;
  660. var x = 0;
  661. var y = 0;
  662. do {
  663. x += element.offsetLeft;
  664. y += element.offsetTop;
  665. element = element.offsetParent;
  666. } while (element);
  667. return {
  668. height: height,
  669. width: width,
  670. x: x,
  671. y: y
  672. };
  673. };
  674. Blockly.asyncSvgResize = function (a) {
  675. Blockly.svgResizePending_ || (a || (a = Blockly.getMainWorkspace()), Blockly.svgResizePending_ = !0, setTimeout(function () { Blockly.svgResize(a) }, 0))
  676. };
  677. /**
  678. * Save blocks as XML file. Note that MSIE 11 does not support
  679. * @param {String} filename
  680. */
  681. function downloadXml() {
  682. var _xml = blockpy.components.editor.getBlocksFromXml();
  683. _xml.setAttribute("type", $("#mode")[0].selectedIndex == 1 ? "AI" : "IoT")
  684. var data = Blockly.Xml.domToPrettyText(_xml);
  685. let filename = $('#sketch_name').val();
  686. let reg = new RegExp(/[^a-z0-9]/)
  687. // Make safe
  688. filename = reg.test(filename) ? filename : filename.replace(/[^a-z0-9]/gi, '_').toLowerCase();
  689. filename = filename + '.xml'
  690. var blob = new Blob([data], { type: 'text/plain;charset=utf-8' });
  691. // if (window.navigator.msSaveOrOpenBlob) {
  692. // window.navigator.msSaveBlob(blob, filename);
  693. // } else {
  694. var elem = window.document.createElement('a');
  695. elem.href = window.URL.createObjectURL(blob);
  696. elem.download = filename;
  697. document.body.appendChild(elem);
  698. elem.click();
  699. document.body.removeChild(elem);
  700. // }
  701. }