/** * @license Licensed under the Apache License, Version 2.0 (the "License"): * http://www.apache.org/licenses/LICENSE-2.0 * * @fileoverview Ardublockly JavaScript for the Blockly resources and bindings. */ 'use strict'; /** Create a namespace for the application. */ var Ardublockly = Ardublockly || {}; /** * Blockly main workspace. * @type Blockly.WorkspaceSvg */ Ardublockly.workspace = null; /** * Blockly workspace toolbox XML. * @type Element */ Ardublockly.xmlTree = null; Ardublockly.injectBlockly = function (blocklyEl, toolboxXml, blocklyPath) { // Remove any trailing slashes in the blockly path if (blocklyPath.substr(-1) === '/') { blocklyPath = blocklyPath.slice(0, -1); } // Ardublockly.xmlTree = Blockly.Xml.textToDom(toolboxXml); // The Toolbox menu language is edited directly from the XML nodes. Ardublockly.updateToolboxLanguage(toolboxXml); Ardublockly.workspace = Blockly.inject(blocklyEl, { collapse: true, comments: true, css: true, disable: true, grid: false, maxBlocks: Infinity, media: blocklyPath + '/media/', rtl: false, scrollbars: true, sounds: true, toolbox: Ardublockly.xmlTree, trashcan: true, zoom: { controls: true, wheel: false, startScale: 1.0, maxScale: 2, minScale: 0.2, scaleSpeed: 1.2 } }); }; /** Binds the event listeners relevant to Blockly. */ Ardublockly.bindBlocklyEventListeners = function () { blockpy.components.editor.blockly.addChangeListener(Ardublockly.renderContent); // Ensure the Blockly workspace resizes accordingly window.addEventListener('resize', function () { Blockly.asyncSvgResize(blockpy.components.editor.blockly); }, false); }; /** @return {!string} Generated Arduino code from the Blockly workspace. */ Ardublockly.generateArduino = function () { return Blockly.Python.workspaceToCode(blockpy.components.editor.blockly); }; /** @return {!string} Generated XML code from the Blockly workspace. */ Ardublockly.generateXml = function () { var xmlDom = Blockly.Xml.workspaceToDom(blockpy.components.editor.blockly); //add tag for different mode var tag = CCB.mode ? "main" : "wifi" var xmlText = tag + "$" + Blockly.Xml.domToPrettyText(xmlDom); return xmlText; }; Ardublockly.loadXmlBlockFile = function (xmlFile, cbSuccess, cbError) { var request = Ardublockly.ajaxRequest(); var requestCb = function () { if (request.readyState == 4) { if (request.status == 200) { var result_xml = request.responseText; //if file has tag(main/wifi) if (request.responseText.indexOf('$') == 4) { result_xml = request.responseText.substr(5); } var success = Ardublockly.replaceBlocksfromXml(result_xml); cbSuccess(success); } else { cbError(); } } }; try { request.open('GET', xmlFile, true); request.onreadystatechange = requestCb; request.send(null); } catch (e) { cbError(); } }; Ardublockly.replaceBlocksfromXml = function (blocksXml) { var xmlDom = null; try { xmlDom = Blockly.Xml.textToDom(blocksXml); } catch (e) { return false; } blockpy.components.editor.blockly.clear(); var sucess = false; if (xmlDom) { sucess = Ardublockly.loadBlocksfromXmlDom(xmlDom); } return sucess; }; /** * Parses the XML from its argument input to generate and add blocks to the * Blockly workspace. * @param {!string} blocksXmlDom String of XML DOM code for the blocks. * @return {!boolean} Indicates if the XML into blocks parse was successful. */ Ardublockly.loadBlocksfromXmlDom = function (blocksXmlDom) { // try { // Blockly.Xml.domToWorkspace(blocksXmlDom, blockpy.components.editor.blockly); // } catch (e) { // return false; // } // return true; Blockly.Xml.domToWorkspace(blocksXmlDom, blockpy.components.editor.blockly); return !Blockly.Xml.getLoadingXmlErr(); }; /** * Save blocks into session storage. Note that MSIE 11 does not support * sessionStorage on file:// URLs. */ Ardublockly.saveSessionStorageBlocks = function () { if (window.sessionStorage) { var xml = Blockly.Xml.workspaceToDom(blockpy.components.editor.blockly); var text = Blockly.Xml.domToText(xml); window.sessionStorage.loadOnceBlocks = text; } }; /** Load blocks saved on session storage and deletes them from storage. */ Ardublockly.loadSessionStorageBlocks = function () { try { var loadOnce = window.sessionStorage.loadOnceBlocks; } catch (e) { // Firefox sometimes throws a SecurityError when accessing sessionStorage. // Restarting Firefox fixes this, so it looks like a bug. var loadOnce = null; } if (loadOnce) { delete window.sessionStorage.loadOnceBlocks; var xml = Blockly.Xml.textToDom(loadOnce); Blockly.Xml.domToWorkspace(xml, blockpy.components.editor.blockly); } }; /** * Save blocks into local storage. Note that MSIE 11 does not support * LocalStorage on file:// URLs. */ Ardublockly.saveLocalStorageBlocks = function () { if (window.sessionStorage) { var xml = Blockly.Xml.workspaceToDom(blockpy.components.editor.blockly); var text = Blockly.Xml.domToText(xml); window.localStorage.loadOnceBlocks = text; } }; /** Load blocks saved on loacl storage and deletes them from storage. */ Ardublockly.loadLocalStorageBlocks = function () { try { var loadOnce = window.localStorage.loadOnceBlocks; } catch (e) { // Firefox sometimes throws a SecurityError when accessing sessionStorage. // Restarting Firefox fixes this, so it looks like a bug. var loadOnce = null; } if (loadOnce) { delete window.localStorage.loadOnceBlocks; var xml = Blockly.Xml.textToDom(loadOnce); Blockly.Xml.domToWorkspace(xml, blockpy.components.editor.blockly); } }; /** Discard all blocks from the workspace. */ Ardublockly.discardAllBlocks = function () { var blockCount = blockpy.components.editor.blockly.getAllBlocks().length; var is_delete = false; if (blockCount == 1) { blockpy.components.editor.blockly.clear(); Ardublockly.renderContent(); } else if (blockCount > 1) { Ardublockly.alertMessage( Ardublockly.getLocalStr('discardBlocksTitle'), Ardublockly.getLocalStr('discardBlocksBody').replace('%1', blockCount), true, function () { blockpy.components.editor.blockly.clear(); Ardublockly.renderContent(); }); } }; /** @return {!boolean} Indicates if the Blockly workspace has blocks. */ Ardublockly.isWorkspaceEmpty = function () { return blockpy.components.editor.blockly.getAllBlocks().length ? false : true; }; /** * Changes the Arduino board profile if different from the currently set one. * @param {string} newBoard Name of the new profile to set. */ Ardublockly.changeBlocklyArduinoBoard = function (newBoard) { if (Blockly.Python.Boards.selected !== Blockly.Python.Boards[newBoard]) { Blockly.Python.Boards.changeBoard(blockpy.components.editor.blockly, newBoard); } }; /** Update the toolbox categories language. */ Ardublockly.updateToolboxLanguage = function (data) { var categories = ['catLogic', 'catLoops', 'catMath', 'catText', 'catVariables', 'catDefine', 'catFunctions', 'catInputOutput', 'catTime', 'catAudio', 'catMotors', 'catComms', 'catFASTLED', 'catTouch', 'catHCI', 'catENV', 'catMOTOR', 'catROBOT', 'catLists', 'catMOTION', 'catMainWifi', 'catCloudData', 'catWifiClient', 'catWifiWebServices', 'catWifiComms', 'catNetwork', 'catEasyMode', 'catBlynk', "ExtendedFunction" ]; Ardublockly.xmlTree = Blockly.Xml.textToDom(data) var categoryNodes = Ardublockly.xmlTree.getElementsByTagName('category'); for (var i = 0, cat; cat = categoryNodes[i]; i++) { var catId = cat.getAttribute('id'); var catText = Ardublockly.getLocalStr(catId); if (catText) { cat.setAttribute('name', catText); } } }; /** * Adds a category to the current toolbox. * @param {!string} categoryTitle Toolbox category title. * @param {!Element} categoryDom Toolbox category to add add the end of tree. */ Ardublockly.addToolboxCategory = function (categoryTitle, categoryDom) { categoryDom.id = 'cat' + categoryTitle.replace(/\s+/g, ''); categoryDom.setAttribute('name', categoryTitle); Ardublockly.xmlTree.appendChild(document.createElement('sep')); Ardublockly.xmlTree.appendChild(categoryDom); blockpy.components.editor.blockly.updateToolbox(Ardublockly.xmlTree); }; /** * Removes a category to the current toolbox. * @param {!String} categoryTitle Toolbox category name to remove from tree. */ Ardublockly.removeToolboxCategory = function (categoryTitle) { var categoryId = 'cat' + categoryTitle.replace(/\s+/g, ''); var categoryNodes = Ardublockly.xmlTree.getElementsByTagName('category'); for (var i = 0; i < categoryNodes.length; i++) { if (categoryNodes[i].getAttribute('id') === categoryId) { var previousNode = categoryNodes[i].previousElementSibling; Ardublockly.xmlTree.removeChild(categoryNodes[i]); if (previousNode && previousNode.nodeName == 'sep') { Ardublockly.xmlTree.removeChild(previousNode); } } } blockpy.components.editor.blockly.updateToolbox(Ardublockly.xmlTree); }; /** Closes the toolbox block container sub-menu. */ Ardublockly.blocklyCloseToolbox = function () { blockpy.components.editor.blockly.toolbox_.flyout_.hide(); }; /** @return {!integer} The width of the blockly workspace toolbox. */ Ardublockly.blocklyToolboxWidth = function () { return blockpy.components.editor.blockly.toolbox_.width; }; /** @return {!boolean} Indicates if a block is currently being dragged. */ Ardublockly.blocklyIsDragging = function () { return (Blockly.dragMode_ != 0) ? true : false; }; Ardublockly.blocklyCut = function () { if (Blockly.selected) { Blockly.copy_(Blockly.selected); Blockly.selected.dispose(true, true); } }; Ardublockly.blocklyCopy = function () { if (Blockly.selected) { Blockly.copy_(Blockly.selected); } }; Ardublockly.blocklyPaste = function () { if (Blockly.clipboardXml_) { Blockly.hideChaff(); Blockly.clipboardSource_.paste(Blockly.clipboardXml_); } }; Ardublockly.blocklyDelete = function () { if (Blockly.selected && Blockly.selected.isDeletable()) { Blockly.hideChaff(); Blockly.selected.dispose(true, true); } }; /** @return {XMLHttpRequest} An XML HTTP Request multi-browser compatible. */ Ardublockly.ajaxRequest = function () { var request; try { // Firefox, Chrome, IE7+, Opera, Safari request = new XMLHttpRequest(); } catch (e) { try { // IE6 and earlier request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) { throw 'Your browser does not support AJAX'; request = null; } } } return request; }; /** * get a workspace snapshot on blocks * handle with blocks imgs with onload function twice * in order to convert img into base64 format data for download * @param {String} name name of pic to store as */ Ardublockly.workspace_capture = async function (name) { let _header = window.location.protocol + "//" let _href = window.location.origin + "/"; let ws = blockpy.components.editor.blockly.svgBlockCanvas_.cloneNode(true); ws.removeAttribute("width"); ws.removeAttribute("height"); ws.removeAttribute("transform"); let styleElem = document.createElementNS("http://www.w3.org/2000/svg", "style"); styleElem.textContent = Blockly.Css.CONTENT.join(''); ws.insertBefore(styleElem, ws.firstChild); let bbox = blockpy.components.editor.blockly.svgBlockCanvas_.getBBox(); let canvas = document.createElement("canvas"); // canvas.width = Math.ceil(bbox.width + 120); // canvas.height = Math.ceil(bbox.height + 50); canvas.width = Math.ceil(Math.abs(bbox.x) + bbox.width + 120); canvas.height = Math.ceil(Math.abs(bbox.y) + bbox.height + 50); canvas.appendChild(ws); let ctx = canvas.getContext("2d"); let xml = new XMLSerializer().serializeToString(ws); let test = /xlink:href="[a-zA-z]+:\/\/[^\s]*"/g; let test1 = /xlink:href="[^\s]*"/g; let arr = xml.match(test1) if (arr != null) { let loadImage = function () { return new Promise((resolve) => { let length = arr.length; let count = 0; for (let i = 0; i < length; i++) { let dataURL; let canvas_inline = document.createElement("canvas"); let ctx1 = canvas_inline.getContext("2d"); let image_item = new Image(); image_item.setAttribute("src", _header + arr[i].replace("xlink:href=\"", _href).replace("\"", "")); image_item.onload = function () { //convert image to base64 canvas_inline.width = image_item.width; canvas_inline.height = image_item.height; ctx1.drawImage(image_item, 0, 0); dataURL = canvas_inline.toDataURL("image/png", 1); xml = xml.replace(arr[i], "xlink:href=\"" + dataURL + "\""); count++; } } let timer = setInterval(() => { if (count == length) { return resolve(true); clearInterval(timer); } }); }); } await loadImage(); } xml = '' + xml + ''; var img = new Image(); img.setAttribute("src", 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(xml)))); img.onload = function () { ctx.drawImage(img, 5, 5); var canvasdata = canvas.toDataURL("image/png", 1); var a = document.createElement("a"); a.download = name + ".png"; a.href = canvasdata; document.body.appendChild(a); a.click(); } }