/**
* 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');
});