/**
* An object that manages the various editors, where users can edit their program. Also manages the
* movement between editors.
* There are currently two editors:
* - Blocks: A Blockly instance
* - Text: A html textbox
*
* @constructor
* @this {BlockPyEditor}
*/
function BlockPyEditor() {
// This tool is what actually converts text to blocks!
this.converter = new PythonToBlocks();
// HTML DOM accessors
this.blocklyDiv = document.querySelectorAll('.blockly-div');
// Blockly and CodeMirror instances
this.blockly = null;
// The updateStack keeps track of whether an update is percolating, to prevent duplicate update events.
this.silenceBlock = false;
this.silenceBlockTimer = null;
this.silenceText = false;
this.oldCode = ""; //DA ELIMINARE
// Hack to prevent chrome errors. Forces audio to load on demand.
// See: https://github.com/google/blockly/issues/299
//NON HO CAPITO BENE A COSA SERVE. FORSE PERMETTE DI RISOLVERE UN BUG IN CHROMIUM
Blockly.WorkspaceSvg.prototype.preloadAudio_ = function() {};
// Initialize subcomponents
this.initText();
this.initBlockly();
//SERVONO SOLO INIZIALMENTE
this.updateBlocksFromModel();
this.updateTextFromModel();
}
/**
* Initializes the Blockly instance (handles all the blocks). This includes
* attaching a number of ChangeListeners that can keep the internal code
* representation updated and enforce type checking.
*/
BlockPyEditor.prototype.initBlockly = function() {
//alert("initBlockly");
this.blockly = Blockly.inject(this.blocklyDiv[0],
{ path: "blockly/",
scrollbars: true,
zoom: {enabled: false},
oneBasedIndex: false,
comments: false,
toolbox: this.updateToolbox(false)});
// Register model changer
var editor = this;
this.blockly.addChangeListener(function(evt) {
//alert("eventListener");
editor.updateBlocks();
});
// Force the proper window size
this.blockly.resize();
};
/**
* Initializes the CodeMirror instance. This handles text editing (with syntax highlighting)
* and also attaches a listener for change events to update the internal code represntation.
*/
BlockPyEditor.prototype.initText = function() {
//alert("initText");
// Register model changer
var editor = this;
$('.codemirror-div').keyup(function() {
//alert("change");
editor.updateText();
});
};
/**
* Actually changes the value of the CodeMirror instance
*
* @param {String} code - The new code for the CodeMirror
*/
BlockPyEditor.prototype.setText = function(code) {
//alert("setText");
if (code == undefined || code.trim() == "") {
$('.codemirror-div').val("\n");
} else {
$('.codemirror-div').val(code);
}
}
BlockPyEditor.prototype.setBlocks = function(python_code) {
//alert("setBlocks");
var xml_code = "";
if (python_code !== '' && python_code !== undefined && python_code.trim().charAt(0) !== '<') {
var result = this.converter.convertSource(python_code);
xml_code = result.xml;
}
var error_code = this.converter.convertSourceToCodeBlock(python_code);
var errorXml = Blockly.Xml.textToDom(error_code);
if (python_code == '' || python_code == undefined || python_code.trim() == '') {
this.blockly.clear();
} else if (xml_code !== '' && xml_code !== undefined) {
var blocklyXml = Blockly.Xml.textToDom(xml_code);
try {
this.setBlocksFromXml(blocklyXml);
} catch (e) {
console.error(e);
this.setBlocksFromXml(errorXml);
}
} else {
this.setBlocksFromXml(errorXml);
}
Blockly.Events.disable();
this.blockly.align();
Blockly.Events.enable();
}
BlockPyEditor.prototype.clearDeadBlocks = function() {
//alert("clearDeadBlocks");
var all_blocks = this.blockly.getAllBlocks();
all_blocks.forEach(function(elem) {
if (!Blockly.Python[elem.type]) {
elem.dispose(true);
}
});
}
/**
* Attempts to update the model for the current code file from the
* block workspace. Might be prevented if an update event was already
* percolating.
*/
BlockPyEditor.prototype.updateBlocks = function() {
//alert("updateBlocks");
if (! this.silenceBlock) {
try {
var newCode = Blockly.Python.workspaceToCode(this.blockly);
} catch (e) {
this.clearDeadBlocks();
}
this.silenceText = true;
this.setText(newCode);
}
}
/**
* Attempts to update the model for the current code file from the
* text editor. Might be prevented if an update event was already
* percolating. Also unhighlights any lines.
*/
var timerGuard = null;
BlockPyEditor.prototype.updateText = function() {
//alert("updateText");
if (! this.silenceText) {
var newCode = $('.codemirror-div').val();
// Update Blocks
this.silenceBlock = true;
this.setBlocks(newCode);
this.resetBlockSilence();
//this.silenceBlock = false;
}
this.silenceText = false;
}
/**
* Resets the silenceBlock after a short delay
*/
BlockPyEditor.prototype.resetBlockSilence = function() {
//alert("resetBlockSilence");
var editor = this;
if (editor.silenceBlockTimer != null) {
clearTimeout(editor.silenceBlockTimer);
}
this.silenceBlockTimer = window.setTimeout(function() {
editor.silenceBlock = false;
editor.silenceBlockTimer = null;
}, 40);
};
/**
* Updates the text editor from the current code file in the
* model. Might be prevented if an update event was already
* percolating.
*/
BlockPyEditor.prototype.updateTextFromModel = function() {
//alert("updateTextFromModel");
var code = Blockly.Python.workspaceToCode(this.blockly);
this.setText(code);
}
/**
* Updates the block editor from the current code file in the
* model. Might be prevented if an update event was already
* percolating. This can also report an error if one occurs.
*
* @returns {Boolean} Returns true upon success.
*/
BlockPyEditor.prototype.updateBlocksFromModel = function() {
var code = "print('ciao')";
this.silenceBlock = true;
this.setBlocks(code);
this.resetBlockSilence();
//this.silenceBlock = false;
}
/**
* Helper function for retrieving the current Blockly workspace as
* an XML DOM object.
*
* @returns {XMLDom} The blocks in the current workspace.
*/
BlockPyEditor.prototype.getBlocksFromXml = function() {
//alert("getBlocksFromXml");
return Blockly.Xml.workspaceToDom(this.blockly);
}
/**
* Helper function for setting the current Blockly workspace to
* whatever XML DOM is given. This clears out any existing blocks.
*/
BlockPyEditor.prototype.setBlocksFromXml = function(xml) {
//alert("setBlocksFromXml");
Blockly.Xml.domToWorkspaceDestructive(xml, this.blockly);
}
/**
* Maps short category names in the toolbox to the full XML used to
* represent that category as usual. This is kind of a clunky mechanism
* for managing the different categories, and doesn't allow us to specify
* individual blocks.
*/
BlockPyEditor.CATEGORY_MAP = {
'Variables': '