/** * An object that manages the various editors, where users can edit their program. Also manages the * movement between editors. * There are currently four editors: * - Blocks: A Blockly instance * - Text: A CodeMirror instance * - Instructor: Features for changing the assignment and environment settings * * @constructor * @this {BlockPyEditor} * @param {Object} main - The main BlockPy instance * @param {HTMLElement} tag - The HTML object this is attached to. */ function BlockPyEditor(main, tag) { this.main = main; this.tag = tag; // This tool is what actually converts text to blocks! this.converter = new PythonToBlocks(); // HTML DOM accessors this.blockTag = tag.find('.blockpy-blocks'); this.blocklyDiv = this.blockTag.find('.blockly-div'); this.textTag = tag.find('.blockpy-text'); this.instructorTag = tag.find('.blockpy-instructor'); this.textSidebarTag = this.textTag.find(".blockpy-text-sidebar"); // Blockly and CodeMirror instances this.blockly = null; this.codeMirror = 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.silenceModel = 0; this.blocksFailed = false; this.blocksFailedTimeout = null; // Hack to prevent chrome errors. Forces audio to load on demand. // See: https://github.com/google/blockly/issues/299 Blockly.WorkspaceSvg.prototype.preloadAudio_ = function () { }; // Keep track of the toolbox width this.blocklyToolboxWidth = 0; // Initialize subcomponents this.initText(); this.initBlockly(); this.initInstructor(); this.triggerOnChange = null; var editor = this; var firstEdit = true; this.main.model.program.subscribe(function () { editor.updateBlocksFromModel(); editor.updateTextFromModel(); if (editor.main.model.settings.filename() == "__main__" && !firstEdit) { // if (editor.triggerOnChange) { // clearTimeout(editor.triggerOnChange); // } // var engine = editor.main.components.engine; // editor.triggerOnChange = setTimeout(engine.on_change.bind(engine), 1000); } firstEdit = false; }); // Handle mode switching var settings = this.main.model.settings; settings.editor.subscribe(function () { editor.setMode() }); var updateReadOnly = function () { var newValue = !!(settings.read_only() && !settings.instructor()); //editor.codeMirror.setOption('readOnly', newValue); tag.toggleClass("blockpy-read-only", newValue); }; settings.read_only.subscribe(updateReadOnly); settings.instructor.subscribe(updateReadOnly); // Handle filename switching this.main.model.settings.filename.subscribe(function (name) { if (name == 'give_feedback') { editor.setMode('Text'); } }); // Handle Upload mode turned on this.main.model.assignment.upload.subscribe(function (uploadsMode) { if (uploadsMode) { editor.setMode('Text'); } }); // Have to force a manual block update //this.updateText(); 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 () { this.blockly = Blockly.inject(this.blocklyDiv[0], { path: this.main.model.constants.blocklyPath, scrollbars: this.main.model.constants.blocklyScrollbars, readOnly: this.main.model.settings.read_only(), //"nocursor", // oneBasedIndex: false, comments: false, toolbox: this.updateToolbox(false), collapse: true, css: true, disable: true, grid: false, maxBlocks: Infinity, //media: blocklyPath + '/media/', rtl: false, sounds: true, trashcan: true, zoom: { controls: true, wheel: false, startScale: 1.0, maxScale: 2, minScale: 0.2, scaleSpeed: 1.2 } }); // Register model changer var editor = this; this.blockly.addChangeListener(function (evt) { //editor.main.components.feedback.clearEditorErrors(); editor.blockly.highlightBlock(null); editor.updateBlocks(); }); this.main.model.settings.filename.subscribe(function () { /*if (editor.main.model.settings.editor() == "Blocks") { editor.updateBlocksFromModel() }*/ }); this.main.model.assignment.modules.subscribe(function () { editor.updateToolbox(true) }); // Force the proper window size this.blockly.resize(); // Keep the toolbox width set this.blocklyToolboxWidth = this.getToolbarWidth(); Blockly.captureDialog_ = this.copyImage.bind(this); // Enable static type checking! /* this.blockly.addChangeListener(function() { if (!editor.main.model.settings.disable_variable_types()) { var variables = editor.main.components.engine.analyzeVariables() editor.blockly.getAllBlocks().filter(function(r) {return r.type == 'variables_get'}).forEach(function(block) { var name = block.inputList[0].fieldRow[0].value_; if (name in variables) { var type = variables[name]; if (type.type == "Num") { block.setOutput(true, "Number"); } else if (type.type == "List") { block.setOutput(true, "Array"); } else if (type.type == "Str") { block.setOutput(true, "String"); } else { block.setOutput(true, null); } } }) } }); */ }; /** * Retrieves the current width of the Blockly Toolbox, unless * we're in read-only mode (when there is no toolbox). * @returns {Number} The current width of the toolbox. */ BlockPyEditor.prototype.getToolbarWidth = function () { if (this.main.model.settings.read_only()) { return 0; } else if (this.blockly.toolbox_) { return this.blockly.toolbox_.width; } else { return 0; } } /** * 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 () { var codeMirrorDiv = this.textTag.find('.codemirror-div')[0]; this.codeMirror = CodeMirror.fromTextArea(codeMirrorDiv, { mode: { name: "python", version: 3, singleLineStringErrors: false }, readOnly: this.main.model.settings.read_only(),//"nocursor", // showCursorWhenSelecting: true, lineNumbers: true, firstLineNumber: 1, indentUnit: 4, //tabSize: 4, indentWithTabs: false, matchBrackets: true, extraKeys: { "Tab": "indentMore", "Shift-Tab": "indentLess" }, }); // Register model changer var editor = this; let a = this.codeMirror this.codeMirror.on("change", function () { //editor.main.components.feedback.clearEditorErrors(); editor.updateText() editor.unhighlightLines(); }); a.on("keypress", function () { console.log('11111',editor) a.showHint() }); // Ensure that it fills the editor area this.codeMirror.setSize(null, "100%"); }; BlockPyEditor.prototype.reloadIntroduction = function () { var introductionEditor = this.tag.find('.blockpy-presentation-body-editor'); var model = this.main.model; introductionEditor.code(model.assignment.introduction()); } /** * Initializes the Instructor tab, which has a number of buttons and menus for * manipulating assignments and the environment. One important job is to register the * SummerNote instance used for editing the Introduction of the assignment. */ BlockPyEditor.prototype.initInstructor = function () { var introductionEditor = this.tag.find('.blockpy-presentation-body-editor'); var model = this.main.model; introductionEditor.summernote({ codemirror: { // codemirror options theme: 'monokai' }, onChange: model.assignment.introduction, toolbar: [ ['style', ['bold', 'italic', 'underline', 'clear']], ['font', ['fontname', 'fontsize']], ['insert', ['link', 'table', 'ul', 'ol', 'image']], ['misc', ['codeview', 'help']] ] }); this.reloadIntroduction(); this.availableModules = this.tag.find('.blockpy-available-modules'); this.availableModules.multiSelect({ selectableOptgroup: true }); } /** * Makes the module available in the availableModules multi-select menu by adding * it to the list. * * @param {String} name - The name of the module (human-friendly version, as opposed to the slug) to be added. */ BlockPyEditor.prototype.addAvailableModule = function (name) { this.availableModules.multiSelect('addOption', { 'value': name, 'text': name }); this.availableModules.multiSelect('select', name); }; /** * Hides the Text tab, which involves shrinking it and hiding its CodeMirror too. */ BlockPyEditor.prototype.hideSplitMenu = function () { this.hideTextMenu(); this.hideBlockMenu(); } /** * Shows the Text tab, which requires restoring its height, showing AND refreshing * the CodeMirror instance. */ BlockPyEditor.prototype.showSplitMenu = function () { this.showBlockMenu(); this.showTextMenu(); this.textTag.css('width', '100%'); this.blockTag.css('width', '100%'); this.textSidebarTag.css('width', '0px'); // this.textTag.addClass('col-md-12'); // this.blockTag.addClass('col-md-12'); Blockly.svgResize(this.blockly); } /** * Hides the Text tab, which involves shrinking it and hiding its CodeMirror too. */ BlockPyEditor.prototype.hideTextMenu = function () { this.textTag.css('height', '0%'); $(this.codeMirror.getWrapperElement()).hide(); this.textSidebarTag.hide(); this.textTag.hide(); } /** * Shows the Text tab, which requires restoring its height, showing AND refreshing * the CodeMirror instance. */ BlockPyEditor.prototype.showTextMenu = function () { this.textTag.show(); // Adjust height this.textTag.css('height', '100%'); this.textTag.css('width', '100%'); // Show CodeMirror $(this.codeMirror.getWrapperElement()).show(); // CodeMirror doesn't know its changed size this.codeMirror.refresh(); // Resize sidebar var codemirrorGutterWidth = $('.CodeMirror-gutters').width(); var sideBarWidth = this.blocklyToolboxWidth - codemirrorGutterWidth - 2; this.textSidebarTag.css('width', sideBarWidth + 'px'); this.textSidebarTag.show(); // this.textTag.removeClass('col-md-6'); } /** * Hides the Block tab, which involves shrinking it and hiding the Blockly instance. */ BlockPyEditor.prototype.hideBlockMenu = function () { this.blocklyToolboxWidth = this.getToolbarWidth(); this.blockTag.css('height', '0%'); this.blocklyDiv.css("width", "0"); this.blockly.setVisible(false); } /** * Shows the Block tab, which involves restoring its height and showing the Blockly instance. */ BlockPyEditor.prototype.showBlockMenu = function () { this.blockTag.css('height', '100%'); this.blockTag.css('width', '100%'); this.blocklyDiv.css("width", "100%"); this.blockly.resize(); this.blockly.setVisible(true); this.blockTag.removeClass('col-md-6'); Blockly.svgResize(this.blockly); } /** * Hides the Instructor tab, which shrinking it. */ BlockPyEditor.prototype.hideInstructorMenu = function () { this.instructorTag.hide(); this.instructorTag.css('height', '0%'); } /** * Shows the Instructor tab, which involves restoring its height. */ BlockPyEditor.prototype.showInstructorMenu = function () { this.instructorTag.css('height', '100%'); this.instructorTag.show(); } /** * Sets the current editor mode to Text, hiding the other menus. * Also forces the text side to update. */ BlockPyEditor.prototype.setModeToText = function () { this.hideBlockMenu(); this.hideInstructorMenu(); this.showTextMenu(); // Update the text model from the blocks } /** * Sets the current editor mode to Blocks, hiding the other menus. * Also forces the block side to update. * There is a chance this could fail, if the text side is irredeemably * awful. So then the editor bounces back to the text side. */ BlockPyEditor.prototype.setModeToBlocks = function () { this.hideTextMenu(); this.hideInstructorMenu(); this.showBlockMenu(); if (this.blocksFailed !== false) { this.showConversionError(); var main = this.main; main.model.settings.editor("Text"); setTimeout(function () { main.components.toolbar.tags.mode_set_text.click(); }, 0); } // Update the blocks model from the text /* success = this.updateBlocksFromModel(); if (!success) { var main = this.main; main.components.editor.updateTextFromModel(); main.model.settings.editor("Text"); setTimeout(function() { main.components.toolbar.tags.mode_set_text.click(); }, 0); }*/ } /** * Sets the current editor mode to Split mode, hiding the other menus. */ BlockPyEditor.prototype.setModeToSplit = function () { this.hideTextMenu(); this.hideInstructorMenu(); this.hideBlockMenu(); this.showSplitMenu(); if (this.blocksFailed !== false) { this.showConversionError(); } } /** * Sets the current editor mode to the Instructor mode, hiding the other menus. */ BlockPyEditor.prototype.setModeToInstructor = function () { this.hideTextMenu(); this.hideBlockMenu(); this.showInstructorMenu(); //TODO: finish upload mode //this.main.reportError("editor", "Instructor mode has not been implemented"); } BlockPyEditor.prototype.changeMode = function () { if (main.model.settings.editor() == "Blocks") { main.model.settings.editor("Text"); } else { main.model.settings.editor("Blocks"); } } /** * Dispatch method to set the mode to the given argument. * If the mode is invalid, an editor error is reported. If the * * @param {String} mode - The new mode to set to ("Blocks", "Text", or "Instructor") */ BlockPyEditor.prototype.setMode = function (mode) { // Either update the model, or go with the model's if (mode === undefined) { mode = this.main.model.settings.editor(); } else { this.main.model.settings.editor(mode); } // Dispatch according to new mode if (mode == 'Blocks') { this.setModeToBlocks(); } else if (mode == 'Text') { this.setModeToText(); } else if (mode == 'Split') { this.setModeToSplit(); } else if (mode == 'Instructor') { this.setModeToInstructor(); } else if (mode == 'Upload') { this.setModeToText(); } // else { // this.main.components.feedback.internalError("" + mode, "Invalid Mode", "The editor attempted to change to an invalid mode.") // } } /** * Actually changes the value of the CodeMirror instance * * @param {String} code - The new code for the CodeMirror */ BlockPyEditor.prototype.setText = function (code) { if (code == undefined || code.trim() == "") { this.codeMirror.setValue("\n"); } else { this.codeMirror.setValue(code); } // Ensure that we maintain proper highlighting this.refreshHighlight(); } BlockPyEditor.prototype.showConversionError = function () { var error = this.blocksFailed; //this.main.components.feedback.convertSkulptSyntax(error); } BlockPyEditor.prototype.setBlocks = function (python_code) { if (!(!this.main.model.assignment.upload() && (this.main.model.settings.filename() == "__main__" || this.main.model.settings.filename() == "starting_code"))) { return false; } 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; window.clearTimeout(this.blocksFailedTimeout); if (result.error !== null) { this.blocksFailed = result.error; var editor = this; this.blocksFailedTimeout = window.setTimeout(function () { if (editor.main.model.settings.editor() != 'Text') { editor.showConversionError(); } }, 500) } else { this.blocksFailed = false; //this.main.components.feedback.clearEditorErrors(); } } 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); pythonnewcode = python_code; } catch (e) { console.error(e); this.setBlocksFromXml(errorXml); } } else { this.setBlocksFromXml(errorXml); } Blockly.Events.disable(); /* // Parsons shuffling if (this.main.model.assignment.parsons()) { this.blockly.shuffle(); } else { this.blockly.align(); } */ Blockly.Events.enable(); if (this.previousLine !== null) { this.refreshBlockHighlight(this.previousLine); } } BlockPyEditor.prototype.clearDeadBlocks = function () { 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 () { if (!this.silenceBlock) { try { var newCode = Blockly.Python.workspaceToCode(this.blockly); pythonnewcode = newCode; } catch (e) { this.clearDeadBlocks(); //this.main.components.feedback.editorError("Unknown Block", "It looks like you attempted to paste or load some blocks that were not known. Typically, this is because you failed to load in the dataset before trying to paste in a data block. If there are any black blocks on the canvas, delete them before continuing.", "Unknown Block"); } // Update Model this.silenceModel = 2; var changed = this.main.setCode(newCode); if (!changed) { this.silenceModel = 0; } else { // Update Text 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 (code) { if (!this.silenceText) { var newCode = code || this.codeMirror.getValue(); // Update Model this.silenceModel = 2; this.main.setCode(newCode); // Update Blocks this.silenceBlock = true; this.setBlocks(newCode); this.unhighlightLines(); this.resetBlockSilence(); } this.silenceText = false; } /** * Resets the silenceBlock after a short delay */ BlockPyEditor.prototype.resetBlockSilence = function () { 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 () { if (this.silenceModel == 0) { var code = this.main.model.program(); this.silenceText = true; this.setText(code); } else { this.silenceModel -= 1; } } /** * 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 () { if (this.silenceModel == 0) { var code = this.main.model.program().trim(); this.silenceBlock = true; this.setBlocks(code); this.resetBlockSilence(); } else { this.silenceModel -= 1; } } /** * 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 () { 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) { //this.blockly.clear(); Blockly.Xml.domToWorkspaceDestructive(xml, this.blockly); //console.log(this.blockly.getAllBlocks()); } BlockPyEditor.prototype.clearBlocksFromXml = function () { this.blockly.clear(); } /** * @property {Number} previousLine - Keeps track of the previously highlighted line. */ BlockPyEditor.prototype.previousLine = null; /** * Assuming that a line has been highlighted previously, this will set the * line to be highlighted again. Used when we need to restore a highlight. */ BlockPyEditor.prototype.refreshHighlight = function () { if (this.previousLine !== null) { if (this.previousLine < this.codeMirror.lineCount()) { this.codeMirror.addLineClass(this.previousLine, 'text', 'editor-error-line'); } } // TODO: Shouldn't this refresh the highlight in the block side too? } /** * Highlights a line of code in the CodeMirror instance. This applies the "active" style * which is meant to bring attention to a line, but not suggest it is wrong. * * @param {Number} line - The line of code to highlight. I think this is zero indexed? */ BlockPyEditor.prototype.highlightLine = function (line) { if (this.previousLine !== null) { if (this.previousLine < this.codeMirror.lineCount()) { this.codeMirror.removeLineClass(this.previousLine, 'text', 'editor-active-line'); this.codeMirror.removeLineClass(this.previousLine, 'text', 'editor-error-line'); } } if (line < this.codeMirror.lineCount()) { this.codeMirror.addLineClass(line, 'text', 'editor-active-line'); } this.previousLine = line; } /** * Highlights a line of code in the CodeMirror instance. This applies the "error" style * which is meant to suggest that a line is wrong. * * @param {Number} line - The line of code to highlight. I think this is zero indexed? */ BlockPyEditor.prototype.highlightError = function (line) { if (this.previousLine !== null) { if (this.previousLine < this.codeMirror.lineCount()) { this.codeMirror.removeLineClass(this.previousLine, 'text', 'editor-active-line'); this.codeMirror.removeLineClass(this.previousLine, 'text', 'editor-error-line'); } } if (line < this.codeMirror.lineCount()) { this.codeMirror.addLineClass(line, 'text', 'editor-error-line'); } this.refreshBlockHighlight(line); this.previousLine = line; } /** * Highlights a block in Blockly. Unfortunately, this is the same as selecting it. * * @param {Number} block - The ID of the block object to highlight. */ BlockPyEditor.prototype.highlightBlock = function (block) { //this.blockly.highlightBlock(block); } /** * Used to restore a block's highlight when travelling from the code tab. This * uses a mapping between the blocks and text that is generated from the parser. * The parser has stored the relevant line numbers for each block in the XML of the * block. Very sophisticated, and sadly fairly fragile. * TODO: I believe there's some kind of off-by-one error here... * * @param {Number} line - The line of code to highlight. I think this is zero indexed? */ BlockPyEditor.prototype.refreshBlockHighlight = function (line) { if (this.blocksFailed) { this.blocksFailed = false; return; } if (this.main.model.settings.editor() != "Blocks" && this.main.model.settings.editor() != "Split") { return; } var all_blocks = this.blockly.getAllBlocks(); //console.log(all_blocks.map(function(e) { return e.lineNumber })); var blockMap = {}; all_blocks.forEach(function (elem) { var lineNumber = parseInt(elem.lineNumber, 10); if (lineNumber in blockMap) { blockMap[lineNumber].push(elem); } else { blockMap[lineNumber] = [elem]; } }); if (1 + line in blockMap) { var hblocks = blockMap[1 + line]; var blockly = this.blockly; hblocks.forEach(function (elem) { //elem.addSelect(); blockly.highlightBlock(elem.id, true); }); /*if (hblocks.length > 0) { this.blockly.highlightBlock(hblocks[0].id, true); }*/ } } /** * Removes the outline around a block. Currently unused. */ BlockPyEditor.prototype.unhighlightBlock = function () { // TODO: } /** * Removes any highlight in the text code editor. * */ BlockPyEditor.prototype.unhighlightLines = function () { if (this.previousLine !== null) { if (this.previousLine < this.codeMirror.lineCount()) { this.codeMirror.removeLineClass(this.previousLine, 'text', 'editor-active-line'); this.codeMirror.removeLineClass(this.previousLine, 'text', 'editor-error-line'); } } this.previousLine = null; } /** * Removes any highlight in the text code editor. * */ BlockPyEditor.prototype.unhighlightAllLines = function () { var editor = this.codeMirror; var count = editor.lineCount(), i; for (i = 0; i < count; i++) { editor.removeLineClass(i, 'text', 'editor-error-line'); } } /** * DEPRECATED, thankfully * Builds up an array indicating the relevant block ID for a given step. * Operates on the current this.blockly instance * It works by injecting __HIGHLIGHT__(id); at the start of every line of code * and then extracting that with regular expressions. This makes it vulnerable * if someone decides to use __HIGHLIGHT__ in their code. I'm betting on that * never being a problem, though. Still, this was a miserable way of accomplishing * the desired behavior. */ BlockPyEditor.prototype.getHighlightMap = function () { // Protect the current STATEMENT_PREFIX var backup = Blockly.Python.STATEMENT_PREFIX; Blockly.Python.STATEMENT_PREFIX = '__HIGHLIGHT__(%1);'; Blockly.Python.addReservedWords('__HIGHLIGHT__'); // Get the source code, injected with __HIGHLIGHT__(id) var highlightedCode = Blockly.Python.workspaceToCode(this.blockly); Blockly.Python.STATEMENT_PREFIX = backup; // Build up the array by processing the highlighted code line-by-line var highlightMap = []; var lines = highlightedCode.split("\n"); for (var i = 0; i < lines.length; i++) { // Get the block ID from the line var id = lines[i].match(/\W*__HIGHLIGHT__\(\'(.+?)\'\)/); if (id !== null) { // Convert it into a base-10 number, because JavaScript. highlightMap[i] = parseInt(id[1], 10); } } return highlightMap; } /** * Updates the current file being edited in the editors. * This appears to be deprecated. * * @param {String} name - The name of the file being edited (e.g, "__main__", "starting_code") */ /* BlockPyEditor.prototype.changeProgram = function(name) { console.log("TEST") this.silentChange_ = true; if (name == 'give_feedback') { this.setMode('Text'); } this.model.settings.filename = name; this.editor.setPython(this.model.programs[name]); this.toolbar.elements.programs.find("[data-name="+name+"]").click(); }*/ /** * Eventually will be used to update "levels" of sophistication of the code interface. * Currently unimplemented and unused. */ BlockPyEditor.prototype.setLevel = function () { var level = this.main.model.settings.level(); } /** * 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 = { 'Decisions': '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '', 'Iteration': '' + '' + '' + '' + '' + ' 10' + '' + '' + '' + '' + '' + '' + '' + '1' + '' + '' + '' + '' + '10' + '' + '' + '' + '' + '1' + '' + '' + '' + '' + '' + '', 'Calculation': '' + '' + '' + ' ' + '' + ' 1' + '' + '' + '' + '' + ' 1' + '' + '' + '' + '' + '' + '' + '' + ' 9' + '' + '' + '' + '' + '' + '' + ' 45' + '' + '' + '' + '' + '' + '' + '' + '' + ' 0' + '' + '' + '' + '' + '' + ' ' + ' 3.1' + '' + '' + '' + '' + '' + ' ' + ' 3.1415926' + '' + '' + '' + ' ' + ' 2' + '' + '' + '' + '' + '' + '' + '' + ' 64' + '' + '' + ' ' + '' + ' 10' + '' + '' + ' ' + '' + ' ' + '' + ' 64' + '' + '' + '' + '' + ' 10' + '' + '' + '' + '' + ' ' + '' + ' 50' + '' + '' + '' + '' + ' 1' + '' + '' + ' ' + '' + ' 100' + '' + ' ' + '' + ' ' + '10' + '0' + '100' + '0' + '200' + '' + '' + '' + '' + ' 1' + '' + '' + '' + '' + ' 100' + '' + '' + '' + '' + '0' + ' 10' + '2' + '' + '' + '' + '' + '' + '' + ' 1' + '' + '' + '' + '' + ' 1' + '' + '' + '' + '' + '60' + '' + '' + ' 10' + '' + '' + ' 11' + '' + '' + ' 11' + '' + '' + ' 97' + '' + '' + ' a' + ' ' + ' ' + 'abcdfg' + '' + '' + '313233' + ' ' + '' + ' cocorobo' + '' + '' + ' bXB5dGhvbg==' + '' + '', 'Variables': '' + '', 'Values': '' + // '' + // '' + // '' + // '' + // '' + // ' ' + // '' + ' ' + // '' + '' + ' ' + '' + '' + ' ' + ' ' + ' Value: %.2f' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 3.1415926' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' Value: {:.3}' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 3.1415926' + ' ' + ' ' + '' + '' + ' ' + '' + '' + '' + 'abc' + 'def' + '' + '' + '' + ' ' + '' + '' + '' + '' + '' + '' + '' + ' ' + '' + '' + '' + '' + '' + '' + ' ' + '' + '' + '' + '' + '' + ' ' + 'abc' + '' + '' + '' + ' ' + 'b' + ' ' + '' + '' + '' + '' + '' + 'abc' + ' ' + '' + ' ' + ' ' + '' + ' ' + 'abc' + ' ' + '' + ' ' + ' ' + '' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' abc' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + '' + ' "Age":8' + '' + '' + '{"Age":8}' + '' + '' + '' + '' + '', 'Lists': '' + '' + '' + ' 0, 0, 0' + ' ' + ' ' + ' my_list' + ' 0, 0, 0' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' cocorobo' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + ' ' + ' 5' + ' ' + '' + ' ' + ' ' + ' ' + ' ' + ' cocorobo' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' 0' + ' ' + ' ' + ' ' + ' 0' + ' 0' + ' ' + ' ' + ' ' + ' 0' + ' 2' + ' ' + '' + ' ' + ' REMOVE' + ' FROM_START' + ' ' + ' ' + ' ' + '' + '' + ' ' + ' GET_REMOVE' + ' FROM_START' + ' ' + ' ' + ' ' + '' + ' ' + ' ,' + ' ' + ' ' + '', 'Dictionary': '' + ' ' + ' "Age":8' + ' ' + ' my_dict' + ' "Age":8' + ' ' + ' ' + ' ' + ' Age' + ' 10' + ' ' + ' ' + ' ' + ' Age' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' Age' + ' ' + ' ' + ' ' + ' ' + '', // 'Tuples': '' + // '', 'Tuples': '' + ' ' + '"cocorobo",' + '' + ' my_tuple' + ' "cocorobo",' + ' ' + ' ' + ' ' + ' 0' + '' + '' + ' ' + ' ' + '' + ' ' + ' cocorobo' + '' + '' + ' ' + ' 2' + '' + ' ' + ' ' + ' 0' + ' 2' + '' + '' + ' ' + '' + '', 'Set': '' + '' + ' "string", 0.9, ("tuple",)' + ' ' + ' my_set' + ' ' + ' ' + ' ' + ' my_set' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + ' ' + ' ' + '' + '', 'Functionsa': '' + '', 'Python': '' + '' + '' + '', 'Output': '' + // '' + // ' ' + // '' + // ' 1' + // '' + // '' + // '' + // '' + // ' ' + // '' + // ' 1' + // '' + // '' + // ' ' + // '' + // '' + // '' + // '' + // '' + // ' ' + // '' + // ' ' + // '' + // '' + // // PWM ai // '' + // ' ' + // '' + // ' ' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + '', 'Files': '' + '' + ' ' + ' ' + ' /your_python_code.py' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' cocorobo.txt' + ' ' + ' ' + ' ' + ' ' + ' /' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' cocorobo.txt' + ' ' + ' ' + ' ' + ' ' + ' /' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + '' + '' + ' ' + ' ' + ' cocorobo.txt' + ' ' + ' ' + ' ' + ' ' + ' /' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' /cocorobo.txt' + ' ' + ' ' + '' + '', 'Time': '' + ' ' + ' ' + ' ' + ' 1000' + ' ' + ' ' + ' ' + // ' ' + // ' ' + // ' ' + // ' 1000' + // ' ' + // ' ' + // ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + // ' ' + // ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '', 'Serial Comm.': '' + '' + ' ' + ' ' + ' Hello World!' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' Data' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' Data' + ' ' + ' ' + '' + '' + '' + '' + '' + ' ' + ' ' + ' Data' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' Data' + ' ' + ' ' + '' + '', 'MainBoard': '' + '' + '' + '' + '' + '' + '' + '' + '' + '', 'A.I.Board': '' + /* _ ___ ____ _ / \ |_ _| | __ ) __ _ ___(_) ___ / _ \ | | | _ \ / _` / __| |/ __| / ___ \ | | | |_) | (_| \__ \ | (__ /_/ \_\___| |____/ \__,_|___/_|\___| */ '' +//基础 '' + '' + '' + // '' + '' + '' + '' + // '' + '' + ''+ ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + '' + ' ' + '' + ' 1' + '' + '' + ' ' + '' + '' + '' + '' + '' + ' ' + // '' + // ' 1' + // '' + ' ' + '' + ' ' + '' + ' 1' + '' + '' + '' + // // PWM ai // '' + // ' ' + // '' + // ' ' + // '' + // '' + '' + '' + // '' + // '' + // '' + // '' + ''+ /* _ ___ __ __ _ _ / \ |_ _| | \/ (_) ___ _ __ ___ _ __ | |__ ___ _ __ ___ / _ \ | | | |\/| | |/ __| '__/ _ \| '_ \| '_ \ / _ \| '_ \ / _ \ / ___ \ | | | | | | | (__| | | (_) | |_) | | | | (_) | | | | __/ /_/ \_\___| |_| |_|_|\___|_| \___/| .__/|_| |_|\___/|_| |_|\___| |_| */ // 传感器 '' + '' + '' + '' + '' + '' + // 传感器 '' + '' + '' + '' + ' ' + ' ' + ' 90' + ' ' + ' ' + '' + '' + '' + '' + ' ' + ' ' + ' 150' + ' ' + ' ' + '' + '' + '' + '' + '' + // '' + // '' + // '' + // '' + '' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + '' + ' ' + ' ' + ' ' + ' 320' + ' ' + ' ' + ' ' + ' ' + ' 240' + ' ' + ' ' + ' ' + '' + ' ' + ' ' + ' /root/preset/fonts/CascadiaCodePL-Italic.ttf' + ' ' + ' ' + ''+ '' + '' + ' ' + ' ' + ' ' + ' ' + ' 320' + ' ' + ' ' + ' ' + ' ' + ' 240' + ' ' + ' ' + '' + ' ' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + '' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 320' + ' ' + ' ' + ' ' + ' ' + ' 240' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 2' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' /root/user/img/saved.jpg' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + '' + // '' + '' + '' + '' + '' + '' + '' + '' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + '' + '' + '' + '' + ''+ ' ' + ' ' + ' /root/user/audio/record.wav' + ' ' + ' ' + ' ' + ' ' + ' 4' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + '' + '' + '' + ' ' + ' ' + ' /root/preset/audio/luckystar.wav' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + '', 'ExtendedFunctions': '' + /* _ ___ ____ / \ |_ _| / ___| ___ _ __ ___ ___ _ __ / _ \ | | \___ \ / __| '__/ _ \/ _ \ '_ \ / ___ \ _ | | _ ___) | (__| | | __/ __/ | | | /_/ \_(_)___(_) |____/ \___|_| \___|\___|_| |_| */ '' + '' + '' + ' ' + ' ' + ' /root/user/img/saved.jpg' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' /root/user/img/saved.jpg' + ' ' + ' ' + '' + '' + ''+ ' ' + ' ' + ' 50' + ' ' + ' ' + '' + // '' + '' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + '' + // '' + // '' + '' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + '' + '' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + '' + // '' + // ' ' + // ' ' + // ' ' + // ' ' + // ' 0' + // ' ' + // ' ' + // ' ' + // ' ' + // ' 0' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' ' + // ' 0' + // ' ' + // ' ' + // ' ' + // ' ' + // ' 0' + // ' ' + // ' ' + // ' ' + // ' ' + // '' + '' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 10' + ' ' + ' ' + ' ' + ' ' + ' 10' + ' ' + ' ' + ' ' + ' ' + ' 10' + ' ' + ' ' + '' + ''+ '' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 10' + ' ' + ' ' + ' ' + ' ' + ' 10' + ' ' + ' ' + ' ' + ' ' + ' 10' + ' ' + ' ' + '' + ''+ '' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 140' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 100' + ' ' + ' ' + ' ' + ' ' + ' 240' + ' ' + ' ' + ' ' + ' ' + '' + ' ' + ' ' + '' + ' ' + ' ' + ' ' + ' ' + ' 220' + ' ' + ' ' + ' ' + ' ' + ' 140' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 320' + ' ' + ' ' + ' ' + ' ' + ' 240' + ' ' + ' ' + ' ' + ' ' + '' + ' ' + ' ' + ' 112' + ' ' + '' + '' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + // '' + // '' + '' + '' + '' + /* _ ___ __ ___ _ / \ |_ _| \ \ / (_) __| | ___ ___ / _ \ | | \ \ / /| |/ _` |/ _ \/ _ \ / ___ \ | | \ V / | | (_| | __/ (_) | /_/ \_\___| \_/ |_|\__,_|\___|\___/ */ '' + '' + '' + ' ' + ' ' + ' /root/user/video/record.mp4' + ' ' + ' ' + '' + '' + '' + '' + '' + ' ' + ' ' + ' /root/user/video/record.mp4' + ' ' + ' ' + ' ' + ' ' + ' 320' + ' ' + ' ' + ' ' + ' ' + ' 240' + ' ' + ' ' + '' + '' + '' + '' + '', 'AI': '' + '' +//模型 '' + '' + '' + '' + '' + // '' + // '' + // '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + // 车牌识别 '' + '' + '' + '' + '' + '' + // '' + // '' + // '' + // '' + // '' + // '' + '' + '' + ' ' + ' ' + ' ' + ' Name1' + ' ' + ' ' + ' Name2' + ' ' + ' ' + ' Name3' + ' ' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + ' ' + ' /root/user/model/recorded_face_features.py' + ' ' + '' + '' + // '' + // ' ' + // ' /root/user/model/recorded_face_features.py' + // ' ' + // '' + // ''+ // ' ' + // ' recorded_face_features' + // ' ' + // '' + // '' + '' + ' ' + ' /root/user/model/recorded_face_features.py' + ' ' + '' + '' + // '' + '' + '' + ' ' + ' ' + ' ' + ' Object 1 Name' + ' ' + ' ' + ' Object 2 Name' + ' ' + ' ' + ' Object 3 Name' + ' ' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + '' + '' + '' + // '' + '' + // '' + '' + ' ' + ' ' + ' ' + ' Object 1 Name' + ' ' + ' ' + ' Object 2 Name' + ' ' + ' ' + ' Object 3 Name' + ' ' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + '' + '' + // ''+ // ' ' + // ' ' + // ' 0' + // ' ' + // ' ' + // '' + '' + '' + '' + '' + // '' + // '' + // '' + // '' + // '' + // ' ' + // ' ' + // ' ' + // ' Name1' + // ' ' + // ' ' + // ' Name2' + // ' ' + // ' ' + // ' Name3' + // ' ' + // ' ' + // ' ' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + // ' ' + // ' ' + // ' ' + // '' + // '' + // '' + // ' ' + // ' recorded_face_features' + // ' ' + // '' + // '' + // ' ' + // ' recorded_face_features' + // ' ' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + // '' + // ' ' + // ' ' + // ' ' + // ' Class Name1' + // ' ' + // ' ' + // ' Class Name2' + // ' ' + // ' ' + // ' Class Name3' + // ' ' + // ' ' + // ' ' + // '' + // '' + // '' + // '' + // '' + // '' + // ' ' + // ' 3_classes' + // ' ' + // '' + // '' + // '' + // ' ' + // ' 3_classes' + // ' ' + // '' + // '' + // '' + // '' + // '' + // '' + // ' ' + // ' ' + // ' ' + // ' Name1' + // ' ' + // ' ' + // ' Name2' + // ' ' + // ' ' + // ' Name3' + // ' ' + // ' ' + // ' ' + // '' + // '' + // ' ' + // ' ' + // ' ' + // ' Class Name1' + // ' ' + // ' ' + // ' Class Name2' + // ' ' + // ' ' + // ' Class Name3' + // ' ' + // ' ' + // ' ' + // '' + // '' + // '' + // '' + // '' + '' + // 讯飞在线识别 ''+ '' + '' + ' ' + ' ' + ' 2a588b52' + ' ' + ' ' + ' ' + ' ' + ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + ' ' + ' ' + ' ' + ' ' + ' afa7ae78c88a5fb6e93d836ffa941938' + ' ' + ' ' + '' + ''+ ' ' + ' ' + ' /root/preset/img/cocorobo_logo.jpg' + ' ' + ' ' + '' + '' + '' + '' + '' + ' ' + ' ' + ' 2a588b52' + ' ' + ' ' + ' ' + ' ' + ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + ' ' + ' ' + ' ' + ' ' + ' afa7ae78c88a5fb6e93d836ffa941938' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' hello world' + ' ' + ' ' + ' ' + ' ' + ' /demo.pcm' + ' ' + ' ' + '' + '' + '' + ' ' + ' ' + ' 2a588b52' + ' ' + ' ' + ' ' + ' ' + ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + ' ' + ' ' + ' ' + ' ' + ' afa7ae78c88a5fb6e93d836ffa941938' + ' ' + ' ' + '' + ''+ ' ' + ' ' + ' /root/user/audio/record.wav' + ' ' + ' ' + '' + '' + // '' + // '' + // ' ' + // ' ' + // ' 2a588b52' + // ' ' + // ' ' + // // ' ' + // // ' ' + // // ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + // // ' ' + // // ' ' + // ' ' + // ' ' + // ' afa7ae78c88a5fb6e93d836ffa941938' + // ' ' + // ' ' + // '' + // ''+ // ' ' + // ' ' + // ' /root/preset/img/cocorobo_logo.jpg' + // ' ' + // ' ' + // '' + // '' + '' + '' + ' ' + ' ' + ' 2a588b52' + ' ' + ' ' + ' ' + ' ' + ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + ' ' + ' ' + ' ' + ' ' + ' afa7ae78c88a5fb6e93d836ffa941938' + ' ' + ' ' + '' + ''+ ' ' + ' ' + ' /root/preset/img/cocorobo_logo.jpg' + ' ' + ' ' + '' + '' + '' + '' + ' ' + ' ' + ' 2a588b52' + ' ' + ' ' + ' ' + ' ' + ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + ' ' + ' ' + ' ' + ' ' + ' afa7ae78c88a5fb6e93d836ffa941938' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' hello world' + ' ' + ' ' + '' + '' + '' + '' + ' ' + ' ' + ' 2a588b52' + ' ' + ' ' + // ' ' + // ' ' + // ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + // ' ' + // ' ' + ' ' + ' ' + ' afa7ae78c88a5fb6e93d836ffa941938' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' /root/preset/img/cocorobo_logo.jpg' + ' ' + ' ' + '' + '' + '' + '' + ' ' + ' ' + ' 2a588b52' + ' ' + ' ' + ' ' + ' ' + ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + ' ' + ' ' + ' ' + ' ' + ' afa7ae78c88a5fb6e93d836ffa941938' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' /root/preset/img/cocorobo_logo.jpg' + ' ' + ' ' + '' + '' + // '' + // '' + // ' ' + // ' ' + // ' 2a588b52' + // ' ' + // ' ' + // ' ' + // ' ' + // ' MzQyZDE3MmQ1ZThhNjBiODI3ZDEzY2Nk' + // ' ' + // ' ' + // ' ' + // ' ' + // ' afa7ae78c88a5fb6e93d836ffa941938' + // ' ' + // ' ' + // '' + // '' + // ' ' + // ' ' + // ' /root/preset/img/cocorobo_logo.jpg' + // ' ' + // ' ' + // '' + // '' + '' + '' + ' ' + ' "age":"", "emotion":"", "gender":"", "expression":""' + ' ' + '' + '' + ' ' + ' ' + ' /root/preset/img/cocorobo_logo.jpg' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + ' ' + ' ' + ' /root/preset/img/cocorobo_logo.jpg' + ' ' + ' ' + '' + '' + '' + '', 'IOT': '' + '' + '' + ' ' + ' ' + ' ENTER_YOUR_SSID' + ' ' + ' ' + ' ' + ' ' + ' ENTER_YOUR_PASSWORD' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + '' + '' + '' + '' + ' ' + ' ' + ' 0' + ' ' + ' ' + '' + '' + '' + '' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' 1' + ' ' + ' ' + '' + '' + '' + ' ' + ' ' + ' HTTP://ENTER_AN_URL' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' HTTP://ENTER_AN_URL' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + '' + '' + '' + '' + '' + '' + ' ' + ' ' + ' 0' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' Property' + ' ' + ' ' + ' ' + ' ' + ' value' + ' ' + ' ' + '' + '' + '' + ' ' + ' ' + ' property' + ' ' + ' ' + '' + '' + '', 'ExtendedFunction': '' + '' + '' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' 16' + ' ' + ' ' + '' + '' + '' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + ' ' + ' ' + ' 0' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' 1' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' 50' + ' ' + ' ' + '' + '' + '' + '' + // '' + '' + '' + ' ' + ' ' + ' 31' + ' ' + ' ' + '' + '' + '' + ' ' + ' ' + ' ' + ' ' + '' + // '' + // '' + // '' + // '' + '' + '', // 'Third_party_Sensor': // 第三方 // '' + // '' + // '' + // ' ' + // ' ' + // ' 4' + // ' ' + // ' ' + // ' ' + // ' ' + // ' 6' + // ' ' + // ' ' + // ' ' + // ' ' + // ' 6' + // ' ' + // ' ' + // ' ' + // ' ' + // ' 50' + // ' ' + // ' ' + // '' + // '' + // '', }; /** * Creates an updated representation of the Toolboxes XML as currently specified in the * model, using whatever modules have been added or removed. This method can either set it * or just retrieve it for future use. * * @param {Boolean} only_set - Whether to return the XML string or to actually set the XML. False means that it will not update the toolbox! * @returns {String?} Possibly returns the XML of the toolbox as a string. */ BlockPyEditor.prototype.updateToolbox = function (only_set) { // let blocklyXml = Blockly.Xml.textToDom(BlockPyEditor.CATEGORY_MAP) // var categoryNodes = blocklyXml.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); // } // } var xml = ''; Ardublockly.initLanguage(); Ardublockly.updateToolboxLanguage(xml); xml = Ardublockly.xmlTree; if (only_set && !this.main.model.settings.read_only()) { this.blockly.updateToolbox(xml); this.blockly.resize(); } else { return xml; } }; BlockPyEditor.prototype.DOCTYPE = '' + '<' + '!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; BlockPyEditor.prototype.cssData = null; BlockPyEditor.prototype.loadCss = function () { if (this.cssData == null) { var txt = '.blocklyDraggable {}\n'; txt += Blockly.Css.CONTENT.join('\n'); if (Blockly.FieldDate) { txt += Blockly.FieldDate.CSS.join('\n'); } // Strip off any trailing slash (either Unix or Windows). this.cssData = txt.replace(/<<>>/g, Blockly.Css.mediaPath_); } } /** * Generates a PNG version of the current workspace. This PNG is stored in a Base-64 encoded * string as part of a data URL (e.g., "data:image/png;base64,..."). * TODO: There seems to be some problems capturing blocks that don't start with * statement level blocks (e.g., expression blocks). * * @param {Function} callback - A function to be called with the results. This function should take two parameters, the URL (as a string) of the generated base64-encoded PNG and the IMG tag. */ BlockPyEditor.prototype.getPngFromBlocks = function (callback) { this.loadCss(); try { // Retreive the entire canvas, strip some unnecessary tags var blocks = this.blockly.svgBlockCanvas_.cloneNode(true); blocks.removeAttribute("width"); blocks.removeAttribute("height"); // Ensure that we have some content if (blocks.childNodes[0] !== undefined) { // Remove tags that offset blocks.removeAttribute("transform"); blocks.childNodes[0].removeAttribute("transform"); blocks.childNodes[0].childNodes[0].removeAttribute("transform"); // Add in styles var linkElm = document.createElementNS("http://www.w3.org/1999/xhtml", "style"); linkElm.textContent = this.cssData + '\n\n'; blocks.insertBefore(linkElm, blocks.firstChild); // Get the bounding box var bbox = document.getElementsByClassName("blocklyBlockCanvas")[0].getBBox(); // Create the XML representation of the SVG var xml = new XMLSerializer().serializeToString(blocks); xml = '' + xml + ''; // create a file blob of our SVG. // Unfortunately, this crashes modern chrome for unknown reasons. //var blob = new Blob([ this.DOCTYPE + xml], { type: 'image/svg+xml' }); //var url = window.URL.createObjectURL(blob); // Old method: this failed on IE var url = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(xml))); // Create an IMG tag to hold the new element var img = document.createElement("img"); img.style.display = 'block'; img.onload = function () { var canvas = document.createElement('canvas'); canvas.width = bbox.width; canvas.height = bbox.height; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); var canvasUrl; try { canvasUrl = canvas.toDataURL("image/png"); } catch (e) { canvasUrl = url; } img.onload = null; callback(canvasUrl, img); } img.onerror = function () { callback("", img); } img.setAttribute('src', url); } else { callback("", document.createElement("img")) } } catch (e) { callback("", document.createElement("img")); console.error("PNG image creation not supported!", e); } } /** * Shows a dialog window with the current block workspace encoded as a * downloadable PNG image. */ BlockPyEditor.prototype.copyImage = function () { var dialog = this.main.components.dialog; this.getPngFromBlocks(function (canvasUrl, img) { img.onload = function () { var p = document.createElement('p'); p.textContent = "Right click the image below and choose to copy or save it." var div = document.createElement('div'); div.appendChild(p); div.appendChild(img); dialog.show("Save Blocks As Image", div); }; img.src = canvasUrl; }); }