1
0
Fork 0
mirror of https://github.com/sensebox/blockly-app synced 2025-02-23 16:23:59 +01:00
blockly-app/assets/blockly/ardublockly/ardublockly.js
2019-05-01 14:23:21 +00:00

767 lines
29 KiB
JavaScript

/**
* @license Licensed under the Apache License, Version 2.0 (the "License"):
* http://www.apache.org/licenses/LICENSE-2.0
*
* @fileoverview General javaScript for Arduino app with material design.
*/
'use strict';
/** Create a namespace for the application. */
var Ardublockly = Ardublockly || {};
/** Initialize function for Ardublockly, to be called on page load. */
Ardublockly.init = function(options) {
this.options = options || {};
this.options.blocklyDivId = this.options.blocklyDivId || 'content_blocks'
this.options.blocklyPath = this.options.blocklyPath || '..'
// Lang init must run first for the rest of the page to pick the right msgs
Ardublockly.initLanguage();
// Inject Blockly into content_blocks and fetch additional blocks
Ardublockly.injectBlockly(document.getElementById(this.options.blocklyDivId),
Ardublockly.TOOLBOX_XML, this.options.blocklyPath + '/blockly');
Ardublockly.importExtraBlocks();
Ardublockly.designJsInit();
Ardublockly.initialiseIdeButtons();
Ardublockly.bindDesignEventListeners();
Ardublockly.bindActionFunctions();
Ardublockly.bindBlocklyEventListeners();
// Hackish way to check if not running locally
if (document.location.hostname != 'localhost') {
Ardublockly.openNotConnectedModal();
console.log('Offline app modal opened as non localhost host name found: ' +
document.location.hostname)
}
};
/** Binds functions to each of the buttons, nav links, and related. */
Ardublockly.bindActionFunctions = function() {
// Navigation buttons
Ardublockly.bindClick_('button_load', Ardublockly.loadUserXmlFile);
Ardublockly.bindClick_('button_save', Ardublockly.saveXmlFile);
Ardublockly.bindClick_('button_save_ino', Ardublockly.saveSketchFile);
Ardublockly.bindClick_('button_delete', Ardublockly.discardAllBlocks);
// Side menu buttons, they also close the side menu
Ardublockly.bindClick_('menu_load', function() {
Ardublockly.loadUserXmlFile();
$('.button-collapse').sideNav('hide');
});
Ardublockly.bindClick_('menu_save', function() {
Ardublockly.saveXmlFile();
$('.button-collapse').sideNav('hide');
});
Ardublockly.bindClick_('menu_delete', function() {
Ardublockly.discardAllBlocks();
$('.button-collapse').sideNav('hide');
});
Ardublockly.bindClick_('menu_settings', function() {
Ardublockly.openSettings();
$('.button-collapse').sideNav('hide');
});
Ardublockly.bindClick_('menu_example_1', function() {
Ardublockly.loadServerXmlFile('../examples/blink.xml');
$('.button-collapse').sideNav('hide');
});
Ardublockly.bindClick_('menu_example_2', function() {
Ardublockly.loadServerXmlFile('../examples/display_print.xml');
$('.button-collapse').sideNav('hide');
});
Ardublockly.bindClick_('menu_example_3', function() {
Ardublockly.loadServerXmlFile('../examples/display_gps.xml');
$('.button-collapse').sideNav('hide');
});
Ardublockly.bindClick_('menu_example_4', function() {
Ardublockly.loadServerXmlFile('../examples/send_osem.xml');
$('.button-collapse').sideNav('hide');
});
// Floating buttons
Ardublockly.bindClick_('button_ide_large', function() {
Ardublockly.ideButtonLargeAction();
});
Ardublockly.bindClick_('button_ide_middle', function() {
Ardublockly.ideButtonMiddleAction();
});
Ardublockly.bindClick_('button_ide_left', function() {
Ardublockly.ideButtonLeftAction();
});
Ardublockly.bindClick_('button_load_xml', Ardublockly.XmlTextareaToBlocks);
Ardublockly.bindClick_('button_toggle_toolbox', Ardublockly.toogleToolbox);
// Settings modal input field listeners only if they can be edited
var settingsPathInputListeners = function(elId, setValFunc, setHtmlCallback) {
var el = document.getElementById(elId);
if (el.readOnly === false) {
// Event listener that send the data when the user presses 'Enter'
el.onkeypress = function(e) {
if (!e) e = window.event;
var keyCode = e.keyCode || e.which;
if (keyCode == '13') {
setValFunc(el.value, function(jsonObj) {
setHtmlCallback(ArdublocklyServer.jsonToHtmlTextInput(jsonObj));
});
return false;
}
};
// Event listener that send the data when moving out of the input field
el.onblur = function() {
setValFunc(el.value, function(jsonObj) {
setHtmlCallback(ArdublocklyServer.jsonToHtmlTextInput(jsonObj));
});
};
}
};
settingsPathInputListeners('settings_compiler_location',
ArdublocklyServer.setCompilerLocation,
Ardublockly.setCompilerLocationHtml);
settingsPathInputListeners('settings_sketch_location',
ArdublocklyServer.setSketchLocationHtml,
Ardublockly.setSketchLocationHtml);
};
/** Sets the Ardublockly server IDE setting to upload and sends the code. */
Ardublockly.ideSendUpload = function() {
// Check if this is the currently selected option before edit sever setting
if (Ardublockly.ideButtonLargeAction !== Ardublockly.ideSendUpload) {
Ardublockly.showExtraIdeButtons(false);
Ardublockly.setIdeSettings(null, 'upload');
}
Ardublockly.shortMessage(Ardublockly.getLocalStr('uploadingSketch'));
Ardublockly.resetIdeOutputContent();
Ardublockly.sendCode();
};
/** Sets the Ardublockly server IDE setting to verify and sends the code. */
Ardublockly.ideSendVerify = function() {
// Check if this is the currently selected option before edit sever setting
if (Ardublockly.ideButtonLargeAction !== Ardublockly.ideSendVerify) {
Ardublockly.showExtraIdeButtons(false);
Ardublockly.setIdeSettings(null, 'verify');
}
Ardublockly.shortMessage(Ardublockly.getLocalStr('verifyingSketch'));
Ardublockly.resetIdeOutputContent();
Ardublockly.sendCode();
};
/** Sets the Ardublockly server IDE setting to open and sends the code. */
Ardublockly.ideSendOpen = function() {
// Check if this is the currently selected option before edit sever setting
if (Ardublockly.ideButtonLargeAction !== Ardublockly.ideSendOpen) {
Ardublockly.showExtraIdeButtons(false);
Ardublockly.setIdeSettings(null, 'open');
}
Ardublockly.shortMessage(Ardublockly.getLocalStr('openingSketch'));
Ardublockly.resetIdeOutputContent();
Ardublockly.sendCode();
};
/** Function bound to the left IDE button, to be changed based on settings. */
Ardublockly.ideButtonLargeAction = Ardublockly.ideSendUpload;
/** Function bound to the middle IDE button, to be changed based on settings. */
Ardublockly.ideButtonMiddleAction = Ardublockly.ideSendVerify;
/** Function bound to the large IDE button, to be changed based on settings. */
Ardublockly.ideButtonLeftAction = Ardublockly.ideSendOpen;
/** Initialises the IDE buttons with the default option from the server. */
Ardublockly.initialiseIdeButtons = function() {
document.getElementById('button_ide_left').title =
Ardublockly.getLocalStr('openSketch');
document.getElementById('button_ide_middle').title =
Ardublockly.getLocalStr('verifySketch');
document.getElementById('button_ide_large').title =
Ardublockly.getLocalStr('uploadSketch');
ArdublocklyServer.requestIdeOptions(function(jsonObj) {
if (jsonObj != null) {
Ardublockly.changeIdeButtons(jsonObj.selected);
} // else Null: Ardublockly server is not running, do nothing
});
};
/**
* Changes the IDE launch buttons based on the option indicated in the argument.
* @param {!string} value One of the 3 possible values from the drop down select
* in the settings modal: 'upload', 'verify', or 'open'.
*/
Ardublockly.changeIdeButtons = function(value) {
var largeButton = document.getElementById('button_ide_large');
var middleButton = document.getElementById('button_ide_middle');
var leftButton = document.getElementById('button_ide_left');
var openTitle = Ardublockly.getLocalStr('openSketch');
var verifyTitle = Ardublockly.getLocalStr('verifySketch');
var uploadTitle = Ardublockly.getLocalStr('uploadSketch');
if (value === 'upload') {
Ardublockly.changeIdeButtonsDesign(value);
Ardublockly.ideButtonLeftAction = Ardublockly.ideSendOpen;
Ardublockly.ideButtonMiddleAction = Ardublockly.ideSendVerify;
Ardublockly.ideButtonLargeAction = Ardublockly.ideSendUpload;
leftButton.title = openTitle;
middleButton.title = verifyTitle;
largeButton.title = uploadTitle;
} else if (value === 'verify') {
Ardublockly.changeIdeButtonsDesign(value);
Ardublockly.ideButtonLeftAction = Ardublockly.ideSendOpen;
Ardublockly.ideButtonMiddleAction = Ardublockly.ideSendUpload;
Ardublockly.ideButtonLargeAction = Ardublockly.ideSendVerify;
leftButton.title = openTitle;
middleButton.title = uploadTitle;
largeButton.title = verifyTitle;
} else if (value === 'open') {
Ardublockly.changeIdeButtonsDesign(value);
Ardublockly.ideButtonLeftAction = Ardublockly.ideSendVerify;
Ardublockly.ideButtonMiddleAction = Ardublockly.ideSendUpload;
Ardublockly.ideButtonLargeAction = Ardublockly.ideSendOpen;
leftButton.title = verifyTitle;
middleButton.title = uploadTitle;
largeButton.title = openTitle;
}
};
/**
* Loads an XML file from the server and replaces the current blocks into the
* Blockly workspace.
* @param {!string} xmlFile Server location of the XML file to load.
*/
Ardublockly.loadServerXmlFile = function(xmlFile) {
var loadXmlfileAccepted = function() {
// loadXmlBlockFile loads the file asynchronously and needs a callback
var loadXmlCb = function(sucess) {
if (sucess) {
Ardublockly.renderContent();
} else {
Ardublockly.alertMessage(
Ardublockly.getLocalStr('invalidXmlTitle'),
Ardublockly.getLocalStr('invalidXmlBody'),
false);
}
};
var connectionErrorCb = function() {
Ardublockly.openNotConnectedModal();
};
Ardublockly.loadXmlBlockFile(xmlFile, loadXmlCb, connectionErrorCb);
};
if (Ardublockly.isWorkspaceEmpty()) {
loadXmlfileAccepted();
} else {
Ardublockly.alertMessage(
Ardublockly.getLocalStr('loadNewBlocksTitle'),
Ardublockly.getLocalStr('loadNewBlocksBody'),
true, loadXmlfileAccepted);
}
};
/**
* Loads an XML file from the users file system and adds the blocks into the
* Blockly workspace.
*/
Ardublockly.loadUserXmlFile = function() {
// Create File Reader event listener function
var parseInputXMLfile = function(e) {
var xmlFile = e.target.files[0];
var filename = xmlFile.name;
var extensionPosition = filename.lastIndexOf('.');
if (extensionPosition !== -1) {
filename = filename.substr(0, extensionPosition);
}
var reader = new FileReader();
reader.onload = function() {
var success = Ardublockly.replaceBlocksfromXml(reader.result);
if (success) {
Ardublockly.renderContent();
Ardublockly.sketchNameSet(filename);
} else {
Ardublockly.alertMessage(
Ardublockly.getLocalStr('invalidXmlTitle'),
Ardublockly.getLocalStr('invalidXmlBody'),
false);
}
};
reader.readAsText(xmlFile);
};
// Create once invisible browse button with event listener, and click it
var selectFile = document.getElementById('select_file');
if (selectFile === null) {
var selectFileDom = document.createElement('INPUT');
selectFileDom.type = 'file';
selectFileDom.id = 'select_file';
var selectFileWrapperDom = document.createElement('DIV');
selectFileWrapperDom.id = 'select_file_wrapper';
selectFileWrapperDom.style.display = 'none';
selectFileWrapperDom.appendChild(selectFileDom);
document.body.appendChild(selectFileWrapperDom);
selectFile = document.getElementById('select_file');
selectFile.addEventListener('change', parseInputXMLfile, false);
}
selectFile.click();
};
/**
* Creates an XML file containing the blocks from the Blockly workspace and
* prompts the users to save it into their local file system.
*/
Ardublockly.saveXmlFile = function() {
Ardublockly.saveTextFileAs(
document.getElementById('sketch_name').value + '.xml',
Ardublockly.generateXml());
};
/**
* Creates an Arduino Sketch file containing the Arduino code generated from
* the Blockly workspace and prompts the users to save it into their local file
* system.
*/
Ardublockly.saveSketchFile = function() {
Ardublockly.saveTextFileAs(
document.getElementById('sketch_name').value + '.ino',
Ardublockly.generateArduino());
};
/**
* Creates an text file with the input content and files name, and prompts the
* users to save it into their local file system.
* @param {!string} fileName Name for the file to be saved.
* @param {!string} content Text datd to be saved in to the file.
*/
Ardublockly.saveTextFileAs = function(fileName, content) {
var blob = new Blob([content], {type: 'text/plain;charset=utf-8'});
saveAs(blob, fileName);
};
/**
* Retrieves the Settings from ArdublocklyServer to populates the form data
* and opens the Settings modal dialog.
*/
Ardublockly.openSettings = function() {
ArdublocklyServer.requestCompilerLocation(function(jsonObj) {
Ardublockly.setCompilerLocationHtml(
ArdublocklyServer.jsonToHtmlTextInput(jsonObj));
});
ArdublocklyServer.requestSketchLocation(function(jsonObj) {
Ardublockly.setSketchLocationHtml(
ArdublocklyServer.jsonToHtmlTextInput(jsonObj));
});
ArdublocklyServer.requestArduinoBoards(function(jsonObj) {
Ardublockly.setArduinoBoardsHtml(
ArdublocklyServer.jsonToHtmlDropdown(jsonObj));
});
ArdublocklyServer.requestSerialPorts(function(jsonObj) {
Ardublockly.setSerialPortsHtml(
ArdublocklyServer.jsonToHtmlDropdown(jsonObj));
});
ArdublocklyServer.requestIdeOptions(function(jsonObj) {
Ardublockly.setIdeHtml(ArdublocklyServer.jsonToHtmlDropdown(jsonObj));
});
// Language menu only set on page load within Ardublockly.initLanguage()
Ardublockly.openSettingsModal();
};
/**
* Sets the compiler location form data retrieve from an updated element.
* @param {element} jsonResponse JSON data coming back from the server.
* @return {undefined} Might exit early if response is null.
*/
Ardublockly.setCompilerLocationHtml = function(newEl) {
if (newEl === null) return Ardublockly.openNotConnectedModal();
var compLocIp = document.getElementById('settings_compiler_location');
if (compLocIp != null) {
compLocIp.value = newEl.value || compLocIp.value ||
'Please enter the location of the Arduino IDE executable';
compLocIp.style.cssText = newEl.style.cssText;
}
};
/**
* Sets the sketch location form data retrieve from an updated element.
* @param {element} jsonResponse JSON data coming back from the server.
* @return {undefined} Might exit early if response is null.
*/
Ardublockly.setSketchLocationHtml = function(newEl) {
if (newEl === null) return Ardublockly.openNotConnectedModal();
var sketchLocIp = document.getElementById('settings_sketch_location');
if (sketchLocIp != null) {
sketchLocIp.value = newEl.value || sketchLocIp.value ||
'Please enter a folder to store the Arduino Sketch';
sketchLocIp.style.cssText = newEl.style.cssText;
}
};
/**
* Replaces the Arduino Boards form data with a new HTMl element.
* Ensures there is a change listener to call 'setSerialPort' function
* @param {element} jsonObj JSON data coming back from the server.
* @return {undefined} Might exit early if response is null.
*/
Ardublockly.setArduinoBoardsHtml = function(newEl) {
if (newEl === null) return Ardublockly.openNotConnectedModal();
var boardDropdown = document.getElementById('board');
if (boardDropdown !== null) {
// Restarting the select elements built by materialize
$('select').material_select('destroy');
newEl.name = 'settings_board';
newEl.id = 'board';
newEl.onchange = Ardublockly.setBoard;
boardDropdown.parentNode.replaceChild(newEl, boardDropdown);
// Refresh the materialize select menus
$('select').material_select();
}
};
/**
* Sets the Arduino Board type with the selected user input from the drop down.
*/
Ardublockly.setBoard = function() {
var el = document.getElementById('board');
var boardValue = el.options[el.selectedIndex].value;
ArdublocklyServer.setArduinoBoard(boardValue, function(jsonObj) {
var newEl = ArdublocklyServer.jsonToHtmlDropdown(jsonObj);
Ardublockly.setArduinoBoardsHtml(newEl);
});
Ardublockly.changeBlocklyArduinoBoard(
boardValue.toLowerCase().replace(/ /g, '_'));
};
/**
* Replaces the Serial Port form data with a new HTMl element.
* Ensures there is a change listener to call 'setSerialPort' function
* @param {element} jsonResponse JSON data coming back from the server.
* @return {undefined} Might exit early if response is null.
*/
Ardublockly.setSerialPortsHtml = function(newEl) {
if (newEl === null) return Ardublockly.openNotConnectedModal();
var serialDropdown = document.getElementById('serial_port');
if (serialDropdown !== null) {
// Restarting the select elements built by materialize
$('select').material_select('destroy');
newEl.name = 'settings_serial';
newEl.id = 'serial_port';
newEl.onchange = Ardublockly.setSerial;
serialDropdown.parentNode.replaceChild(newEl, serialDropdown);
// Refresh the materialize select menus
$('select').material_select();
}
};
/** Sets the Serial Port with the selected user input from the drop down. */
Ardublockly.setSerial = function() {
var el = document.getElementById('serial_port');
var serialValue = el.options[el.selectedIndex].value;
ArdublocklyServer.setSerialPort(serialValue, function(jsonObj) {
var newEl = ArdublocklyServer.jsonToHtmlDropdown(jsonObj);
Ardublockly.setSerialPortsHtml(newEl);
});
};
/**
* Replaces IDE options form data with a new HTMl element.
* Ensures there is a change listener to call 'setIdeSettings' function
* @param {element} jsonResponse JSON data coming back from the server.
* @return {undefined} Might exit early if response is null.
*/
Ardublockly.setIdeHtml = function(newEl) {
if (newEl === null) return Ardublockly.openNotConnectedModal();
var ideDropdown = document.getElementById('ide_settings');
if (ideDropdown !== null) {
// Restarting the select elements built by materialize
$('select').material_select('destroy');
newEl.name = 'settings_ide';
newEl.id = 'ide_settings';
newEl.onchange = Ardublockly.setIdeSettings;
ideDropdown.parentNode.replaceChild(newEl, ideDropdown);
// Refresh the materialize select menus
$('select').material_select();
}
};
/**
* Sets the IDE settings data with the selected user input from the drop down.
* @param {Event} e Event that triggered this function call. Required for link
* it to the listeners, but not used.
* @param {string} preset A value to set the IDE settings bypassing the drop
* down selected value. Valid data: 'upload', 'verify', or 'open'.
*/
Ardublockly.setIdeSettings = function(e, preset) {
if (preset !== undefined) {
var ideValue = preset;
} else {
var el = document.getElementById('ide_settings');
var ideValue = el.options[el.selectedIndex].value;
}
Ardublockly.changeIdeButtons(ideValue);
ArdublocklyServer.setIdeOptions(ideValue, function(jsonObj) {
Ardublockly.setIdeHtml(ArdublocklyServer.jsonToHtmlDropdown(jsonObj));
});
};
/**
* Send the Arduino Code to the ArdublocklyServer to process.
* Shows a loader around the button, blocking it (unblocked upon received
* message from server).
*/
Ardublockly.sendCode = function() {
Ardublockly.largeIdeButtonSpinner(true);
/**
* Receives the IDE data back to be displayed and stops spinner.
* @param {element} jsonResponse JSON data coming back from the server.
* @return {undefined} Might exit early if response is null.
*/
var sendCodeReturn = function(jsonObj) {
Ardublockly.largeIdeButtonSpinner(false);
if (jsonObj === null) return Ardublockly.openNotConnectedModal();
var dataBack = ArdublocklyServer.jsonToIdeModal(jsonObj);
Ardublockly.arduinoIdeOutput(dataBack);
};
ArdublocklyServer.sendSketchToServer(
Ardublockly.generateArduino(), sendCodeReturn);
};
/** Populate the workspace blocks with the XML written in the XML text area. */
Ardublockly.XmlTextareaToBlocks = function() {
var success = Ardublockly.replaceBlocksfromXml(
document.getElementById('content_xml').value);
if (success) {
Ardublockly.renderContent();
} else {
Ardublockly.alertMessage(
Ardublockly.getLocalStr('invalidXmlTitle'),
Ardublockly.getLocalStr('invalidXmlBody'),
false);
}
};
/**
* Private variable to save the previous version of the Arduino Code.
* @type {!String}
* @private
*/
Ardublockly.PREV_ARDUINO_CODE_ = 'void setup() {\n\n}\n\n\nvoid loop() {\n\n}';
/**
* Populate the Arduino Code and Blocks XML panels with content generated from
* the blocks.
*/
Ardublockly.renderContent = function() {
Ardublockly.workspace.addChangeListener(Blockly.Events.disableOrphans);
// Render Arduino Code with latest change highlight and syntax highlighting
var arduinoCode = Ardublockly.generateArduino();
if (arduinoCode !== Ardublockly.PREV_ARDUINO_CODE_) {
var diff = JsDiff.diffWords(Ardublockly.PREV_ARDUINO_CODE_, arduinoCode);
var resultStringArray = [];
for (var i = 0; i < diff.length; i++) {
if (!diff[i].removed) {
var escapedCode = diff[i].value.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
if (diff[i].added) {
resultStringArray.push(
'<span class="code_highlight_new">' + escapedCode + '</span>');
} else {
resultStringArray.push(escapedCode);
}
}
}
document.getElementById('content_arduino').innerHTML =
prettyPrintOne(resultStringArray.join(''), 'cpp', false);
Ardublockly.PREV_ARDUINO_CODE_ = arduinoCode;
}
// Generate plain XML into element
document.getElementById('content_xml').value = Ardublockly.generateXml();
};
/**
* Private variable to indicate if the toolbox is meant to be shown.
* @type {!boolean}
* @private
*/
Ardublockly.TOOLBAR_SHOWING_ = true;
/**
* Toggles the blockly toolbox and the Ardublockly toolbox button On and Off.
* Uses namespace member variable TOOLBAR_SHOWING_ to toggle state.
*/
Ardublockly.toogleToolbox = function() {
if (Ardublockly.TOOLBAR_SHOWING_) {
Ardublockly.blocklyCloseToolbox();
Ardublockly.displayToolbox(false);
} else {
Ardublockly.displayToolbox(true);
}
Ardublockly.TOOLBAR_SHOWING_ = !Ardublockly.TOOLBAR_SHOWING_;
};
/** @return {boolean} Indicates if the toolbox is currently visible. */
Ardublockly.isToolboxVisible = function() {
return Ardublockly.TOOLBAR_SHOWING_;
};
/**
* Lazy loads the additional block JS files from the ./block directory.
* Initialises any additional Ardublockly extensions.
* TODO: Loads the examples into the examples modal
*/
Ardublockly.importExtraBlocks = function() {
/**
* Parses the JSON data to find the block and languages js files.
* @param {jsonDataObj} jsonDataObj JSON in JavaScript object format, null
* indicates an error occurred.
* @return {undefined} Might exit early if response is null.
*/
var blockPath = this.options.blocklyPath + '/blocks/';
var jsonDataCb = function(jsonDataObj) {
if (jsonDataObj === null) return Ardublockly.openNotConnectedModal();
if (jsonDataObj.categories !== undefined) {
var head = document.getElementsByTagName('head')[0];
for (var catDir in jsonDataObj.categories) {
var blocksJsLoad = document.createElement('script');
blocksJsLoad.src = blockPath + catDir + '/blocks.js';
head.appendChild(blocksJsLoad);
var blocksLangJsLoad = document.createElement('script');
blocksLangJsLoad.src = blockPath + catDir + '/msg/' + 'messages.js';
//'lang/' + Ardublockly.LANG + '.js';
head.appendChild(blocksLangJsLoad);
var blocksGeneratorJsLoad = document.createElement('script');
blocksGeneratorJsLoad.src = blockPath + catDir + '/generator_arduino.js';
head.appendChild(blocksGeneratorJsLoad);
// Check if the blocks add additional Ardublockly functionality
var extensions = jsonDataObj.categories[catDir].extensions;
if (extensions) {
for (var i = 0; i < extensions.length; i++) {
var blockExtensionJsLoad = document.createElement('script');
blockExtensionJsLoad.src = blockPath + catDir + '/extensions.js';
head.appendChild(blockExtensionJsLoad);
// Add function to scheduler as lazy loading has to complete first
setTimeout(function(category, extension) {
var extensionNamespaces = extension.split('.');
var extensionCall = window;
var invalidFunc = false;
for (var j = 0; j < extensionNamespaces.length; j++) {
extensionCall = extensionCall[extensionNamespaces[j]];
if (extensionCall === undefined) {
invalidFunc = true;
break;
}
}
if (typeof extensionCall != 'function') {
invalidFunc = true;
}
if (invalidFunc) {
throw 'Blocks ' + category.categoryName + ' extension "' +
extension + '" is not a valid function.';
} else {
extensionCall();
}
}, 800, jsonDataObj.categories[catDir], extensions[i]);
}
}
}
}
};
// Reads the JSON data containing all block categories from ./blocks directory
// TODO: Now reading a local file, to be replaced by server generated JSON
ArdublocklyServer.getJson(blockPath + '/blocks_data.json', jsonDataCb);
};
/** Opens a modal with a list of categories to add or remove to the toolbox */
Ardublockly.openExtraCategoriesSelect = function() {
/**
* Parses the JSON data from the server into a list of additional categories.
* @param {jsonDataObj} jsonDataObj JSON in JavaScript object format, null
* indicates an error occurred.
* @return {undefined} Might exit early if response is null.
*/
var jsonDataCb = function(jsonDataObj) {
if (jsonDataObj === null) return Ardublockly.openNotConnectedModal();
var htmlContent = document.createElement('div');
if (jsonDataObj.categories !== undefined) {
for (var catDir in jsonDataObj.categories) {
// Function required to maintain each loop variable scope separated
(function(cat) {
var clickBind = function(tickValue) {
if (tickValue) {
var catDom = (new DOMParser()).parseFromString(
cat.toolbox.join(''), 'text/xml').firstChild;
Ardublockly.addToolboxCategory(cat.toolboxName, catDom);
} else {
Ardublockly.removeToolboxCategory(cat.toolboxName);
}
};
htmlContent.appendChild(Ardublockly.createExtraBlocksCatHtml(
cat.categoryName, cat.description, clickBind));
})(jsonDataObj.categories[catDir]);
}
}
Ardublockly.openAdditionalBlocksModal(htmlContent);
};
// Reads the JSON data containing all block categories from ./blocks directory
// TODO: Now reading a local file, to be replaced by server generated JSON
ArdublocklyServer.getJson(this.options.blocklyPath + '/blocks/blocks_data.json', jsonDataCb);
};
/** Informs the user that the selected function is not yet implemented. */
Ardublockly.functionNotImplemented = function() {
Ardublockly.shortMessage('Function not yet implemented');
};
/**
* Interface to display messages with a possible action.
* @param {!string} title HTML to include in title.
* @param {!element} body HTML to include in body.
* @param {boolean=} confirm Indicates if the user is shown a single option (ok)
* or an option to cancel, with an action applied to the "ok".
* @param {string=|function=} callback If confirm option is selected this would
* be the function called when clicked 'OK'.
*/
Ardublockly.alertMessage = function(title, body, confirm, callback) {
Ardublockly.materialAlert(title, body, confirm, callback);
};
/**
* Interface to displays a short message, which disappears after a time out.
* @param {!string} message Text to be temporarily displayed.
*/
Ardublockly.shortMessage = function(message) {
Ardublockly.MaterialToast(message);
};
/**
* 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.
* @private
*/
Ardublockly.bindClick_ = function(el, func) {
if (typeof el == 'string') {
el = document.getElementById(el);
}
// Need to ensure both, touch and click, events don't fire for the same thing
var propagateOnce = function(e) {
e.stopPropagation();
e.preventDefault();
func();
};
el.addEventListener('ontouchend', propagateOnce);
el.addEventListener('click', propagateOnce);
};