forked from Open-CT/openct-tasks
1907 lines
76 KiB
JavaScript
1907 lines
76 KiB
JavaScript
var getUrlParameter = function getUrlParameter(sParam) {
|
|
var sPageURL = decodeURIComponent(window.location.search.substring(1)),
|
|
sURLVariables = sPageURL.split('&'),
|
|
sParameterName,
|
|
i;
|
|
|
|
for (i = 0; i < sURLVariables.length; i++) {
|
|
sParameterName = sURLVariables[i].split('=');
|
|
|
|
if (sParameterName[0] === sParam) {
|
|
return sParameterName[1] === undefined ? true : sParameterName[1];
|
|
}
|
|
}
|
|
};
|
|
|
|
var highlightPause = false;
|
|
|
|
function resetFormElement(e) {
|
|
e.wrap('<form>').closest('form').get(0).reset();
|
|
e.unwrap();
|
|
|
|
// Prevent form submission
|
|
//e.stopPropagation();
|
|
//e.preventDefault();
|
|
}
|
|
|
|
function getBlocklyHelper(maxBlocks, nbTestCases) {
|
|
return {
|
|
textFile: null,
|
|
extended: false,
|
|
programs: [],
|
|
languages: [],
|
|
player: 0,
|
|
workspace: null,
|
|
prevWidth: 0,
|
|
definitions: {},
|
|
generators: [],
|
|
groupByCategory: true,
|
|
includedAll: true,
|
|
includedCategories : [],
|
|
includedBlocks: [],
|
|
availableVariables: [],
|
|
languageStrings: {
|
|
fr: {
|
|
actions: "Actions",
|
|
sensors: "Capteurs",
|
|
debug: "Débuggage",
|
|
colour: "Couleurs",
|
|
dicts: "Dictionnaires",
|
|
inputs: "Entrées",
|
|
lists: "Listes",
|
|
logic: "Logique",
|
|
loops: "Boucles",
|
|
math: "Maths",
|
|
text: "Texte",
|
|
variables: "Variables",
|
|
functions: "Fonctions",
|
|
invalidContent: "Contenu invalide",
|
|
unknownFileType: "Type de fichier non reconnu",
|
|
download: "télécharger",
|
|
smallestOfTwoNumbers: "Plus petit des deux nombres",
|
|
greatestOfTwoNumbers: "Plus grand des deux nombres",
|
|
programOfRobot: "Programme du robot",
|
|
tooManyIterations: "Votre programme met trop de temps à se terminer !",
|
|
if: "si",
|
|
do: "faire",
|
|
else: "sinon",
|
|
|
|
submitProgram: "Valider le programme",
|
|
runProgram: "Exécuter sur ce test",
|
|
stopProgram: "Recommencer",
|
|
speed: "Vitesse :",
|
|
slowSpeed: "Lent",
|
|
mediumSpeed: "Moyen",
|
|
fastSpeed: "Rapide",
|
|
ludicrousSpeed: "Très rapide",
|
|
selectLanguage: "Langage :",
|
|
blocklyLanguage: "Blockly",
|
|
javascriptLanguage: "Javascript",
|
|
importFromBlockly: "Repartir de blockly",
|
|
saveOrLoadProgram: "Enregistrer ou recharger votre programme :",
|
|
avoidReloadingOtherTask: "Attention : ne rechargez pas le programme d'un autre sujet !",
|
|
reloadProgram: "Recharger :",
|
|
saveProgram: "Enregistrer",
|
|
limitBlocks1: " blocs restants sur ",
|
|
limitBlocks2: " autorisés."
|
|
},
|
|
en: {
|
|
actions: "Actions",
|
|
sensors: "Sensors",
|
|
debug: "Debug",
|
|
colour: "Colors",
|
|
dicts: "Dictionnaries",
|
|
inputs: "Inputs",
|
|
lists: "Lists",
|
|
logic: "Logic",
|
|
loops: "Loops",
|
|
math: "Math",
|
|
text: "Text",
|
|
variables: "Variables",
|
|
functions: "Functions",
|
|
invalidContent: "Invalid content",
|
|
unknownFileType: "Unrecognized file type",
|
|
download: "download",
|
|
smallestOfTwoNumbers: "Smallest of the two numbers",
|
|
greatestOfTwoNumbers: "Greatest of the two numbers",
|
|
programOfRobot: "Robot's program",
|
|
tooManyIterations: "Too many iterations before an action!",
|
|
if: "if",
|
|
do: "do",
|
|
else: "else",
|
|
submitProgram: "Validate this program",
|
|
runProgram: "Run this program",
|
|
stopProgram: "Stop",
|
|
speed: "Speed:",
|
|
slowSpeed: "Slow",
|
|
mediumSpeed: "Medium",
|
|
fastSpeed: "Fast",
|
|
ludicrousSpeed: "Very fast",
|
|
selectLanguage: "Language :",
|
|
blocklyLanguage: "Blockly",
|
|
javascriptLanguage: "Javascript",
|
|
importFromBlockly: "Generate from blockly",
|
|
saveOrLoadProgram: "Save or reload your code:",
|
|
avoidReloadingOtherTask: "Warning: do not reload code for another task!",
|
|
reloadProgram: "Reload:",
|
|
saveProgram: "Save",
|
|
limitBlocks1: " blocks remaining out of ",
|
|
limitBlocks2: " available."
|
|
},
|
|
de: {
|
|
actions: "Aktionen",
|
|
sensors: "Sensoren",
|
|
debug: "Debug",
|
|
colour: "Farben",
|
|
dicts: "Diktionär",
|
|
inputs: "Eingaben",
|
|
lists: "Listen",
|
|
logic: "Logik",
|
|
loops: "Schleifen",
|
|
math: "Mathe",
|
|
text: "Texte",
|
|
variables: "Variablen",
|
|
functions: "Funktionen",
|
|
invalidContent: "Ungültiger Inhalt",
|
|
unknownFileType: "Ungültiger Datentyp",
|
|
download: "Herunterladen",
|
|
smallestOfTwoNumbers: "Kleinste von zwei Zahlen",
|
|
greatestOfTwoNumbers: "Größte von zwei Zahlen",
|
|
programOfRobot: "Programm des Roboters",
|
|
tooManyIterations: "Zu viele Iterationen vor einer Aktion!",
|
|
if: "wenn",
|
|
do: "mache",
|
|
else: "sonst",
|
|
submitProgram: "Programm überprüfen lassen",
|
|
runProgram: "Programm ausführen",
|
|
stopProgram: "Stop",
|
|
speed: "Ablaufgeschwindigkeit:",
|
|
slowSpeed: "Langsam",
|
|
mediumSpeed: "Mittel",
|
|
fastSpeed: "Schnell",
|
|
ludicrousSpeed: "Sehr schnell",
|
|
selectLanguage: "Sprache:",
|
|
blocklyLanguage: "Blockly",
|
|
javascriptLanguage: "Javascript",
|
|
importFromBlockly: "Generiere von Blockly-Blöcken",
|
|
saveOrLoadProgram: "Speicher oder lade deinen Quelltext:",
|
|
avoidReloadingOtherTask: "Warnung: Lade keinen Quelltext von einer anderen Aufgabe!",
|
|
reloadProgram: "Laden:",
|
|
saveProgram: "Speichern",
|
|
limitBlocks1: " Blöcke von ",
|
|
limitBlocks2: " noch verfügbar."
|
|
}
|
|
},
|
|
loadHtml: function(nbTestCases) {
|
|
var strMaxBlocks = "";
|
|
if (maxBlocks != undefined) {
|
|
strMaxBlocks = "<span class='blocklyCapacity' style='display:inline-block;width:24px;text-align:right'>XXX</span>" + this.strings.limitBlocks1 + maxBlocks + this.strings.limitBlocks2;
|
|
}
|
|
$("#blocklyLibContent").html(
|
|
" <xml id='toolbox' style='display: none'>" +
|
|
" </xml>" +
|
|
" <div style='height: 40px;display:none' id='lang'>" +
|
|
" <p>" +
|
|
" " + this.strings.selectLanguage +
|
|
" <select id='selectLanguage' onchange='task.displayedSubTask.blocklyHelper.changeLanguage()'>" +
|
|
" <option value='blockly'>" + this.strings.blocklyLanguage + "</option>" +
|
|
" <option value='javascript'>" + this.strings.javascriptLanguage + "</option>" +
|
|
" </select>" +
|
|
" <input type='button' class='language_javascript' value='" + this.strings.importFromBlockly + "' style='display:none' onclick='task.displayedSubTask.blocklyHelper.importFromBlockly()' />" +
|
|
" </p>" +
|
|
" </div>" +
|
|
"<div style='clear:both;'>" + strMaxBlocks + "</div>" +
|
|
" <div id='blocklyContainer' style='resize:vertical;overflow:auto;height:600px;padding-bottom:10px;border: solid black 1px;width: 100%;position:relative'>" +
|
|
" <div id='blocklyDiv' class='language_blockly' style='height: 100%; width: 100%'></div>" +
|
|
" <textarea id='program' class='language_javascript' style='width:100%;height:100%;display:none'></textarea>" +
|
|
" </div>" +
|
|
" <div id='saveOrLoad'> "+
|
|
" <p><b>" + this.strings.saveOrLoadProgram + "</b></p>" +
|
|
" <p>" + this.strings.avoidReloadingOtherTask + "</p>" +
|
|
" <p>" + this.strings.reloadProgram + " <input type='file' id='input' onchange='task.displayedSubTask.blocklyHelper.handleFiles(this.files);resetFormElement($(\"#input\"))'></p>" +
|
|
" <p><input type='button' value='" + this.strings.saveProgram + "' onclick='task.displayedSubTask.blocklyHelper.saveProgram()' /><span id='saveUrl'></span>" +
|
|
" </div>"
|
|
);
|
|
|
|
var gridButtonsBefore = "";
|
|
if (nbTestCases > 1) {
|
|
gridButtonsBefore += "<div>" +
|
|
"<input type='button' value='Précédent' onclick='task.displayedSubTask.changeTest(-1)'/>" +
|
|
"<span id='testCaseName' style='padding-left: 20px; padding-right: 20px'>Test 1</span>" +
|
|
"<input type='button' value='Suivant' onclick='task.displayedSubTask.changeTest(1)'/>" +
|
|
"</div>";
|
|
}
|
|
$("#gridButtonsBefore").html(gridButtonsBefore);
|
|
|
|
|
|
var gridButtonsAfter =
|
|
this.strings.speed +
|
|
" <select id='selectSpeed' onchange='task.displayedSubTask.changeSpeed()'>" +
|
|
" <option value='200'>" + this.strings.slowSpeed + "</option>" +
|
|
" <option value='50'>" + this.strings.mediumSpeed + "</option>" +
|
|
" <option value='5'>" + this.strings.fastSpeed + "</option>" +
|
|
" <option value='0'>" + this.strings.ludicrousSpeed + "</option>" +
|
|
" </select> " +
|
|
"<input type='button' value='" + this.strings.stopProgram + "' onclick='task.displayedSubTask.stop()'/><br/><br/>";
|
|
|
|
if (nbTestCases > 1) {
|
|
gridButtonsAfter += "<input type='button' value='" + this.strings.runProgram + "' onclick='task.displayedSubTask.run()'/> ";
|
|
}
|
|
gridButtonsAfter += "<input type='button' value='" + this.strings.submitProgram + "' onclick='task.displayedSubTask.submit()' /><br/>" +
|
|
"<div id='errors' style='color:red;padding-top:10px;'></div>";
|
|
$("#gridButtonsAfter").html(gridButtonsAfter);
|
|
},
|
|
|
|
load: function(language, display, nbTestCases, options) {
|
|
if (language == undefined) {
|
|
language = "fr";
|
|
}
|
|
if (options == undefined) options = {};
|
|
if (!options.divId) options.divId = 'blocklyDiv';
|
|
this.strings = this.languageStrings[language];
|
|
|
|
this.createGeneratorsAndBlocks(this.generators);
|
|
|
|
if (display) {
|
|
this.loadHtml(nbTestCases);
|
|
var xml = this.getToolboxXml();
|
|
var wsConfig = {
|
|
toolbox: "<xml>"+xml+"</xml>",
|
|
sounds: false,
|
|
media: "http://static3.castor-informatique.fr/contestAssets/blockly/"
|
|
};
|
|
if (!this.groupByCategory) {
|
|
wsConfig.comments = true;
|
|
wsConfig.scrollbars = true;
|
|
wsConfig.trashcan = true;
|
|
}
|
|
if (maxBlocks != undefined) {
|
|
wsConfig.maxBlocks = maxBlocks;
|
|
}
|
|
if (options.readOnly) {
|
|
wsConfig.readOnly = true;
|
|
}
|
|
this.addExtraBlocks();
|
|
this.workspace = Blockly.inject(options.divId, wsConfig);
|
|
Blockly.Trashcan.prototype.MARGIN_SIDE_ = 410;
|
|
$(".blocklyToolboxDiv").css("background-color", "rgba(168, 168, 168, 0.5)");
|
|
var that = this;
|
|
function onchange(event) {
|
|
window.focus();
|
|
$('.blocklyCapacity').html(that.workspace.remainingCapacity());
|
|
}
|
|
this.workspace.addChangeListener(onchange);
|
|
onchange();
|
|
} else {
|
|
this.workspace = new Blockly.Workspace();
|
|
}
|
|
|
|
this.programs = [];
|
|
for (var iPlayer = this.mainContext.nbRobots - 1; iPlayer >= 0; iPlayer--) {
|
|
this.programs[iPlayer] = {blockly: null, blocklyJS: "", blocklyPython: "", javascript: ""};
|
|
this.languages[iPlayer] = "blockly";
|
|
this.setPlayer(iPlayer);
|
|
if(!options.noRobot) {
|
|
var xml = '<xml><block type="robot_start" deletable="false" movable="false"></block></xml>';
|
|
Blockly.Events.recordUndo = false;
|
|
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), this.workspace);
|
|
Blockly.Events.recordUndo = true;
|
|
}
|
|
this.savePrograms();
|
|
}
|
|
},
|
|
|
|
unload: function() {
|
|
//var ws = Blockly.getMainWorkspace('blocklyDiv');
|
|
var ws = this.workspace;
|
|
if (ws != null) {
|
|
ws.dispose();
|
|
// TODO: this should be in a global unload function
|
|
if (false) {
|
|
$(".blocklyWidgetDiv").remove();
|
|
$(".blocklyTooltipDiv").remove();
|
|
document.removeEventListener("keydown", Blockly.onKeyDown_); // TODO: find correct way to remove all event listeners
|
|
delete Blockly;
|
|
}
|
|
}
|
|
},
|
|
|
|
initXML: function() {
|
|
var categories = ["actions", "sensors", "debug"];
|
|
for (var iCategory = 0; iCategory < categories.length; iCategory++) {
|
|
var categoryStr = "";
|
|
for (var objectName in this.generators) {
|
|
for (var iGen = 0; iGen < this.generators[objectName].length; iGen++) {
|
|
var generator = this.generators[objectName][iGen];
|
|
if (generator.category == categories[iCategory]) {
|
|
categoryStr += "<block type='";
|
|
categoryStr += (objectName == '.') ? '' : objectName + "_";
|
|
categoryStr += generator.labelEn + "__'></block>";
|
|
}
|
|
}
|
|
}
|
|
$("#blockly_" + categories[iCategory]).html(categoryStr);
|
|
}
|
|
},
|
|
|
|
createSelection: function(id, start, end) {
|
|
var field = document.getElementById(id)
|
|
if (field.createTextRange) {
|
|
var selRange = field.createTextRange();
|
|
selRange.collapse(true);
|
|
selRange.moveStart('character', start);
|
|
selRange.moveEnd('character', end);
|
|
selRange.select();
|
|
} else if (field.setSelectionRange) {
|
|
field.setSelectionRange(start, end);
|
|
} else if (field.selectionStart) {
|
|
field.selectionStart = start;
|
|
field.selectionEnd = end;
|
|
}
|
|
field.focus();
|
|
},
|
|
|
|
showStep: function(interpreter, id) {
|
|
if (interpreter.stateStack[0]) {
|
|
var node = interpreter.stateStack[0].node;
|
|
var start = node.start;
|
|
var end = node.end;
|
|
} else {
|
|
var start = 0;
|
|
var end = 0;
|
|
}
|
|
this.createSelection(id, start, end);
|
|
},
|
|
|
|
setPlayer: function(newPlayer) {
|
|
this.player = newPlayer;
|
|
$("#selectPlayer").val(this.player);
|
|
$(".robot0, .robot1").hide();
|
|
$(".robot" + this.player).show();
|
|
},
|
|
|
|
changePlayer: function() {
|
|
this.loadPlayer($("#selectPlayer").val());
|
|
},
|
|
|
|
loadPlayer: function(player) {
|
|
this.savePrograms();
|
|
this.player = player;
|
|
for (var iRobot = 0; iRobot < this.mainContext.nbRobots; iRobot++) {
|
|
$(".robot" + iRobot).hide();
|
|
}
|
|
$(".robot" + this.player).show();
|
|
|
|
$(".language_blockly, .language_javascript").hide();
|
|
$(".language_" + this.languages[this.player]).show();
|
|
|
|
var blocklyElems = $(".blocklyToolboxDiv, .blocklyWidgetDiv");
|
|
$("#selectLanguage").val(this.languages[this.player]);
|
|
if (this.languages[this.player] == "blockly") {
|
|
blocklyElems.show();
|
|
} else {
|
|
blocklyElems.hide();
|
|
$(".blocklyTooltipDiv").hide();
|
|
}
|
|
this.loadPrograms();
|
|
},
|
|
|
|
getCodeFromXml: function(xmlText, language) {
|
|
try {
|
|
var xml = Blockly.Xml.textToDom(xmlText)
|
|
} catch (e) {
|
|
alert(e);
|
|
return;
|
|
}
|
|
var tmpWorkspace = new Blockly.Workspace();
|
|
Blockly.Xml.domToWorkspace(xml, tmpWorkspace);
|
|
return this.getCode(language, tmpWorkspace);
|
|
},
|
|
|
|
|
|
getCode: function(language, codeWorkspace) {
|
|
if (codeWorkspace == undefined) {
|
|
codeWorkspace = this.workspace;
|
|
}
|
|
blocks = codeWorkspace.getTopBlocks(true);
|
|
var languageObj = null;
|
|
if (language == "javascript") {
|
|
languageObj = Blockly.JavaScript;
|
|
}
|
|
if (language == "python") {
|
|
languageObj = Blockly.Python;
|
|
}
|
|
languageObj.init(codeWorkspace);
|
|
|
|
var code = [];
|
|
var comments = [];
|
|
for (var b = 0; b < blocks.length; b++) {
|
|
var block = blocks[b];
|
|
var blockCode = languageObj.blockToCode(block);
|
|
if (["procedures_defnoreturn", "procedures_defreturn"].indexOf(block.type) > -1) {
|
|
// For function blocks, the code is stored in languageObj.definitions_
|
|
} else {
|
|
if (block.type == "robot_start") {
|
|
comments.push(blockCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var def in languageObj.definitions_) {
|
|
code.push(languageObj.definitions_[def]);
|
|
}
|
|
|
|
var code = code.join("\n");
|
|
code += comments.join("\n");
|
|
return code;
|
|
},
|
|
|
|
savePrograms: function() {
|
|
this.programs[this.player].javascript = $("#program").val();
|
|
if (this.workspace != null) {
|
|
var xml = Blockly.Xml.workspaceToDom(this.workspace);
|
|
this.programs[this.player].blockly = Blockly.Xml.domToText(xml);
|
|
this.programs[this.player].blocklyJS = this.getCode("javascript");
|
|
this.programs[this.player].blocklyPython = this.getCode("python");
|
|
}
|
|
},
|
|
|
|
loadPrograms: function() {
|
|
$("#program").val(this.programs[this.player].javascript);
|
|
if (this.workspace != null) {
|
|
var xml = Blockly.Xml.textToDom(this.programs[this.player].blockly);
|
|
this.workspace.clear();
|
|
Blockly.Xml.domToWorkspace(xml, this.workspace);
|
|
}
|
|
},
|
|
|
|
changeLanguage: function() {
|
|
this.languages[this.player] = $("#selectLanguage").val();
|
|
this.loadPlayer(this.player);
|
|
},
|
|
|
|
importFromBlockly: function() {
|
|
//var player = $("#selectPlayer").val();
|
|
var player = 0;
|
|
this.programs[player].javascript = this.getCode("javascript");
|
|
$("#program").val(this.programs[player].javascript);
|
|
},
|
|
|
|
handleFiles: function(files) {
|
|
var that = this;
|
|
if (files.length < 0) {
|
|
return;
|
|
}
|
|
var file = files[0];
|
|
var textType = /text.*/;
|
|
if (file.type.match(textType)) {
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = function(e) {
|
|
var code = reader.result;
|
|
if (code[0] == "<") {
|
|
try {
|
|
var xml = Blockly.Xml.textToDom(code);
|
|
that.programs[that.player].blockly = code;
|
|
} catch(e) {
|
|
if(window.quickAlgoInterface) {
|
|
window.quickAlgoInterface.displayError(that.strings.invalidContent);
|
|
} else {
|
|
$("#errors").html(that.strings.invalidContent);
|
|
}
|
|
}
|
|
that.languages[that.player] = "blockly";
|
|
} else {
|
|
that.programs[that.player].javascript = code;
|
|
that.languages[that.player] = "javascript";
|
|
}
|
|
that.loadPrograms();
|
|
that.loadPlayer(that.player);
|
|
}
|
|
|
|
reader.readAsText(file);
|
|
} else {
|
|
if(window.quickAlgoInterface) {
|
|
window.quickAlgoInterface.displayError(that.strings.unknownFileType);
|
|
} else {
|
|
$("#errors").html(that.strings.unknownFileType);
|
|
}
|
|
}
|
|
},
|
|
|
|
saveProgram: function() {
|
|
this.savePrograms();
|
|
var code = this.programs[this.player][this.languages[this.player]];
|
|
var data = new Blob([code], {type: 'text/plain'});
|
|
|
|
// If we are replacing a previously generated file we need to
|
|
// manually revoke the object URL to avoid memory leaks.
|
|
if (this.textFile !== null) {
|
|
window.URL.revokeObjectURL(this.textFile);
|
|
}
|
|
|
|
this.textFile = window.URL.createObjectURL(data);
|
|
|
|
// returns a URL you can use as a href
|
|
$("#saveUrl").html(" <a href='" + this.textFile + "' download='robot_" + this.languages[this.player] + "_program.txt'>" + this.strings.download + "</a>");
|
|
return this.textFile;
|
|
},
|
|
|
|
toggleSize: function() {
|
|
if (!this.extended) {
|
|
this.extended = true;
|
|
$("#blocklyContainer").css("width", "800px");
|
|
$("#extendButton").val("<<");
|
|
} else {
|
|
this.extended = false;
|
|
$("#blocklyContainer").css("width", "500px");
|
|
$("#extendButton").val(">>");
|
|
}
|
|
this.updateSize();
|
|
},
|
|
|
|
updateSize: function() {
|
|
var panelWidth = 500;
|
|
if (this.languages[this.player] == "blockly") {
|
|
panelWidth = $("#blocklyDiv").width() - 10;
|
|
} else {
|
|
panelWidth = $("#program").width() + 20;
|
|
}
|
|
if (panelWidth != this.prevWidth) {
|
|
$("#taskIntro").css("width", panelWidth);
|
|
$("#grid").css("left", panelWidth + 20 + "px");
|
|
if (this.languages[this.player] == "blockly") {
|
|
Blockly.Trashcan.prototype.MARGIN_SIDE_ = panelWidth - 90;
|
|
Blockly.svgResize(this.workspace);
|
|
}
|
|
}
|
|
this.prevWidth = panelWidth;
|
|
},
|
|
|
|
createGenerator: function(label, code, type, nbParams) {
|
|
var jsDefinitions = this.definitions['javascript'] ? this.definitions['javascript'] : [];
|
|
var pyDefinitions = this.definitions['python'] ? this.definitions['python'] : [];
|
|
Blockly.JavaScript[label] = function(block) {
|
|
for (var iDef=0; iDef < jsDefinitions.length; iDef++) {
|
|
var def = jsDefinitions[iDef];
|
|
Blockly.Javascript.definitions_[def.label] = def.code;
|
|
}
|
|
var params = "";
|
|
for (var iParam = 0; iParam < nbParams; iParam++) {
|
|
if (iParam != 0) {
|
|
params += ", ";
|
|
}
|
|
params += Blockly.JavaScript.valueToCode(block, 'NAME_' + (iParam + 1), Blockly.JavaScript.ORDER_ATOMIC);
|
|
}
|
|
if (type == 0) {
|
|
return code + "(" + params + ");\n";
|
|
} else if (type == 1){
|
|
return [code + "(" + params + ")", Blockly.JavaScript.ORDER_NONE];
|
|
}
|
|
};
|
|
Blockly.Python[label] = function(block) {
|
|
for (var iDef=0; iDef < pyDefinitions.length; iDef++) {
|
|
var def = pyDefinitions[iDef];
|
|
Blockly.Python.definitions_[def.label] = def.code;
|
|
}
|
|
var params = "";
|
|
for (var iParam = 0; iParam < nbParams; iParam++) {
|
|
if (iParam != 0) {
|
|
params += ", ";
|
|
}
|
|
params += Blockly.Python.valueToCode(block, 'NAME_' + (iParam + 1), Blockly.Python.ORDER_ATOMIC);
|
|
}
|
|
if (type == 0) {
|
|
return code + "(" + params + ")\n";
|
|
} else if (type == 1) {
|
|
return [code + "(" + params + ")", Blockly.Python.ORDER_NONE];
|
|
}
|
|
};
|
|
},
|
|
|
|
createBlock: function(label, code, type, nbParams) {
|
|
Blockly.Blocks[label] = {
|
|
init: function() {
|
|
this.appendDummyInput()
|
|
.appendField(code);
|
|
if (type == 0) {
|
|
this.setPreviousStatement(true);
|
|
this.setNextStatement(true);
|
|
}
|
|
if (type == 1) {
|
|
this.setOutput(true);
|
|
}
|
|
this.setInputsInline(true);
|
|
for (var iParam = 0; iParam < nbParams; iParam++) {
|
|
this.appendValueInput("NAME_" + (iParam + 1)).setCheck(null);
|
|
}
|
|
this.setColour(210);
|
|
this.setTooltip('');
|
|
this.setHelpUrl('');
|
|
}
|
|
};
|
|
},
|
|
|
|
createGeneratorsAndBlocks: function(generators) {
|
|
for (var objectName in generators) {
|
|
for (var iGen = 0; iGen < generators[objectName].length; iGen++) {
|
|
var generator = generators[objectName][iGen];
|
|
if(objectName == '.') {
|
|
var label = generator.labelEn + "__";
|
|
var code = generator.codeFr;
|
|
} else {
|
|
var label = objectName + "_" + generator.labelEn + "__";
|
|
var code = objectName + "." + generator.codeFr;
|
|
}
|
|
this.createGenerator(label, code, generator.type, generator.nbParams);
|
|
this.createBlock(label, generator.labelFr, generator.type, generator.nbParams);
|
|
}
|
|
}
|
|
},
|
|
|
|
getBlocklyLibCode: function(generators) {
|
|
var strCode = "";
|
|
for (var objectName in generators) {
|
|
strCode += "var " + objectName + " = {\n";
|
|
for (var iGen = 0; iGen < generators[objectName].length; iGen++) {
|
|
var generator = generators[objectName][iGen];
|
|
|
|
if (generator.nbParams == 0) {
|
|
strCode += generator.codeFr + ": function() { ";
|
|
} else {
|
|
strCode += generator.codeFr + ": function(param1) { ";
|
|
}
|
|
if (generator.type == 1) {
|
|
strCode += "return ";
|
|
}
|
|
if (generator.nbParams == 0) {
|
|
strCode += objectName + "_" + generator.labelEn + "(); }";
|
|
} else {
|
|
strCode += objectName + "_" + generator.labelEn + "(param1); }";
|
|
}
|
|
if (iGen < generators[objectName].length - 1) {
|
|
strCode += ",";
|
|
}
|
|
strCode += "\n";
|
|
}
|
|
strCode += "};\n\n";
|
|
}
|
|
strCode += "Math['max'] = function(a, b) { if (a > b) return a; return b; };\n";
|
|
strCode += "Math['min'] = function(a, b) { if (a > b) return b; return a; };\n";
|
|
return strCode;
|
|
},
|
|
|
|
getStdBlocks: function() {
|
|
return [
|
|
{
|
|
category: "input",
|
|
name: this.strings.inputs,
|
|
colour: 345,
|
|
blocks: [
|
|
{
|
|
name: "input_num",
|
|
xml: "<block type='input_num'></block>"
|
|
},
|
|
{
|
|
name: "input_char",
|
|
xml: "<block type='input_char'></block>"
|
|
},
|
|
{
|
|
name: "input_word",
|
|
xml: "<block type='input_word'></block>"
|
|
},
|
|
{
|
|
name: "input_line",
|
|
xml: "<block type='input_line'></block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "logic",
|
|
name: this.strings.logic,
|
|
colour: 65,
|
|
blocks: [
|
|
{
|
|
name: "controls_if",
|
|
xml: "<block type='controls_if' colour='65'></block>"
|
|
},
|
|
{
|
|
name: "controls_if_else",
|
|
xml: "<block type='controls_if_else' colour='65'></block>"
|
|
},
|
|
{
|
|
name: "logic_compare",
|
|
xml: "<block type='logic_compare'></block>"
|
|
},
|
|
{
|
|
name: "logic_operation",
|
|
xml: "<block type='logic_operation'></block>"
|
|
},
|
|
{
|
|
name: "logic_negate",
|
|
xml: "<block type='logic_negate'></block>"
|
|
},
|
|
{
|
|
name: "logic_boolean",
|
|
xml: "<block type='logic_boolean'></block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "loops",
|
|
name: this.strings.loops,
|
|
colour: 120,
|
|
blocks: [
|
|
{
|
|
name: "controls_repeat",
|
|
xml: "<block type='controls_repeat'></block>"
|
|
},
|
|
{
|
|
name: "controls_repeat_ext",
|
|
xml: "<block type='controls_repeat_ext'><value name='TIMES'><block type='math_number'><field name='NUM'>10</field></block></value></block>"
|
|
},
|
|
{
|
|
name: "controls_whileUntil",
|
|
xml: "<block type='controls_whileUntil'></block>"
|
|
},
|
|
{
|
|
name: "controls_for",
|
|
xml: "<block type='controls_for'>" +
|
|
" <value name='FROM'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>1</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='TO'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>10</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='BY'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>1</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
},
|
|
{
|
|
name: "controls_forEach",
|
|
excludedByDefault: true,
|
|
xml: "<block type='controls_forEach'></block>"
|
|
},
|
|
{
|
|
name: "controls_flow_statements",
|
|
xml: "<block type='controls_flow_statements'></block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "math",
|
|
name: this.strings.math,
|
|
colour: 230,
|
|
blocks: [
|
|
{
|
|
name: "math_number",
|
|
xml: "<block type='math_number' gap='32'></block>"
|
|
},
|
|
{
|
|
name: "math_arithmetic",
|
|
xml: "<block type='math_arithmetic'>" +
|
|
" <value name='A'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>1</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='B'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>1</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
},
|
|
{
|
|
name: "math_number_property",
|
|
xml: "<block type='math_number_property'>" +
|
|
" <value name='NUMBER_TO_CHECK'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>0</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
},
|
|
{
|
|
name: "math_change",
|
|
xml: "<block type='math_change'>" +
|
|
" <value name='DELTA'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>1</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
},
|
|
{
|
|
name: "math_round",
|
|
xml: "<block type='math_round'>" +
|
|
" <value name='NUM'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>3.1</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
},
|
|
{
|
|
name: "math_extra_single",
|
|
xml: "<block type='math_extra_single'>" +
|
|
" <value name='NUM'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>3.1</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
},
|
|
{
|
|
name: "math_extra_double",
|
|
xml: "<block type='math_extra_double'>" +
|
|
" <value name='A'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='A'>2</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='B'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='B'>2</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
},
|
|
{
|
|
name: "math_modulo",
|
|
xml: "<block type='math_modulo'>" +
|
|
" <value name='DIVIDEND'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>64</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='DIVISOR'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>10</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" </block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "text",
|
|
name: this.strings.text,
|
|
colour: 210,
|
|
blocks: [
|
|
{
|
|
name: "text",
|
|
xml: "<block type='text'></block>"
|
|
},
|
|
{
|
|
name: "text_join",
|
|
xml: "<block type='text_join'></block>"
|
|
},
|
|
{
|
|
name: "text_append",
|
|
xml: "<block type='text_append'></block>"
|
|
},
|
|
{
|
|
name: "text_length",
|
|
xml: "<block type='text_length'>" +
|
|
" <value name='VALUE'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'>abc</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_isEmpty",
|
|
xml: "<block type='text_isEmpty'>" +
|
|
" <value name='VALUE'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'></field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_indexOf",
|
|
xml: "<block type='text_indexOf'>" +
|
|
" <value name='VALUE'>" +
|
|
" <block type='variables_get'>" +
|
|
" <field name='VAR'>{textVariable}</field>" +
|
|
" </block>" +
|
|
" </value>" +
|
|
" <value name='FIND'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'>abc</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_charAt",
|
|
xml: "<block type='text_charAt'>" +
|
|
" <value name='VALUE'>" +
|
|
" <block type='variables_get'>" +
|
|
" <field name='VAR'>{textVariable}</field>" +
|
|
" </block>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_getSubstring",
|
|
xml: "<block type='text_getSubstring'>" +
|
|
" <value name='STRING'>" +
|
|
" <block type='variables_get'>" +
|
|
" <field name='VAR'>{textVariable}</field>" +
|
|
" </block>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_changeCase",
|
|
xml: "<block type='text_changeCase'>" +
|
|
" <value name='TEXT'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'>abc</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_trim",
|
|
xml: "<block type='text_trim'>" +
|
|
" <value name='TEXT'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'>abc</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_print",
|
|
xml: "<block type='text_print'>" +
|
|
" <value name='TEXT'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'>abc</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "text_print_noend",
|
|
xml: "<block type='text_print_noend'></block>"
|
|
},
|
|
{
|
|
name: "text_prompt_ext",
|
|
xml: "<block type='text_prompt_ext'>" +
|
|
" <value name='TEXT'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'>abc</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "lists",
|
|
name: this.strings.lists,
|
|
colour: 260,
|
|
blocks: [
|
|
{
|
|
name: "lists_create_with_empty",
|
|
xml: "<block type='lists_create_with'>" +
|
|
" <mutation items='0'></mutation>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "lists_create_with",
|
|
xml: "<block type='lists_create_with'></block>"
|
|
},
|
|
{
|
|
name: "lists_repeat",
|
|
xml: "<block type='lists_repeat'>" +
|
|
" <value name='NUM'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>5</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "lists_length",
|
|
xml: "<block type='lists_length'></block>"
|
|
},
|
|
{
|
|
name: "lists_isEmpty",
|
|
xml: "<block type='lists_isEmpty'></block>"
|
|
},
|
|
{
|
|
name: "lists_indexOf",
|
|
xml: "<block type='lists_indexOf'>" +
|
|
" <value name='VALUE'>" +
|
|
" <block type='variables_get'>" +
|
|
" <field name='VAR'>{listVariable}</field>" +
|
|
" </block>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "lists_getIndex",
|
|
xml: "<block type='lists_getIndex'>" +
|
|
" <value name='VALUE'>" +
|
|
" <block type='variables_get'>" +
|
|
" <field name='VAR'>{listVariable}</field>" +
|
|
" </block>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "lists_setIndex",
|
|
xml: "<block type='lists_setIndex'>" +
|
|
" <value name='LIST'>" +
|
|
" <block type='variables_get'>" +
|
|
" <field name='VAR'>{listVariable}</field>" +
|
|
" </block>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "lists_getSublist",
|
|
xml: "<block type='lists_getSublist'>" +
|
|
" <value name='LIST'>" +
|
|
" <block type='variables_get'>" +
|
|
" <field name='VAR'>{listVariable}</field>" +
|
|
" </block>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "lists_sort",
|
|
xml: "<block type='lists_sort'></block>"
|
|
},
|
|
{
|
|
name: "lists_split",
|
|
xml: "<block type='lists_split'>" +
|
|
" <value name='DELIM'>" +
|
|
" <shadow type='text'>" +
|
|
" <field name='TEXT'>,</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "lists_append",
|
|
xml: "<block type='lists_append'></block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "colour",
|
|
name: this.strings.colour,
|
|
colour: 210,
|
|
blocks: [
|
|
{
|
|
name: "colour_picker",
|
|
xml: "<block type='colour_picker'></block>"
|
|
},
|
|
{
|
|
name: "colour_random",
|
|
xml: "<block type='colour_random'></block>"
|
|
},
|
|
{
|
|
name: "colour_rgb",
|
|
xml: "<block type='colour_rgb'>" +
|
|
" <value name='RED'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>100</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='GREEN'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>50</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='BLUE'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>0</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
},
|
|
{
|
|
name: "colour_blend",
|
|
xml: "<block type='colour_blend'>" +
|
|
" <value name='COLOUR1'>" +
|
|
" <shadow type='colour_picker'>" +
|
|
" <field name='COLOUR'>#ff0000</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='COLOUR2'>" +
|
|
" <shadow type='colour_picker'>" +
|
|
" <field name='COLOUR'>#3333ff</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
" <value name='RATIO'>" +
|
|
" <shadow type='math_number'>" +
|
|
" <field name='NUM'>0.5</field>" +
|
|
" </shadow>" +
|
|
" </value>" +
|
|
"</block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "dicsts",
|
|
name: this.strings.dicts,
|
|
colour: 0,
|
|
blocks: [
|
|
{
|
|
name: "dict_get_literal",
|
|
xml: "<block type='dict_get_literal'></block>"
|
|
},
|
|
{
|
|
name: "dict_keys",
|
|
xml: "<block type='dict_keys'></block>"
|
|
},
|
|
{
|
|
name: "dicts_create_with",
|
|
xml: "<block type='dicts_create_with'></block>"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
category: "variables",
|
|
name: this.strings.variables,
|
|
colour: 330,
|
|
custom: "VARIABLE",
|
|
blocks: []
|
|
},
|
|
{
|
|
category: "functions",
|
|
name: this.strings.functions,
|
|
colour: 290,
|
|
custom: "PROCEDURE",
|
|
blocks: []
|
|
}
|
|
];
|
|
},
|
|
|
|
getToolboxXml: function() {
|
|
var blocksByCategory = {
|
|
}
|
|
for (var objectName in this.generators) {
|
|
for (var iGen = 0; iGen < this.generators[objectName].length; iGen++) {
|
|
var generator = this.generators[objectName][iGen];
|
|
if (blocksByCategory[generator.category] == undefined) {
|
|
blocksByCategory[generator.category] = [];
|
|
}
|
|
var blockName = (objectName == '.') ? generator.labelEn + "__" : objectName + "_" + generator.labelEn + "__";
|
|
blocksByCategory[generator.category].push(blockName);
|
|
}
|
|
}
|
|
xml = "";
|
|
for (var category in blocksByCategory) {
|
|
if (this.groupByCategory) {
|
|
xml += "<category name='" + this.strings[category] + "' colour='210'>";
|
|
}
|
|
var blocks = blocksByCategory[category];
|
|
for (var iBlock = 0; iBlock < blocks.length; iBlock++) {
|
|
xml += "<block type='" + blocks[iBlock] + "'></block>";
|
|
}
|
|
if (this.groupByCategory) {
|
|
xml += "</category>";
|
|
}
|
|
}
|
|
var stdBlocks = this.getStdBlocks();
|
|
for (var iCategory = 0; iCategory < stdBlocks.length; iCategory++) {
|
|
var category = stdBlocks[iCategory];
|
|
var catXml = "";
|
|
if (this.groupByCategory) {
|
|
catXml = "<category name='" + category.name + "' colour='" + category.colour + "'";
|
|
}
|
|
var isIncluded = false;
|
|
if (category.custom != undefined) {
|
|
catXml += " custom='" + category.custom + "'";
|
|
if (this.includedAll || ($.inArray(category.category, this.includedCategories) != -1)) {
|
|
isIncluded = true;
|
|
}
|
|
}
|
|
catXml += ">";
|
|
for (var iBlock = 0; iBlock < category.blocks.length; iBlock++) {
|
|
var block = category.blocks[iBlock];
|
|
if (this.includedAll ||
|
|
($.inArray(category.category, this.includedCategories) != -1) ||
|
|
($.inArray(block.name, this.includedBlocks) != -1)) {
|
|
if (!block.excludedByDefault || ($.inArray(block.name, this.includedBlocks) != -1)) {
|
|
catXml += block.xml;
|
|
isIncluded = true;
|
|
}
|
|
}
|
|
}
|
|
if (this.groupByCategory) {
|
|
catXml += "</category>";
|
|
}
|
|
if (isIncluded) {
|
|
xml += catXml;
|
|
}
|
|
}
|
|
|
|
// Handle variable blocks, which are normally automatically added with
|
|
// the VARIABLES category but can be customized here
|
|
if (this.availableVariables.length > 0 ||
|
|
($.inArray('variables_get', this.includedBlocks) != -1) ||
|
|
($.inArray('variables_set', this.includedBlocks) != -1)) {
|
|
if (this.groupByCategory) {
|
|
xml += "<category name='" + this.strings.variables + "' colour='330'>";
|
|
}
|
|
|
|
// block for each availableVariable
|
|
for (var iVar = 0; iVar < this.availableVariables.length; iVar++) {
|
|
xml += "<block type='variables_get' editable='false'><field name='VAR'>" + this.availableVariables[iVar] + "</field></block>";
|
|
}
|
|
// generic modifyable block
|
|
if ($.inArray('variables_get', this.includedBlocks) != -1) {
|
|
xml += "<block type='variables_get'></block>"
|
|
}
|
|
|
|
// same for setting variables
|
|
for (var iVar = 0; iVar < this.availableVariables.length; iVar++) {
|
|
xml += "<block type='variables_set' editable='false'><field name='VAR'>" + this.availableVariables[iVar] + "</field></block>";
|
|
}
|
|
if ($.inArray('variables_set', this.includedBlocks) != -1) {
|
|
xml += "<block type='variables_set'></block>"
|
|
}
|
|
|
|
if (this.groupByCategory) {
|
|
xml += "</category>";
|
|
}
|
|
}
|
|
return xml;
|
|
},
|
|
|
|
addExtraBlocks: function() {
|
|
var that = this;
|
|
Blockly.Blocks['math_extra_single'] = {
|
|
/**
|
|
* Block for advanced math operators with single operand.
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
var OPERATORS =
|
|
[
|
|
[Blockly.Msg.MATH_SINGLE_OP_ABSOLUTE, 'ABS'],
|
|
['-', 'NEG']
|
|
];
|
|
this.setHelpUrl(Blockly.Msg.MATH_SINGLE_HELPURL);
|
|
this.setColour(Blockly.Blocks.math.HUE);
|
|
this.setOutput(true, 'Number');
|
|
this.appendValueInput('NUM')
|
|
.setCheck('Number')
|
|
.appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
var mode = thisBlock.getFieldValue('OP');
|
|
var TOOLTIPS = {
|
|
'ABS': Blockly.Msg.MATH_SINGLE_TOOLTIP_ABS,
|
|
'NEG': Blockly.Msg.MATH_SINGLE_TOOLTIP_NEG
|
|
};
|
|
return TOOLTIPS[mode];
|
|
});
|
|
}
|
|
};
|
|
|
|
Blockly.JavaScript['math_extra_single'] = Blockly.JavaScript['math_single'];
|
|
Blockly.Python['math_extra_single'] = Blockly.Python['math_single'];
|
|
|
|
|
|
Blockly.Blocks['math_extra_double'] = {
|
|
/**
|
|
* Block for advanced math operators with double operand.
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
var OPERATORS =
|
|
[
|
|
['min', 'MIN'],
|
|
['max', 'MAX']
|
|
];
|
|
this.setColour(Blockly.Blocks.math.HUE);
|
|
this.setInputsInline(true);
|
|
this.setOutput(true, 'Number');
|
|
this.appendDummyInput('OP').appendField(new Blockly.FieldDropdown([["min", "MIN"], ["max", "MAX"], ["", ""]]), "OP");
|
|
this.appendDummyInput().appendField(" entre ");
|
|
this.appendValueInput('A').setCheck('Number');
|
|
this.appendDummyInput().appendField(" et ");
|
|
this.appendValueInput('B').setCheck('Number');
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
var mode = thisBlock.getFieldValue('OP');
|
|
var TOOLTIPS = {
|
|
'MIN': that.strings.smallestOfTwoNumbers,
|
|
'MAX': that.strings.greatestOfTwoNumbers
|
|
};
|
|
return TOOLTIPS[mode];
|
|
});
|
|
}
|
|
};
|
|
|
|
Blockly.JavaScript['math_extra_double'] = function(block) {
|
|
// Math operators with double operand.
|
|
var operator = block.getFieldValue('OP');
|
|
var arg1 = Blockly.JavaScript.valueToCode(block, 'A', Blockly.JavaScript.ORDER_NONE) || '0';
|
|
var arg2 = Blockly.JavaScript.valueToCode(block, 'B', Blockly.JavaScript.ORDER_NONE) || '0';
|
|
if (operator == 'MIN') {
|
|
var code = "Math.min(" + arg1 + ", " + arg2 + ")";
|
|
}
|
|
if (operator == 'MAX') {
|
|
var code = "Math.max(" + arg1 + ", " + arg2 + ")";
|
|
}
|
|
return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
|
|
};
|
|
|
|
Blockly.Python['math_extra_double'] = function(block) {
|
|
// Math operators with double operand.
|
|
var operator = block.getFieldValue('OP');
|
|
var arg1 = Blockly.Python.valueToCode(block, 'A', Blockly.Python.ORDER_NONE) || '0';
|
|
var arg2 = Blockly.Python.valueToCode(block, 'B', Blockly.Python.ORDER_NONE) || '0';
|
|
if (operator == 'MIN') {
|
|
var code = "Math.min(" + arg1 + ", " + arg2 + ")";
|
|
}
|
|
if (operator == 'MAX') {
|
|
var code = "Math.max(" + arg1 + ", " + arg2 + ")";
|
|
}
|
|
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
|
|
};
|
|
|
|
|
|
var old = Blockly.Blocks.controls_if.init;
|
|
Blockly.Blocks.controls_if.init = function() {
|
|
old.call(this);
|
|
this.setMutator(undefined)
|
|
};
|
|
|
|
Blockly.Blocks['controls_if_else'] = {
|
|
init: function() {
|
|
this.appendValueInput("IF")
|
|
.setCheck("Boolean")
|
|
.appendField(that.strings.if);
|
|
this.appendDummyInput()
|
|
.appendField(that.strings.do);
|
|
this.appendStatementInput("DO");
|
|
this.appendDummyInput()
|
|
.appendField(that.strings.else);
|
|
this.appendStatementInput("ELSE");
|
|
this.setInputsInline(false);
|
|
this.setPreviousStatement(true);
|
|
this.setNextStatement(true);
|
|
this.setColour(65);
|
|
this.setTooltip('');
|
|
// this.setHelpUrl('http://www.example.com/');
|
|
}
|
|
};
|
|
|
|
Blockly.JavaScript['controls_if_else'] = function(block) {
|
|
var condition = Blockly.JavaScript.valueToCode(block, 'IF', Blockly.JavaScript.ORDER_NONE) || 'false';
|
|
var stmtIf = Blockly.JavaScript.statementToCode(block, 'DO');
|
|
var stmtElse = Blockly.JavaScript.statementToCode(block, 'ELSE');
|
|
var code = "if (" + condition + ") {\n" + stmtIf + "} else {\n" + stmtElse + "}\n";
|
|
return code;
|
|
};
|
|
|
|
Blockly.Python['controls_if_else'] = function(block) {
|
|
var condition = Blockly.Python.valueToCode(block, 'IF', Blockly.Python.ORDER_NONE) || 'false';
|
|
var stmtIf = Blockly.Python.statementToCode(block, 'DO');
|
|
var stmtElse = Blockly.Python.statementToCode(block, 'ELSE');
|
|
var code = "if (" + condition + "):\n" + stmtIf + "else:\n" + stmtElse + "\n";
|
|
return code;
|
|
};
|
|
|
|
|
|
Blockly.Blocks['robot_start'] = {
|
|
init: function() {
|
|
this.appendDummyInput()
|
|
.appendField(that.strings.programOfRobot);
|
|
this.setNextStatement(true);
|
|
this.setColour(210);
|
|
this.setTooltip('');
|
|
this.deletable_ = false;
|
|
this.editable_ = false;
|
|
this.movable_ = false;
|
|
// this.setHelpUrl('http://www.example.com/');
|
|
}
|
|
};
|
|
|
|
Blockly.JavaScript['robot_start'] = function(block) {
|
|
return "";
|
|
};
|
|
|
|
Blockly.Python['robot_start'] = function(block) {
|
|
return "";
|
|
};
|
|
},
|
|
|
|
run: function() {
|
|
var that = this;
|
|
var nbRunning = this.mainContext.runner.nbRunning();
|
|
if (nbRunning > 0) {
|
|
this.mainContext.runner.stop();
|
|
this.mainContext.delayFactory.createTimeout("run" + Math.random(), function() {
|
|
that.run()
|
|
}, 1000);
|
|
return;
|
|
}
|
|
if (this.mainContext.display) {
|
|
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
|
|
Blockly.JavaScript.addReservedWords('highlightBlock');
|
|
} else {
|
|
Blockly.JavaScript.STATEMENT_PREFIX = '';
|
|
}
|
|
this.savePrograms();
|
|
var codes = [];
|
|
for (var iRobot = 0; iRobot < this.mainContext.nbRobots; iRobot++) {
|
|
var language = this.languages[iRobot];
|
|
if (language == "blockly") {
|
|
language = "blocklyJS";
|
|
}
|
|
codes[iRobot] = this.getFullCode(this.programs[iRobot][language]);
|
|
}
|
|
that.highlightPause = false;
|
|
that.workspace.traceOn(true);
|
|
that.workspace.highlightBlock(null);
|
|
this.mainContext.runner.runCodes(codes);
|
|
},
|
|
|
|
getFullCode: function(code) {
|
|
return this.getBlocklyLibCode(this.generators) + code + "program_end()";
|
|
}
|
|
}
|
|
}
|
|
|
|
function initBlocklyRunner(context, messageCallback) {
|
|
init(context, [], [], [], false, {});
|
|
|
|
function init(context, interpreters, isRunning, toStop, stopPrograms, runner) {
|
|
runner.waitDelay = function(callback, value, delay) {
|
|
if (delay > 0) {
|
|
context.delayFactory.createTimeout("wait" + context.curRobot + "_" + Math.random(), function() {
|
|
runner.noDelay(callback, value);
|
|
},
|
|
delay
|
|
);
|
|
} else {
|
|
runner.noDelay(callback, value);
|
|
}
|
|
};
|
|
|
|
runner.noDelay = function(callback, value) {
|
|
var primitive = undefined;
|
|
if (value != undefined) {
|
|
primitive = interpreters[context.curRobot].createPrimitive(value);
|
|
}
|
|
if (Math.random() < 0.1) {
|
|
context.delayFactory.createTimeout("wait_" + Math.random(), function() {
|
|
callback(primitive);
|
|
runner.runSyncBlock();
|
|
}, 0);
|
|
} else {
|
|
callback(primitive);
|
|
runner.runSyncBlock();
|
|
}
|
|
};
|
|
|
|
runner.initInterpreter = function(interpreter, scope) {
|
|
for (var objectName in context.generators) {
|
|
for (var iGen = 0; iGen < context.generators[objectName].length; iGen++) {
|
|
var generator = context.generators[objectName][iGen];
|
|
interpreter.setProperty(scope, objectName + "_" + generator.labelEn, interpreter.createAsyncFunction(generator.fct));
|
|
}
|
|
}
|
|
interpreter.setProperty(scope, "program_end", interpreter.createAsyncFunction(context.program_end));
|
|
|
|
function highlightBlock(id) {
|
|
if (context.display) {
|
|
context.blocklyHelper.workspace.highlightBlock(id);
|
|
highlightPause = true;
|
|
}
|
|
}
|
|
|
|
// Add an API function for highlighting blocks.
|
|
var wrapper = function(id) {
|
|
id = id ? id.toString() : '';
|
|
return interpreter.createPrimitive(highlightBlock(id));
|
|
};
|
|
interpreter.setProperty(scope, 'highlightBlock', interpreter.createNativeFunction(wrapper));
|
|
};
|
|
|
|
runner.stop = function() {
|
|
for (var iInterpreter = 0; iInterpreter < interpreters.length; iInterpreter++) {
|
|
if (isRunning[iInterpreter]) {
|
|
toStop[iInterpreter] = true;
|
|
}
|
|
}
|
|
context.reset();
|
|
};
|
|
|
|
runner.runSyncBlock = function() {
|
|
var maxIter = 20000;
|
|
/* if (turn > 90) {
|
|
task.program_end(function() {
|
|
that.stop();
|
|
});
|
|
return;
|
|
}*/
|
|
try {
|
|
for (var iInterpreter = 0; iInterpreter < interpreters.length; iInterpreter++) {
|
|
context.curRobot = iInterpreter;
|
|
if (context.infos.checkEndEveryTurn) {
|
|
context.infos.checkEndCondition(context, false);
|
|
}
|
|
var interpreter = interpreters[iInterpreter];
|
|
while (context.curSteps[iInterpreter] < maxIter) {
|
|
if (!interpreter.step() || toStop[iInterpreter]) {
|
|
isRunning[iInterpreter] = false;;
|
|
break;
|
|
}
|
|
if (interpreter.paused_) {
|
|
break;
|
|
}
|
|
context.curSteps[iInterpreter]++;
|
|
}
|
|
if (context.curSteps[iInterpreter] >= maxIter) {
|
|
isRunning[iInterpreter] = false;;
|
|
throw context.blocklyHelper.strings.tooManyIterations;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
for (var iInterpreter = 0; iInterpreter < interpreters.length; iInterpreter++) {
|
|
isRunning[iInterpreter] = false;
|
|
}
|
|
|
|
var message = e.toString();
|
|
if ((context.nbTestCases != undefined) && (context.nbTestCases > 1)) {
|
|
if (context.success) {
|
|
message = context.messagePrefixSuccess + message;
|
|
} else {
|
|
message = context.messagePrefixFailure + message;
|
|
}
|
|
}
|
|
if (context.success) {
|
|
message = "<span style='color:green;font-weight:bold'>" + message + "</span>";
|
|
if (context.linkBack) {
|
|
//message += "<br/><span onclick='window.parent.backToList()' style='font-weight:bold;cursor:pointer;text-decoration:underline;color:blue'>Retour à la liste des questions</span>";
|
|
}
|
|
}
|
|
messageCallback(message);
|
|
}
|
|
};
|
|
|
|
runner.runCodes = function(codes) {
|
|
//this.mainContext.delayFactory.stopAll(); pb: it would top existing graders
|
|
interpreters = [];
|
|
context.programEnded = [];
|
|
context.curSteps = [];
|
|
context.reset();
|
|
for (var iInterpreter = 0; iInterpreter < codes.length; iInterpreter++) {
|
|
context.curSteps[iInterpreter] = 0;
|
|
context.programEnded[iInterpreter] = false;
|
|
interpreters.push(new Interpreter(codes[iInterpreter], runner.initInterpreter));
|
|
isRunning[iInterpreter] = true;
|
|
toStop[iInterpreter] = false;
|
|
}
|
|
runner.runSyncBlock();
|
|
};
|
|
|
|
runner.nbRunning = function() {
|
|
var nbRunning = 0;
|
|
for (var iInterpreter = 0; iInterpreter < interpreters.length; iInterpreter++) {
|
|
if (isRunning[iInterpreter]) {
|
|
nbRunning++;
|
|
}
|
|
}
|
|
return nbRunning;
|
|
};
|
|
|
|
context.runner = runner;
|
|
context.callCallback = runner.noDelay;
|
|
context.programEnded = [];
|
|
}
|
|
}
|
|
|
|
|
|
var initBlocklySubTask = function(subTask) {
|
|
subTask.blocklyHelper = getBlocklyHelper(subTask.gridInfos.maxInstructions);
|
|
subTask.answer = null;
|
|
subTask.state = {};
|
|
subTask.iTestCase = 0;
|
|
|
|
if (subTask.data["medium"] == undefined) {
|
|
subTask.load = function(views, callback) {
|
|
subTask.loadLevel("easy");
|
|
callback();
|
|
};
|
|
}
|
|
|
|
|
|
subTask.loadLevel = function(curLevel) {
|
|
this.level = curLevel;
|
|
|
|
// TODO: fix bebras platform to make this unnecessary
|
|
try {
|
|
$('#question-iframe', window.parent.document).css('width', '100%');
|
|
} catch(e) {
|
|
}
|
|
$('body').css('width', '100%').css('max-width', '1200px').css('margin', 'auto');
|
|
window.focus();
|
|
|
|
this.iTestCase = 0;
|
|
this.nbTestCases = subTask.data[curLevel].length;
|
|
if (this.display) {
|
|
var gridHtml = "<center>";
|
|
gridHtml += "<div id='gridButtonsBefore'></div>";
|
|
gridHtml += "<div id='grid' style='width:400px;height:200px;padding:10px'></div>";
|
|
gridHtml += "<div id='gridButtonsAfter'></div>";
|
|
gridHtml += "</center>";
|
|
$("#gridContainer").html(gridHtml)
|
|
if (this.gridInfos.hideSaveOrLoad) {
|
|
// TODO: do without a timeout
|
|
setTimeout(function() {
|
|
$("#saveOrLoad").hide();
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
var props = ["includedAll", "groupByCategory", "includedCategories", "includedBlocks", "availableVariables"];
|
|
for (var iProp = 0; iProp < props.length; iProp++) {
|
|
var prop = props[iProp];
|
|
if (subTask.gridInfos[prop] != undefined) {
|
|
var taskProp = subTask.gridInfos[prop];
|
|
if ((typeof taskProp == "object") && (taskProp["easy"] != undefined)) {
|
|
taskProp = taskProp[curLevel];
|
|
}
|
|
subTask.blocklyHelper[prop] = taskProp;
|
|
}
|
|
}
|
|
|
|
this.context = getRobotGridContext(this.display, this.gridInfos, curLevel);
|
|
this.context.raphaelFactory = this.raphaelFactory;
|
|
this.context.delayFactory = this.delayFactory;
|
|
this.context.blocklyHelper = this.blocklyHelper;
|
|
|
|
this.blocklyHelper.mainContext = this.context;
|
|
|
|
//this.answer = task.getDefaultAnswerObject();
|
|
displayHelper.hideValidateButton = true;
|
|
displayHelper.timeoutMinutes = 30;
|
|
|
|
this.blocklyHelper.generators = this.context.generators;
|
|
this.blocklyHelper.load(stringsLanguage, this.display, this.data[curLevel].length);
|
|
|
|
subTask.changeTest(0);
|
|
};
|
|
|
|
subTask.updateScale = function() {
|
|
this.context.updateScale();
|
|
this.blocklyHelper.updateSize();
|
|
};
|
|
|
|
var resetScores = function() {
|
|
};
|
|
|
|
var updateScores = function() {
|
|
};
|
|
|
|
function changeScore(robot, deltaScore) {
|
|
scores[robot] += deltaScore;
|
|
updateScores();
|
|
};
|
|
|
|
subTask.unloadLevel = function(callback) {
|
|
this.context.unload();
|
|
this.blocklyHelper.unload();
|
|
callback();
|
|
};
|
|
|
|
subTask.unload = subTask.unloadLevel;
|
|
|
|
subTask.reset = function() {
|
|
this.context.reset();
|
|
};
|
|
|
|
subTask.program_end = function(callback) {
|
|
this.context.program_end(callback);
|
|
};
|
|
|
|
var initContextForLevel = function(iTestCase) {
|
|
subTask.iTestCase = iTestCase;
|
|
subTask.context.reset(subTask.data[subTask.level][iTestCase]);
|
|
subTask.context.iTestCase = iTestCase;
|
|
subTask.context.nbTestCases = subTask.nbTestCases;
|
|
var prefix = "Test " + (subTask.iTestCase + 1) + "/" + subTask.nbTestCases + " : ";
|
|
subTask.context.messagePrefixFailure = prefix;
|
|
subTask.context.messagePrefixSuccess = prefix;
|
|
subTask.context.linkBack = false;
|
|
};
|
|
|
|
subTask.run = function() {
|
|
initBlocklyRunner(subTask.context, function(message, success) {
|
|
if(window.quickAlgoInterface) {
|
|
window.quickAlgoInterface.displayError(message);
|
|
} else {
|
|
$("#errors").html(message);
|
|
}
|
|
});
|
|
initContextForLevel(subTask.iTestCase);
|
|
subTask.blocklyHelper.run(subTask.context);
|
|
};
|
|
|
|
subTask.submit = function() {
|
|
this.context.display = false;
|
|
this.getAnswerObject(); // to fill this.answer;
|
|
this.getGrade(function(result) {
|
|
subTask.context.display = true;
|
|
subTask.changeSpeed();
|
|
initBlocklyRunner(subTask.context, function(message, success) {
|
|
if(window.quickAlgoInterface) {
|
|
window.quickAlgoInterface.displayError(message);
|
|
} else {
|
|
$("#errors").html(message);
|
|
}
|
|
platform.validate("done");
|
|
});
|
|
subTask.changeTest(result.iTestCase - subTask.iTestCase);
|
|
initContextForLevel(result.iTestCase);
|
|
subTask.context.linkBack = true;
|
|
subTask.context.messagePrefixSuccess = "Tous les tests : ";
|
|
subTask.blocklyHelper.run(subTask.context);
|
|
});
|
|
};
|
|
|
|
subTask.stop = function() {
|
|
this.context.runner.stop();
|
|
};
|
|
|
|
subTask.reloadStateObject = function(stateObj) {
|
|
this.state = stateObj;
|
|
// this.level = state.level;
|
|
|
|
// initContextForLevel(this.level);
|
|
|
|
// this.context.runner.stop();
|
|
};
|
|
|
|
subTask.getDefaultStateObject = function() {
|
|
return { level: "easy" };
|
|
};
|
|
|
|
subTask.getStateObject = function() {
|
|
this.state.level = this.level;
|
|
return this.state;
|
|
};
|
|
|
|
subTask.changeSpeed = function() {
|
|
this.context.changeDelay(parseInt($("#selectSpeed").val()));
|
|
};
|
|
|
|
subTask.getAnswerObject = function() {
|
|
this.blocklyHelper.savePrograms();
|
|
|
|
this.answer = this.blocklyHelper.programs;
|
|
return this.answer;
|
|
};
|
|
|
|
subTask.reloadAnswerObject = function(answerObj) {
|
|
if(typeof answerObj === "undefined") {
|
|
this.answer = this.getDefaultAnswerObject();
|
|
} else {
|
|
this.answer = answerObj;
|
|
}
|
|
this.blocklyHelper.programs = this.answer;
|
|
if (this.answer != undefined) {
|
|
this.blocklyHelper.loadPrograms();
|
|
}
|
|
};
|
|
|
|
subTask.getDefaultAnswerObject = function() {
|
|
var defaultBlockly = '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="robot_start" deletable="false" movable="false" x="0" y="0"></block><block type="robot_start" deletable="false" movable="false" x="0" y="0"></block></xml>';
|
|
return [{javascript:"", blockly: defaultBlockly, blocklyJS: ""}];
|
|
};
|
|
|
|
subTask.changeTest = function(delta) {
|
|
var newTest = subTask.iTestCase + delta;
|
|
if ((newTest >= 0) && (newTest < this.nbTestCases)) {
|
|
initContextForLevel(newTest);
|
|
$("#testCaseName").html("Test " + (newTest + 1) + "/" + this.nbTestCases);
|
|
}
|
|
};
|
|
|
|
subTask.getGrade = function(callback) {
|
|
subTask.context.changeDelay(0);
|
|
var code = subTask.blocklyHelper.getCodeFromXml(subTask.answer[0].blockly, "javascript");
|
|
var codes = [subTask.blocklyHelper.getFullCode(code)];
|
|
subTask.iTestCase = 0;
|
|
initBlocklyRunner(subTask.context, function(message, success) {
|
|
subTask.testCaseResults[subTask.iTestCase] = subTask.gridInfos.computeGrade(subTask.context, message);
|
|
subTask.iTestCase++;
|
|
if (subTask.iTestCase < subTask.nbTestCases) {
|
|
initContextForLevel(subTask.iTestCase);
|
|
subTask.context.runner.runCodes(codes);
|
|
} else {
|
|
var iWorstTestCase = 0;
|
|
var worstRate = 1;
|
|
for (var iCase = 0; iCase < subTask.nbTestCases; iCase++) {
|
|
if (subTask.testCaseResults[iCase].successRate < worstRate) {
|
|
worstRate = subTask.testCaseResults[iCase].successRate;
|
|
iWorstTestCase = iCase;
|
|
}
|
|
}
|
|
subTask.testCaseResults[iWorstTestCase].iTestCase = iWorstTestCase;
|
|
callback(subTask.testCaseResults[iWorstTestCase]);
|
|
}
|
|
});
|
|
subTask.iTestCase = 0;
|
|
subTask.testCaseResults = [];
|
|
initContextForLevel(subTask.iTestCase);
|
|
subTask.context.linkBack = true;
|
|
subTask.context.messagePrefixSuccess = "Tous les tests : ";
|
|
subTask.context.runner.runCodes(codes);
|
|
};
|
|
}
|
|
|
|
// We need to be able to clean all events
|
|
|
|
if (EventTarget.prototype.addEventListenerBase == undefined) {
|
|
EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener;
|
|
EventTarget.prototype.addEventListener = function(type, listener)
|
|
{
|
|
if(!this.EventList) { this.EventList = []; }
|
|
this.addEventListenerBase.apply(this, arguments);
|
|
if(!this.EventList[type]) { this.EventList[type] = []; }
|
|
var list = this.EventList[type];
|
|
for(var index = 0; index != list.length; index++)
|
|
{
|
|
if(list[index] === listener) { return; }
|
|
}
|
|
list.push(listener);
|
|
};
|
|
|
|
EventTarget.prototype.removeEventListenerBase = EventTarget.prototype.removeEventListener;
|
|
EventTarget.prototype.removeEventListener = function(type, listener)
|
|
{
|
|
if(!this.EventList) { this.EventList = []; }
|
|
if(listener instanceof Function) { this.removeEventListenerBase.apply(this, arguments); }
|
|
if(!this.EventList[type]) { return; }
|
|
var list = this.EventList[type];
|
|
for(var index = 0; index != list.length;)
|
|
{
|
|
var item = list[index];
|
|
if(!listener)
|
|
{
|
|
this.removeEventListenerBase(type, item);
|
|
list.splice(index, 1); continue;
|
|
}
|
|
else if(item === listener)
|
|
{
|
|
list.splice(index, 1); break;
|
|
}
|
|
index++;
|
|
}
|
|
if(list.length == 0) { delete this.EventList[type]; }
|
|
};
|
|
}
|
|
|
|
function removeBlockly() {
|
|
$(".blocklyWidgetDiv").remove();
|
|
$(".blocklyTooltipDiv").remove();
|
|
document.removeEventListener("keydown"); //, Blockly.onKeyDown_); // TODO: find correct way to remove all event listeners
|
|
delete Blockly;
|
|
}
|