mirror of
https://github.com/sensebox/blockly-app
synced 2025-02-23 16:23:59 +01:00
326 lines
10 KiB
JavaScript
326 lines
10 KiB
JavaScript
/**
|
|
* @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;
|
|
|
|
/**
|
|
* Injects Blockly into a given HTML element. Toolbox XMl has to be a string.
|
|
* @param {!Element} blocklyEl Element to inject Blockly into.
|
|
* @param {!string} toolboxXml String containing the toolbox XML content.
|
|
* @param {!string} blocklyPath String containing the Blockly directory path.
|
|
*/
|
|
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();
|
|
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
|
|
}
|
|
});
|
|
// On language change the blocks have been stored in session storage
|
|
Ardublockly.loadSessionStorageBlocks();
|
|
};
|
|
|
|
/** Binds the event listeners relevant to Blockly. */
|
|
Ardublockly.bindBlocklyEventListeners = function() {
|
|
Ardublockly.workspace.addChangeListener(function(event) {
|
|
if (event.type != Blockly.Events.UI) {
|
|
Ardublockly.renderContent();
|
|
}
|
|
});
|
|
// Ensure the Blockly workspace resizes accordingly
|
|
window.addEventListener('resize',
|
|
function() { Blockly.asyncSvgResize(Ardublockly.workspace); }, false);
|
|
};
|
|
|
|
/** @return {!string} Generated Arduino code from the Blockly workspace. */
|
|
Ardublockly.generateArduino = function() {
|
|
return Blockly.Arduino.workspaceToCode(Ardublockly.workspace);
|
|
};
|
|
|
|
/** @return {!string} Generated XML code from the Blockly workspace. */
|
|
Ardublockly.generateXml = function() {
|
|
var xmlDom = Blockly.Xml.workspaceToDom(Ardublockly.workspace);
|
|
return Blockly.Xml.domToPrettyText(xmlDom);
|
|
};
|
|
|
|
/**
|
|
* Loads an XML file from the server and replaces the current blocks into the
|
|
* Blockly workspace.
|
|
* @param {!string} xmlFile XML file path in a reachable server (no local path).
|
|
* @param {!function} cbSuccess Function to be called once the file is loaded.
|
|
* @param {!function} cbError Function to be called if there is a connection
|
|
* error to the XML server.
|
|
*/
|
|
Ardublockly.loadXmlBlockFile = function(xmlFile, cbSuccess, cbError) {
|
|
var request = Ardublockly.ajaxRequest();
|
|
var requestCb = function() {
|
|
if (request.readyState == 4) {
|
|
if (request.status == 200) {
|
|
var success = Ardublockly.replaceBlocksfromXml(request.responseText);
|
|
cbSuccess(success);
|
|
} else {
|
|
cbError();
|
|
}
|
|
}
|
|
};
|
|
try {
|
|
request.open('GET', xmlFile, true);
|
|
request.onreadystatechange = requestCb;
|
|
request.send(null);
|
|
} catch (e) {
|
|
cbError();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Parses the XML from its argument input to generate and replace the blocks
|
|
* in the Blockly workspace.
|
|
* @param {!string} blocksXml String of XML code for the blocks.
|
|
* @return {!boolean} Indicates if the XML into blocks parse was successful.
|
|
*/
|
|
Ardublockly.replaceBlocksfromXml = function(blocksXml) {
|
|
var xmlDom = null;
|
|
try {
|
|
xmlDom = Blockly.Xml.textToDom(blocksXml);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
Ardublockly.workspace.clear();
|
|
var sucess = false;
|
|
if (xmlDom) {
|
|
sucess = Ardublockly.loadBlocksfromXmlDom(xmlDom);
|
|
}
|
|
return sucess;
|
|
};
|
|
|
|
/**
|
|
* Parses the XML from its argument to add the blocks to the 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, Ardublockly.workspace);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* 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(Ardublockly.workspace);
|
|
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, Ardublockly.workspace);
|
|
}
|
|
};
|
|
|
|
/** Discard all blocks from the workspace. */
|
|
Ardublockly.discardAllBlocks = function() {
|
|
var blockCount = Ardublockly.workspace.getAllBlocks().length;
|
|
if (blockCount == 1) {
|
|
Ardublockly.workspace.clear();
|
|
Ardublockly.renderContent();
|
|
} else if (blockCount > 1) {
|
|
Ardublockly.alertMessage(
|
|
Ardublockly.getLocalStr('discardBlocksTitle'),
|
|
Ardublockly.getLocalStr('discardBlocksBody')
|
|
.replace('%1', blockCount),
|
|
true,
|
|
function() {
|
|
Ardublockly.workspace.clear();
|
|
Ardublockly.renderContent();
|
|
});
|
|
}
|
|
};
|
|
|
|
/** @return {!boolean} Indicates if the Blockly workspace has blocks. */
|
|
Ardublockly.isWorkspaceEmpty = function() {
|
|
return Ardublockly.workspace.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.Arduino.Boards.selected !== Blockly.Arduino.Boards[newBoard]) {
|
|
Blockly.Arduino.Boards.changeBoard(Ardublockly.workspace, newBoard);
|
|
}
|
|
};
|
|
|
|
/** Update the toolbox categories language. */
|
|
Ardublockly.updateToolboxLanguage = function() {
|
|
var categories = ['catLogic', 'catLoops', 'catMath', 'catText',
|
|
'catVariables', 'catFunctions', 'catInputOutput',
|
|
'catTime', 'catAudio', 'catMotors', 'catComms'];
|
|
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);
|
|
Ardublockly.workspace.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);
|
|
}
|
|
}
|
|
}
|
|
Ardublockly.workspace.updateToolbox(Ardublockly.xmlTree);
|
|
};
|
|
|
|
/** Closes the toolbox block container sub-menu. */
|
|
Ardublockly.blocklyCloseToolbox = function() {
|
|
Ardublockly.workspace.toolbox_.flyout_.hide();
|
|
};
|
|
|
|
/** @return {!integer} The width of the blockly workspace toolbox. */
|
|
Ardublockly.blocklyToolboxWidth = function() {
|
|
return Ardublockly.workspace.toolbox_.width;
|
|
};
|
|
|
|
/** @return {!boolean} Indicates if a block is currently being dragged. */
|
|
Ardublockly.blocklyIsDragging = function() {
|
|
return (Blockly.dragMode_ != 0) ? true : false;
|
|
};
|
|
|
|
/** Wraps the blockly 'cut' functionality. */
|
|
Ardublockly.blocklyCut = function() {
|
|
if (Blockly.selected) {
|
|
Blockly.copy_(Blockly.selected);
|
|
Blockly.selected.dispose(true, true);
|
|
}
|
|
};
|
|
|
|
/** Wraps the blockly 'copy' functionality. */
|
|
Ardublockly.blocklyCopy = function() {
|
|
if (Blockly.selected) {
|
|
Blockly.copy_(Blockly.selected);
|
|
}
|
|
};
|
|
|
|
/** Wraps the blockly 'paste' functionality. */
|
|
Ardublockly.blocklyPaste = function() {
|
|
if (Blockly.clipboardXml_) {
|
|
Blockly.hideChaff();
|
|
Blockly.clipboardSource_.paste(Blockly.clipboardXml_);
|
|
}
|
|
};
|
|
|
|
/** Wraps the blockly 'delete' functionality. */
|
|
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;
|
|
};
|