/** * Blockly Demos: Code * * Copyright 2012 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview JavaScript for Blockly's Code demo. * @author fraser@google.com (Neil Fraser) */ 'use strict'; var slice = Array.prototype.slice; var baseUrl = baseUrl || '.'; var storage = new JSONStorage('https://glowing-fire-4998.firebaseio.com/hyproto'); /** * Create a namespace for the application. */ var Code = {}; /** * Lookup for names of supported languages. Keys should be in ISO 639 format. */ Code.LANGUAGE_NAME = { 'en': 'English', 'zh-hant': '正體中文', 'zh-hans': '简体中文' }; /** * List of RTL languages. */ Code.LANGUAGE_RTL = ['ar', 'fa', 'he']; /** * Blockly's main workspace. * @type {Blockly.WorkspaceSvg} */ Code.workspace = null; /** * Extracts a parameter from the URL. * If the parameter is absent default_value is returned. * @param {string} name The name of the parameter. * @param {string} defaultValue Value to return if paramater not found. * @return {string} The parameter value or the default value if not found. */ Code.getStringParamFromUrl = function (name, defaultValue) { Code.queryString = Code.queryString || new QueryString(); return Code.queryString.get(name) || defaultValue; }; /** * Get the language of this user from the URL. * @return {string} User's language. */ Code.getLang = function () { var lang = Code.getStringParamFromUrl('lang', ''); if (Code.LANGUAGE_NAME[lang] === undefined) { // Default to English. lang = 'zh-hant'; } return lang; }; Code.getPage = function () { var page = Code.getStringParamFromUrl('page', 'index'); return page; }; Code.getTags = function () { var tags = Code.getStringParamFromUrl('tags', '').trim(); if (!tags.length) { tags = '*'; } tags = tags.split(/\s*,\s*/); return tags; }; Code.getDemoPage = function () { var area = Code.getStringParamFromUrl('demo', '').trim(); if (!area.length) { area = 'default'; } return area; }; Code.getPinDropdown = function () { var tags = Code.getTags(), mappings = [ [ ["2", "2"], ["3~", "3"], ["4", "4"], ["5~", "5"], ["6~", "6"], ["7", "7"], ["8", "8"], ["9~", "9"], ["10~", "10"], ["11~", "11"], ["12", "12"], ["13", "13"], ["14 ( A0 )", "14"], ["15 ( A1 )", "15"], ["16 ( A2 )", "16"], ["17 ( A3 )", "17"], ["18 ( A4 )", "18"], ["19 ( A5 )", "19"] ], [ ["12~", "12"], ["13~", "13"], ["14~", "14"], ["15~", "15"], ["16~", "16"], ["AD", "17"], ["0~", "0"], ["TX", "1"], ["2~", "2"], ["RX", "3"], ["4~", "4"], ["5~", "5"] ], [ ["0", "0"], ["2", "2"], ["3", "3"], ["4", "4"], ["5", "5"], ["6", "6"], ["7", "7"], ["8", "8"], ["9", "9"], ["10", "10"], ["11", "11"], ["12", "12"], ["13", "13"], ["14", "14"], ["15", "15"], ["16", "16"], ["17", "17"], ["18", "18"], ["19", "19"] ] ]; if (tags.length === 1 && tags[0] !== '*') { if (tags[0] === 'smart') { return mappings[1]; } return mappings[0]; } return mappings[2]; }; Code.loadDoc = function (href, callback) { var link = document.createElement('link'), tag = document.getElementsByTagName('script')[0]; link.rel = 'import'; link.href = href; if (typeof callback === 'function') { link.onload = function () { callback(link.import); }; tag.parentNode.insertBefore(link, tag); } else { return new Promise(function (resolve) { Code.loadDoc(href, resolve); }); } }; Code.loadJs = function (src, callback) { var js = document.createElement('script'), tag = document.getElementsByTagName('script')[0]; js.async = 1; js.src = src; if (typeof callback === 'function') { js.onload = function () { callback(js); }; tag.parentNode.insertBefore(js, tag); } else { return new Promise(function (resolve) { Code.loadJs(src, resolve); }); } }; /** * Is the current language (Code.LANG) an RTL language? * @return {boolean} True if RTL, false if LTR. */ Code.isRtl = function () { return Code.LANGUAGE_RTL.indexOf(Code.LANG) != -1; }; /** * Load blocks saved on App Engine Storage or in session/local storage. * @param {string} defaultXml Text representation of default blocks. */ Code.loadBlocks = function (defaultXml) { 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 ('BlocklyStorage' in window && BlocklyStorage.isLinkUrl()) { // An href with #key trigers an AJAX call to retrieve saved blocks. BlocklyStorage.retrieveXml(window.location.hash.substring(1)); } else if (loadOnce) { // Language switching stores the blocks during the reload. delete window.sessionStorage.loadOnceBlocks; var xml = Blockly.Xml.textToDom(loadOnce); Blockly.Xml.domToWorkspace(xml, Code.workspace); } else if (defaultXml) { // Load the editor with default starting blocks. var xml = Blockly.Xml.textToDom(defaultXml); Blockly.Xml.domToWorkspace(xml, Code.workspace); } else if ('BlocklyStorage' in window) { // Restore saved blocks in a separate thread so that subsequent // initialization is not affected from a failed load. window.setTimeout(BlocklyStorage.restoreBlocks, 0); } }; /** * Save the blocks and reload with a different language. */ Code.changeLanguage = function () { // Store the blocks for the duration of the reload. // This should be skipped for the index page, which has no blocks and does // not load Blockly. // MSIE 11 does not support sessionStorage on file:// URLs. if (typeof Blockly != 'undefined' && window.sessionStorage) { var xml = Blockly.Xml.workspaceToDom(Code.workspace); var text = Blockly.Xml.domToText(xml); window.sessionStorage.loadOnceBlocks = text; } var languageMenu = document.getElementById('languageMenu'); var newLang = encodeURIComponent( languageMenu.options[languageMenu.selectedIndex].value); if (newLang !== 'zh-hant') { Code.queryString.set('lang', newLang, true); } else { Code.queryString.unset('lang', true); } }; /** * Bind a function to a button's click event. * On touch enabled browsers, ontouchend is treated as equivalent to onclick. * @param {!Element|string} el Button element or ID thereof. * @param {!Function} func Event handler to bind. */ Code.bindClick = function (el, func) { if (typeof el == 'string') { el = document.getElementById(el); } if (el) { el.addEventListener('click', func, true); el.addEventListener('touchend', func, true); } }; /** * Load the Prettify CSS and JavaScript. */ Code.importPrettify = function () { // // var link = document.createElement('link'); link.setAttribute('rel', 'stylesheet'); link.setAttribute('href', baseUrl + '/prettify-tomorrow.css'); link.onload = function () { Blockly.fireUiEvent(window, 'resize'); }; document.head.appendChild(link); Code.loadJs(baseUrl + '/prettify.js'); }; /** * Compute the absolute coordinates and dimensions of an HTML element. * @param {!Element} element Element to match. * @return {!Object} Contains height, width, x, and y properties. * @private */ Code.getBBox_ = function (element) { var height = element.offsetHeight; var width = element.offsetWidth; var x = 0; var y = 0; do { x += element.offsetLeft; y += element.offsetTop; element = element.offsetParent; } while (element); return { height: height, width: width, x: x, y: y }; }; Code.checkDeviceOnline = function (device) { device = {}; device.inputArea = document.getElementById('input-device'); device.btn = document.getElementById('check-btn'); device.icon = document.getElementById('check-icon'); device.boardInst = null; document.addEventListener('visibilitychange', function (evt) { if (!document.hidden) { device.check(device.inputArea.value); } else { if (device.boardInst) { device.boardInst.disconnect(function () { delete device.boardInst; }); } } }); if (!localStorage.boardCheckOpen) { localStorage.boardCheckOpen = 0; } device.btn.onclick = function () { if (localStorage.boardCheckOpen == 1) { localStorage.boardCheckOpen = 0; device.inputArea.className = device.inputArea.className.replace('open', ''); } else { localStorage.boardCheckOpen = 1; device.inputArea.className = device.inputArea.className + 'open'; } }; device.check = function (v) { if (v.length > 3 && v.length <= 8 && v.indexOf('.') === -1) { if (device.boardInst) { device.boardInst.disconnect(function () { doCheck(); }); } else { doCheck(); } function doCheck() { var board = new webduino.WebArduino({ device: v, multi: true, clientId: 'lightning' }); board.on('ready', function () { device.icon.setAttribute('class', 'check board-online icon-power'); board.once(webduino.BoardEvent.DISCONNECT, function (e) { device.icon.setAttribute('class', 'check board-error icon-power'); if (!document.hidden) { device.check(v); } }); }); device.icon.setAttribute('class', 'check board-error icon-power'); device.boardInst = board; } } }; device.inputArea.oninput = function () { localStorage.boardState = this.value; device.icon.setAttribute('class', 'check icon-power'); device.check(this.value); }; if (localStorage.boardState) { device.inputArea.value = localStorage.boardState; device.inputArea.oninput(); } if (localStorage.boardCheckOpen == 0) { device.inputArea.className = device.inputArea.className.replace('open', ''); } else if (localStorage.boardCheckOpen == 1 && device.inputArea.value.length < 4) { localStorage.boardCheckOpen = 0; device.inputArea.className = device.inputArea.className.replace('open', ''); } else { device.inputArea.className = device.inputArea.className + 'open'; } }; Code.copyCode = function (copy) { copy = {}; copy.jsTab = document.getElementById('tab_javascript'); copy.copyBtn = document.getElementById('copyCode'); copy.clipboard = new Clipboard('#copyCode'); copy.clipboard.on('success', function (e) { copy.copyBtn.setAttribute('data-tooltip', 'Copied!!!'); }); copy.copyBtn.addEventListener('mouseleave', function () { copy.copyBtn.setAttribute('data-tooltip', 'Copy to clipboard'); }); copy.jsTab.addEventListener('click', function () { copy.copyBtn.style.display = 'table-cell'; }); document.getElementById('tab_blocks').addEventListener('click', function () { copy.copyBtn.style.display = 'none'; }); }; Code.bindHotkey = function (document) { Blockly.bindEvent_(document, 'keydown', null, function (e) { switch (Code.getHotKey(e)) { case Code.HOTKEY.EXEC: Code.runJS(); break; default: break; } }); }; Code.getHotKey = function (e) { // Ctrl/Cmd + E if ((e.ctrlKey || e.metaKey) && e.keyCode === 69) { return Code.HOTKEY.EXEC; } return Code.HOTKEY.UNKNOWN; }; Code.loadDemoArea = function () { var area = document.getElementById('demo-area'); var btn = document.getElementById('demoButton'); var select = document.getElementById('demo-select'); var close = document.querySelector('.close-btn'); var contentHeight = document.getElementById('content_blocks').offsetHeight; var resizeBar = document.getElementById('demo-resize-bar'); var demoPage = Code.getDemoPage(); area.style.height = (contentHeight - 110) + 'px'; area.className = area.className.replace("show", ""); if (localStorage.demoAreaWidth) { area.style.width = localStorage.demoAreaWidth; } if (demoPage !== 'default') { area.className = area.className + "show"; btn.className = "notext toolMenu opened"; Code.queryString.set('demo', demoPage); } select.value = demoPage === 'default' ? 'demo-area-01' : demoPage; Code.reloadSandbox(); resizeBar.onmousedown = function (e) { area.style.opacity = '0.4'; var frame = document.getElementById('demo-frame'); frame.style.pointerEvents = 'none'; var ox = e.pageX; var dw = area.offsetWidth; area.className += " resize"; document.onmousemove = function (event) { var rx = event.pageX; area.style.width = dw - rx + ox - 20 + 'px'; localStorage.demoAreaWidth = area.style.width; }; }; document.onmouseup = function () { area.style.opacity = '1'; var frame = document.getElementById('demo-frame'); frame.style.pointerEvents = 'auto'; area.className = area.className.replace("resize", ""); document.onmousemove = null; }; btn.onclick = function () { var demoPage = Code.getDemoPage(); if (demoPage !== 'default') { area.className = area.className.replace("show", ""); btn.className = "notext toolMenu"; localStorage.demoAreaSelect = demoPage; Code.queryString.unset('demo'); } else { area.className += " show"; btn.className = "notext toolMenu opened"; Code.queryString.set('demo', localStorage.demoAreaSelect || 'demo-area-01'); select.value = localStorage.demoAreaSelect || 'demo-area-01'; delete localStorage.demoAreaSelect; } Code.workspace.updateToolbox(Code.getToolBox()); Code.reloadSandbox(); }; close.onclick = function () { area.className = area.className.replace("show", ""); btn.className = "notext toolMenu"; localStorage.demoAreaSelect = Code.getDemoPage(); Code.queryString.unset('demo'); Code.workspace.updateToolbox(Code.getToolBox()); }; select.onchange = function () { ga('send', 'event', 'Webduino-blockly', 'demo select', this.value); Code.queryString.set('demo', this.value); Code.workspace.updateToolbox(Code.getToolBox()); Code.reloadSandbox(); }; }; Code.loadSample = function () { var sampleBtn = document.getElementById('sampleButton'), sampleMenu = document.getElementById('smaple-menu'), sampleMenuOpen = false, sampleBtnOver = false, sampleTitle = ' b[0]) return 1; if (a[0] < b[0]) return -1; return 0; }; languages.sort(comp); // Populate the language selection menu. var languageMenu = document.getElementById('languageMenu'); languageMenu.options.length = 0; for (var i = 0; i < languages.length; i++) { var tuple = languages[i]; var lang = tuple[tuple.length - 1]; var option = new Option(tuple[0], lang); if (lang == Code.LANG) { option.selected = true; } languageMenu.options.add(option); } languageMenu.addEventListener('change', Code.changeLanguage, true); // Inject language strings. document.title = MSG['title']; }; /** * Execute the user's code. * Just a quick and dirty eval. Catch infinite loops. */ Code.runJS = function () { var now = Date.now(); if (!Code.sandboxLoaded) { return; } if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/iPod/i)) { if (now - Code.lastRun < 1000) { return; } Code.lastRun = now; } Code.toggleRunning(); Code.reloadSandbox(); }; Code.toggleRunning = function () { var runBtn = document.getElementById('runButton'); var select = document.getElementById('demo-select'); var demoBtn = document.getElementById('demoButton'); var demoStopBtn = document.querySelector('.close-btn'); if (!Code.running) { runBtn.style.backgroundColor = "#0a5"; runBtn.style.borderColor = "#0a5"; document.querySelector('#runButton i').setAttribute('class', 'icon-stop2'); document.querySelector('#runButton div').innerHTML = MSG['stopRunTooltip']; select.disabled = true; demoBtn.disabled = true; demoBtn.className = "notext toolMenu running"; demoBtn.style.pointerEvents = 'none'; demoStopBtn.style.pointerEvents = 'none'; demoStopBtn.style.opacity = 0.2; } else { runBtn.style.backgroundColor = "#dd4b39"; runBtn.style.borderColor = "#dd4b39"; document.querySelector('#runButton i').setAttribute('class', 'icon-play3'); document.querySelector('#runButton div').innerHTML = MSG['runTooltip']; select.disabled = false; demoBtn.disabled = false; if (localStorage.demoArea == 'open') { demoBtn.className = "notext toolMenu opened"; } else { demoBtn.className = "notext toolMenu"; } demoBtn.style.pointerEvents = 'auto'; demoStopBtn.style.pointerEvents = 'auto'; demoStopBtn.style.opacity = 0.5; } Code.running = !Code.running; }; Code.reloadSandbox = function () { var container = document.querySelector('.demo-area-content'); var ctx = Code.getContext(); Code.sandboxLoaded = false; launcher.loadTemplate('./templates/' + ctx.tpl + '.html', function (data) { data.body = launcher.translate(data.body, MSG); if (Code.running) { if (ctx.jsPreprocessor === 'babel') { data.js = Code.transform(ctx.data.js); } else { data.js = ctx.data.js; } } var frame = container.querySelector('#demo-frame'); if (frame) { frame.contentWindow.addEventListener('unload', function () { createIframe(); }, false); var event = new UIEvent('beforeunload'); frame.contentWindow.dispatchEvent(event); setTimeout(function () { Code.unhookEvents(window, frame); container.removeChild(frame); frame = null; }, 50); } else { createIframe(); } function createIframe() { frame = document.createElement('iframe'); frame.id = 'demo-frame'; frame.style.display = 'block'; container.appendChild(frame); Code.tabClick('blocks'); frame.addEventListener('load', function () { Code.sandboxLoaded = true; }); launcher.sandbox(frame, data); Code.bindHotkey(frame.contentWindow.document); if (Code.running) { Code.hookEvents(window, frame); } } }); }; Code.hookEvents = function (window, frame) { window.keyDispatcher = function (e) { if (Code.getHotKey(e) === Code.HOTKEY.EXEC) { return; } var doc = frame.contentWindow.document, event = doc.createEvent('Event'); event.initEvent(e.type, true, true); event.key = e.key; event.keyCode = e.keyCode; doc.body && doc.body.dispatchEvent(event); }; window.msgDispatcher = function (e) { var msg = e.data; if (msg.jsonrpc && msg.result) { frame.contentWindow.postMessage(msg, window.location.origin); } }; frame.msgDispatcher = function (e) { var msg = e.data; if (msg.jsonrpc && msg.method) { window.postMessage(msg, window.location.origin); } }; window.addEventListener('keydown', window.keyDispatcher, false); window.addEventListener('keyup', window.keyDispatcher, false); window.addEventListener('message', window.msgDispatcher, false); frame.contentWindow.addEventListener('message', frame.msgDispatcher, false); }; Code.unhookEvents = function (window, frame) { if (frame.msgDispatcher) { frame.contentWindow.removeEventListener('message', frame.msgDispatcher, false); delete frame.msgDispatcher; } if (window.msgDispatcher) { window.removeEventListener('message', window.msgDispatcher, false); delete window.msgDispatcher; } if (window.keyDispatcher) { window.removeEventListener('keyup', window.keyDispatcher, false); window.removeEventListener('keydown', window.keyDispatcher, false); delete window.keyDispatcher; } }; Code.getContext = function () { var code = Blockly.JavaScript.workspaceToCode(Code.workspace), babelize = code.indexOf('async function') !== -1, page = Code.PAGE, lang = Code.LANG; return { page: page, tpl: page === 'index' ? Code.getDemoPage() : page, lang: lang, modes: 'html,css,js,output', data: { js: code }, jsPreprocessor: babelize ? 'babel' : '' }; }; Code.transform = function (code) { try { return Babel.transform(code, { presets: ['es2015', 'stage-3'], plugins: ['transform-strict-mode'] }).code; } catch (e) { alert(e); } }; /** * Discard all blocks from the workspace. */ Code.discard = function () { var count = Code.workspace.getAllBlocks().length; if (count < 2 || window.confirm(Blockly.Msg.DELETE_ALL_BLOCKS.replace('%1', count))) { Code.workspace.clear(); if (window.location.hash) { window.location.hash = ''; } } }; Code.debug = function () { var space = ''; for (var i = 0; i < Blockly.JavaScript.depth; i++) { space += ' '; } console.log.apply(console, [space].concat(Array.prototype.slice.apply(arguments))); }; Code.exportImage = function () { Code.workspace.zoomReset(document.createEvent('MouseEvents')); saveSvgAsPng(Code.workspace.getCanvas(), 'webduino-blocks.png'); }; Code.getBlockDemo = function (type) { var toolbox = Code.rawToolbox; var block = toolbox.querySelector('block[type=' + type + ']'); var attr; if (block) { while (block = block.parentNode) { if (block.nodeType === 1 && block.tagName.toLowerCase() === 'category' && (attr = block.getAttribute('demo'))) { return attr; } } } return null; }; Code.getBlockTypes = function () { var xml = Blockly.Xml.workspaceToDom(Code.workspace); return slice.call(xml.querySelectorAll('block')).map(function (block) { return block.getAttribute('type'); }); }; Blockly.JavaScript['procedures_defnoreturn'] = function (block) { // Define a procedure with a return value. var funcName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var branch = Blockly.JavaScript.statementToCode(block, 'STACK'); if (Blockly.JavaScript.STATEMENT_PREFIX) { branch = Blockly.JavaScript.prefixLines( Blockly.JavaScript.STATEMENT_PREFIX.replace(/%1/g, '\'' + block.id + '\''), Blockly.JavaScript.INDENT) + branch; } if (Blockly.JavaScript.INFINITE_LOOP_TRAP) { branch = Blockly.JavaScript.INFINITE_LOOP_TRAP.replace(/%1/g, '\'' + block.id + '\'') + branch; } var returnValue = Blockly.JavaScript.valueToCode(block, 'RETURN', Blockly.JavaScript.ORDER_NONE) || ''; if (returnValue) { returnValue = ' return ' + returnValue + ';\n'; } var args = []; for (var x = 0; x < block.arguments_.length; x++) { args[x] = Blockly.JavaScript.variableDB_.getName(block.arguments_[x], Blockly.Variables.NAME_TYPE); } var code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + branch + returnValue + '}'; code = Blockly.JavaScript.scrub_(block, code); if (code.indexOf('await ') !== -1) { code = 'async ' + code; } Blockly.JavaScript.definitions_[funcName] = code; return null; }; Blockly.JavaScript['_procedures_callreturn'] = Blockly.JavaScript['procedures_callreturn']; Blockly.JavaScript['procedures_callreturn'] = function (block) { // Call a procedure with a return value. var funcName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var codes = Blockly.JavaScript['_procedures_callreturn'].call(Blockly.JavaScript, block); var defs = Blockly.JavaScript.definitions_; if (defs[funcName] && defs[funcName].indexOf('async ') === 0) { return ['await ' + codes[0], codes[1]]; } return codes; }; Blockly.JavaScript['_procedures_callnoreturn'] = Blockly.JavaScript['procedures_callnoreturn']; Blockly.JavaScript['procedures_callnoreturn'] = function (block) { // Call a procedure with no return value. var funcName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var code = Blockly.JavaScript['_procedures_callnoreturn'].call(Blockly.JavaScript, block); var defs = Blockly.JavaScript.definitions_; if (defs[funcName] && defs[funcName].indexOf('async ') === 0) { return 'await ' + code; } return code; }; Blockly.JavaScript['_workspaceToCode'] = Blockly.JavaScript['workspaceToCode']; Blockly.JavaScript['workspaceToCode'] = function (workspace) { var code = Blockly.JavaScript['_workspaceToCode'].call(Blockly.JavaScript, workspace); if (code.indexOf('await ') === -1) { code = code.replace(new RegExp('async function', 'g'), 'function'); } else { code = '(async function () {\n\n' + code + '\n}());'; } return code; }; Blockly.Xml._domToWorkspace = Blockly.Xml.domToWorkspace; Blockly.Xml.domToWorkspace = function () { Blockly.Xml._domToWorkspace.apply(this, arguments); var types = Code.getBlockTypes(); for (var i = 0; i < types.length; i++) { var demo = Code.getBlockDemo(types[i]); if (demo) { if (demo !== Code.queryString.get('demo')) { Code.queryString.set('demo', demo); Code.loadDemoArea(); Code.workspace.updateToolbox(Code.getToolBox()); } return; } } }; Blockly.JavaScript.scrub_ = function (block, code) { var commentCode = ''; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { // Collect comment for this block. // var comment = block.getCommentText(); // if (comment) { // commentCode += Blockly.JavaScript.prefixLines(comment, '// ') + '\n'; // } // Collect comments for all value arguments. // Don't collect comments for nested statements. for (var x = 0; x < block.inputList.length; x++) { if (block.inputList[x].type == Blockly.INPUT_VALUE) { var childBlock = block.inputList[x].connection.targetBlock(); if (childBlock) { // var comment = Blockly.JavaScript.allNestedComments(childBlock); // if (comment) { // commentCode += Blockly.JavaScript.prefixLines(comment, '// '); // } } } } } var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); var nextCode = Blockly.JavaScript.blockToCode(nextBlock); return commentCode + code + nextCode; }; // ZoomControl icons position Blockly.ZoomControls.prototype.createDom = function () { var workspace = this.workspace_; this.svgGroup_ = Blockly.createSvgElement('g', { 'class': 'blocklyZoom' }, null); var rnd = String(Math.random()).substring(2); var clip = Blockly.createSvgElement('clipPath', { 'id': 'blocklyZoomresetClipPath' + rnd }, this.svgGroup_); Blockly.createSvgElement('rect', { 'width': 32, 'height': 32, 'x': 40, 'y': 80 }, clip); var zoomresetSvg = Blockly.createSvgElement('image', { 'width': Blockly.SPRITE.width, 'height': Blockly.SPRITE.height, 'x': 40, 'y': -12, 'clip-path': 'url(#blocklyZoomresetClipPath' + rnd + ')' }, this.svgGroup_); zoomresetSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'media/sprites.png'); var clip = Blockly.createSvgElement('clipPath', { 'id': 'blocklyZoominClipPath' + rnd }, this.svgGroup_); Blockly.createSvgElement('rect', { 'width': 32, 'height': 32, 'x': 0, 'y': 80 }, clip); var zoominSvg = Blockly.createSvgElement('image', { 'width': Blockly.SPRITE.width, 'height': Blockly.SPRITE.height, 'x': -32, 'y': -12, 'clip-path': 'url(#blocklyZoominClipPath' + rnd + ')' }, this.svgGroup_); zoominSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'media/sprites.png'); var clip = Blockly.createSvgElement('clipPath', { 'id': 'blocklyZoomoutClipPath' + rnd }, this.svgGroup_); Blockly.createSvgElement('rect', { 'width': 32, 'height': 32, 'x': 80, 'y': 80 }, clip); var zoomoutSvg = Blockly.createSvgElement('image', { 'width': Blockly.SPRITE.width, 'height': Blockly.SPRITE.height, 'x': 16, 'y': -12, 'clip-path': 'url(#blocklyZoomoutClipPath' + rnd + ')' }, this.svgGroup_); zoomoutSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'media/sprites.png'); Blockly.bindEvent_(zoomresetSvg, 'mousedown', workspace, workspace.zoomReset); Blockly.bindEvent_(zoominSvg, 'mousedown', null, function () { workspace.zoomCenter(1); }); Blockly.bindEvent_(zoomoutSvg, 'mousedown', null, function () { workspace.zoomCenter(-1); }); return this.svgGroup_; }; // ZoomControl position and size Blockly.ZoomControls.prototype.position = function () { var metrics = this.workspace_.getMetrics(); if (!metrics) { return; } if (this.workspace_.RTL) { this.left_ = this.MARGIN_SIDE_; } else { this.left_ = metrics.absoluteLeft - this.WIDTH_ - this.MARGIN_SIDE_ + 100; } this.top_ = metrics.viewHeight + metrics.absoluteTop - this.HEIGHT_ - this.MARGIN_BOTTOM_ + 5; this.svgGroup_.setAttribute('transform', 'translate(' + this.left_ + ',' + this.top_ + ') scale(.95)');; }; // trashcan position and size Blockly.Trashcan.prototype.position = function () { var metrics = this.workspace_.getMetrics(); if (!metrics) { return; } if (this.workspace_.RTL) { this.left_ = this.MARGIN_SIDE_; } else { this.left_ = metrics.absoluteLeft - this.WIDTH_ - this.MARGIN_SIDE_ + 82; } this.top_ = metrics.viewHeight + metrics.absoluteTop - (this.BODY_HEIGHT_ + this.LID_HEIGHT_) - this.MARGIN_BOTTOM_ + 26; this.svgGroup_.setAttribute('transform', 'translate(' + this.left_ + ',' + this.top_ + ') scale(.58)'); }; Blockly.WorkspaceSvg.prototype.showContextMenu_ = function (e) { if (this.options.readOnly || this.isFlyout) { return; } var menuOptions = []; var topBlocks = this.getTopBlocks(true); var eventGroup = Blockly.genUid(); // Options to undo/redo previous action. var undoOption = {}; undoOption.text = Blockly.Msg.UNDO; undoOption.enabled = this.undoStack_.length > 0; undoOption.callback = this.undo.bind(this, false); menuOptions.push(undoOption); var redoOption = {}; redoOption.text = Blockly.Msg.REDO; redoOption.enabled = this.redoStack_.length > 0; redoOption.callback = this.undo.bind(this, true); menuOptions.push(redoOption); // Option to clean up blocks. if (this.scrollbar) { var cleanOption = {}; cleanOption.text = Blockly.Msg.CLEAN_UP; cleanOption.enabled = topBlocks.length > 1; cleanOption.callback = this.cleanUp_.bind(this); menuOptions.push(cleanOption); } // Add download img option var imgOption = {}; imgOption.text = MSG.exportImage; imgOption.enabled = topBlocks.length > 0; imgOption.callback = Code.exportImage.bind(Code); menuOptions.push(imgOption); // Add a little animation to collapsing and expanding. var DELAY = 10; if (this.options.collapse) { var hasCollapsedBlocks = false; var hasExpandedBlocks = false; for (var i = 0; i < topBlocks.length; i++) { var block = topBlocks[i]; while (block) { if (block.isCollapsed()) { hasCollapsedBlocks = true; } else { hasExpandedBlocks = true; } block = block.getNextBlock(); } } /* * Option to collapse or expand top blocks * @param {boolean} shouldCollapse Whether a block should collapse. * @private */ var toggleOption = function (shouldCollapse) { var ms = 0; for (var i = 0; i < topBlocks.length; i++) { var block = topBlocks[i]; while (block) { setTimeout(block.setCollapsed.bind(block, shouldCollapse), ms); block = block.getNextBlock(); ms += DELAY; } } }; // Option to collapse top blocks. var collapseOption = { enabled: hasExpandedBlocks }; collapseOption.text = Blockly.Msg.COLLAPSE_ALL; collapseOption.callback = function () { toggleOption(true); }; menuOptions.push(collapseOption); // Option to expand top blocks. var expandOption = { enabled: hasCollapsedBlocks }; expandOption.text = Blockly.Msg.EXPAND_ALL; expandOption.callback = function () { toggleOption(false); }; menuOptions.push(expandOption); } // Option to delete all blocks. // Count the number of blocks that are deletable. var deleteList = []; function addDeletableBlocks(block) { if (block.isDeletable()) { deleteList = deleteList.concat(block.getDescendants()); } else { var children = block.getChildren(); for (var i = 0; i < children.length; i++) { addDeletableBlocks(children[i]); } } } for (var i = 0; i < topBlocks.length; i++) { addDeletableBlocks(topBlocks[i]); } var deleteOption = { text: deleteList.length == 1 ? Blockly.Msg.DELETE_BLOCK : Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(deleteList.length)), enabled: deleteList.length > 0, callback: function () { if (deleteList.length < 2 || window.confirm(Blockly.Msg.DELETE_ALL_BLOCKS.replace('%1', String(deleteList.length)))) { deleteNext(); } } }; function deleteNext() { Blockly.Events.setGroup(eventGroup); var block = deleteList.shift(); if (block) { if (block.workspace) { block.dispose(false, true); setTimeout(deleteNext, DELAY); } else { deleteNext(); } } Blockly.Events.setGroup(false); } menuOptions.push(deleteOption); Blockly.ContextMenu.show(e, menuOptions, this.RTL); }; Blockly.WorkspaceSvg.prototype.preloadAudio_ = function () {}; Blockly.BlockSvg.prototype.showContextMenu_ = function(e) { if (this.workspace.options.readOnly || !this.contextMenu) { return; } // Save the current block in a variable for use in closures. var block = this; var menuOptions = []; if (this.isDeletable() && this.isMovable() && !block.isInFlyout) { // Option to duplicate this block. var duplicateOption = { text: Blockly.Msg.DUPLICATE_BLOCK, enabled: true, callback: function() { Blockly.duplicate_(block); } }; if (this.getDescendants().length > this.workspace.remainingCapacity()) { duplicateOption.enabled = false; } menuOptions.push(duplicateOption); if (this.isEditable() && !this.collapsed_ && this.workspace.options.comments) { // Option to add/remove a comment. var commentOption = {enabled: !goog.userAgent.IE}; if (this.comment) { commentOption.text = Blockly.Msg.REMOVE_COMMENT; commentOption.callback = function() { block.setCommentText(null); }; } else { commentOption.text = Blockly.Msg.ADD_COMMENT; commentOption.callback = function() { block.setCommentText(''); }; } menuOptions.push(commentOption); } // Option to make block inline. if (!this.collapsed_) { for (var i = 1; i < this.inputList.length; i++) { if (this.inputList[i - 1].type != Blockly.NEXT_STATEMENT && this.inputList[i].type != Blockly.NEXT_STATEMENT) { // Only display this option if there are two value or dummy inputs // next to each other. var inlineOption = {enabled: true}; var isInline = this.getInputsInline(); inlineOption.text = isInline ? Blockly.Msg.EXTERNAL_INPUTS : Blockly.Msg.INLINE_INPUTS; inlineOption.callback = function() { block.setInputsInline(!isInline); }; menuOptions.push(inlineOption); break; } } } if (this.workspace.options.collapse) { // Option to collapse/expand block. if (this.collapsed_) { var expandOption = {enabled: true}; expandOption.text = Blockly.Msg.EXPAND_BLOCK; expandOption.callback = function() { block.setCollapsed(false); }; menuOptions.push(expandOption); } else { var collapseOption = {enabled: true}; collapseOption.text = Blockly.Msg.COLLAPSE_BLOCK; collapseOption.callback = function() { block.setCollapsed(true); }; menuOptions.push(collapseOption); } } if (this.workspace.options.disable) { // Option to disable/enable block. var disableOption = { text: this.disabled ? Blockly.Msg.ENABLE_BLOCK : Blockly.Msg.DISABLE_BLOCK, enabled: !this.getInheritedDisabled(), callback: function() { block.setDisabled(!block.disabled); } }; menuOptions.push(disableOption); } // Option to delete this block. // Count the number of blocks that are nested in this block. var descendantCount = this.getDescendants().length; var nextBlock = this.getNextBlock(); if (nextBlock) { // Blocks in the current stack would survive this block's deletion. descendantCount -= nextBlock.getDescendants().length; } var deleteOption = { text: descendantCount == 1 ? Blockly.Msg.DELETE_BLOCK : Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(descendantCount)), enabled: true, callback: function() { Blockly.Events.setGroup(true); block.dispose(true, true); Blockly.Events.setGroup(false); } }; menuOptions.push(deleteOption); } // Option to get help. var url = goog.isFunction(this.helpUrl) ? this.helpUrl() : this.helpUrl; var helpOption = {enabled: !!url}; helpOption.text = Blockly.Msg.HELP; helpOption.callback = function() { block.showHelp_(); }; menuOptions.push(helpOption); var toolOption = {}; toolOption.text = Blockly.Msg.TOOL; if(this.toolUrl){ toolOption.enabled = true; }else{ toolOption.enabled = false; } toolOption.callback = function() { block.showTool_(); }; menuOptions.push(toolOption); // Allow the block to add or modify menuOptions. if (this.customContextMenu && !block.isInFlyout) { this.customContextMenu(menuOptions); } Blockly.ContextMenu.show(e, menuOptions, this.RTL); Blockly.ContextMenu.currentBlock = this; }; Blockly.Block.prototype.setToolUrl = function(url) { this.toolUrl = url; }; Blockly.BlockSvg.prototype.showTool_ = function() { var url = goog.isFunction(this.toolUrl) ? this.helpUrl() : this.toolUrl; if (url) { window.open(url); } }; Blockly.Block.prototype.jsonInit = function(json) { // Validate inputs. goog.asserts.assert(json['output'] == undefined || json['previousStatement'] == undefined, 'Must not have both an output and a previousStatement.'); // Set basic properties of block. if (json['colour'] !== undefined) { this.setColour(json['colour']); } // Interpolate the message blocks. var i = 0; while (json['message' + i] !== undefined) { this.interpolate_(json['message' + i], json['args' + i] || [], json['lastDummyAlign' + i]); i++; } if (json['inputsInline'] !== undefined) { this.setInputsInline(json['inputsInline']); } // Set output and previous/next connections. if (json['output'] !== undefined) { this.setOutput(true, json['output']); } if (json['previousStatement'] !== undefined) { this.setPreviousStatement(true, json['previousStatement']); } if (json['nextStatement'] !== undefined) { this.setNextStatement(true, json['nextStatement']); } if (json['tooltip'] !== undefined) { this.setTooltip(json['tooltip']); } if (json['helpUrl'] !== undefined) { this.setHelpUrl(json['helpUrl']); } if (json['toolUrl'] !== undefined) { this.setToolUrl(json['toolUrl']); } }; Blockly.JavaScript.depth = 0; // Load the Code demo's language strings. document.write('\n'); // Load Blockly's language strings. document.write('\n'); document.write('\n'); if (Code.PAGE !== 'index') { document.write('\n'); document.write('\n'); document.write('\n'); } Promise.all([ Code.loadDoc(baseUrl + '/views/' + Code.PAGE.split('/')[0] + '.handlebars'), Code.loadDoc(baseUrl + '/toolbox/' + Code.PAGE + '.xml'), new Promise(function (resolve) { window.addEventListener('load', function () { resolve(); }, false); }) ]).then(function (values) { Code.initHandlebars(); Code.renderPage(values[0].body.innerHTML); Code.rawToolbox = values[1].body.firstChild; Code.init(Code.getToolBox()); Code.loadDemoArea(); Code.loadGa(); Code.ga(); Code.remind(); Code.importPrettify(); Code.bindHotkey(window.document); Promise.all([ Code.loadJs(baseUrl + '/lib/webduino-base.min.js'), Code.loadJs(baseUrl + '/webduino-blockly.js') ]).then(function () { Code.checkDeviceOnline(); }); Code.loadJs(baseUrl + '/webduino-samples.js', function () { Code.loadSample(); }); Code.loadJs(baseUrl + '/lib/clipboard.js', function () { Code.copyCode(); }); Code.loadJs(baseUrl + '/lib/babel.min.js'); Code.loadJs(baseUrl + '/lib/saveSvgAsPng.js'); });