define(function(require, exports, module) { main.consumes = [ "editors", "ui", "settings", "tabManager", "ace", "menus", "commands", "console", "ace.status" ]; main.provides = ["immediate"]; return main; function main(options, imports, register) { var editors = imports.editors; var tabManager = imports.tabManager; var ui = imports.ui; var menus = imports.menus; var commands = imports.commands; var c9console = imports.console; var aceHandle = imports.ace; var aceStatus = imports["ace.status"]; var Repl = require("plugins/c9.ide.ace.repl/repl").Repl; /***** Initialization *****/ var extensions = []; var handle = editors.register("immediate", "Immediate Window", Immediate, extensions); var emit = handle.getEmitter(); var replTypes = {}; //Shared across Immediate windows var theme, defaultEvaluator; handle.on("load", function() { handle.addElement( tabManager.getElement("mnuEditors").appendChild( new ui.item({ caption: "New Immediate Window", onclick: function(e) { tabManager.open({ active: true, pane: this.parentNode.pane, editorType: "immediate" }, function() {}); } }) ) ); menus.addItemByPath("Window/New Immediate Window", new ui.item({ onclick: function() { tabManager.open({ active: true, pane: this.parentNode.pane, editorType: "immediate" }, function() {}); } }), 31, handle); commands.addCommand({ name: "showimmediate", group: "Panels", exec: function (editor, args) { // Search for the output pane if (search(done)) return; // If not found show the console c9console.show(); // Search again if (search(done)) return; // Else open the output panel in the console tabManager.open({ editorType: "immediate", active: true, pane: c9console.getPanes()[0], }, done); function done(err, tab) { if (args && args.evaluator) tab.editor.setActiveEvaluator(args.evaluator); } } }, handle); // Insert some CSS ui.insertCss(require("text!./style.css"), null, handle); }); // Search through pages function search(cb) { return !tabManager.getTabs().every(function(tab) { if (tab.editorType == "immediate") { tabManager.focusTab(tab); if (cb) cb(null, tab); return false; } return true; }); } function Immediate() { var Baseclass = editors.findEditor("ace"); var plugin = new Baseclass(true, [], main.consumes); // var emit = plugin.getEmitter(); var ddType, btnClear, ace, menu, statusbar; plugin.on("draw", function(e) { aceStatus.draw(); // Create UI elements statusbar = e.tab.appendChild(new ui.bar({ skin: "bar-status", skinset: "c9statusbar", zindex: 10000, height: 23, style: "position:absolute;bottom:3px;right:5px;" })); menu = new ui.menu({ htmlNode: document.body, render: "runtime", class: "mnuSbPrefs" }); plugin.addElement(menu); ddType = ui.insertByIndex(statusbar, new ui.button({ skin: "label", style: "cursor:pointer", margin: "1 7 0 0", submenudir: "up", submenu: menu }), 1000, plugin); btnClear = ui.insertByIndex(statusbar, new ui.button({ margin: "0 3 0 0", skin: "btn_console", skinset: "c9statusbar", class: "clear", submenudir: "up" }), 100000, plugin); ace = plugin.ace; ace.setOption("printMargin", false); ace.setOption("scrollPastEnd", 0); ace.setOption("showFoldWidgets", false); ace.setOption("highlightActiveLine", false); ace.setOption("highlightGutterLine", false); ace.renderer.setScrollMargin(0, 10); commands.addCommand({ bindKey: "Ctrl-C", name: "abortimmediateexpression", isAvailable: function() { var tab = tabManager.focussedTab; if (tab && tab.editorType == "immediate" && tab.editor && tab.editor.ace) return tab.editor.ace.selection.isEmpty() && tab.editor.ace.isFocused(); }, exec: function() { abort(); } }, plugin); e.htmlNode.className += " immediate"; // Allow Selection (APF) e.tab.textselect = true; ddType.on("afterchange", function() { if (currentDocument) currentDocument.getSession().changeType(ddType.selectedType); }); btnClear.on("click", function() { plugin.clear(); btnClear.blur(); }); for (var type in replTypes) { var t = replTypes[type]; addType(t.caption, type, t.plugin); } handle.on("addEvaluator", function(e) { addType(e.caption, e.id, e.plugin); }); menu.on("itemclick", function(e) { var value = e.relatedNode.getAttribute("value"); ddType.setType(value); }); function update(e) { if (e && !e.value) return; var items = menu.childNodes; var value = ddType.selectedType || items[0].getAttribute("value"); var selectedItem; items.forEach(function(item) { var selected = item.getAttribute("value") == value ? true : false; if (selected) selectedItem = item; item.setAttribute("selected", selected); }); ddType.setType(value); } ddType.setType = function (type) { if (type == ddType.selectedType) return; var caption = replTypes[type] ? replTypes[type].caption : type + " (Unknown)"; ddType.selectedType = type; ddType.setAttribute("caption", caption); ddType.dispatchEvent("afterchange"); }; function setTheme(e) { theme = e.theme; if (!theme) return; btnClear.parentNode.$ext.className = "bar-status " + (theme.isDark ? "ace_dark" : ""); } aceHandle.on("themeChange", setTheme, plugin); setTheme({ theme: aceHandle.theme }); menu.on("prop.visible", update); update(); }); /***** Method *****/ function addType(caption, value, plugin) { var item = menu.appendChild(new ui.item({ caption: caption, value: value, type: "radio" })); if (ddType.selectedType == value) ddType.setAttribute("caption", caption); plugin.addOther(function() { if (item.parentNode) item.parentNode.removeChild(item); }); } function setActiveEvaluator(value) { ddType.setType(value); } function getActiveEvaluator() { return ddType.selectedType; } function abort() { var session = currentDocument.getSession(); if (session.repl.evaluator.abort) session.repl.evaluator.abort(); } // Set the tab in loading state - later this could be the output block // currentDocument.tab.classList.add("loading"); // settings.save(); /***** Lifecycle *****/ plugin.on("load", function() { }); var currentDocument; plugin.on("documentLoad", function(e) { var doc = e.doc; var session = doc.getSession(); doc.undoManager.on("change", function(e) { if (!doc.undoManager.isAtBookmark()) doc.undoManager.bookmark(); }); doc.tooltip = doc.title = "Immediate"; if (session.repl) return; session.changeType = function(type, noMessage) { if (session.repl && session.repl.type == type) return; handle.findEvaluator(type, function(type, evaluator) { session.type = type; if (!session.repl) { session.repl = new Repl(session.session, { mode: evaluator.mode, evaluator: evaluator, message: evaluator.message }); if (currentDocument && currentDocument.getSession() == session) session.repl.attach(ace); } session.repl.type = type; session.repl.clear(); if (!noMessage) { var cell = session.repl.insertCell( { row: 0, column: 0 }, { type: "comment" }, true); cell.setValue(evaluator.message + "\n"); } if (session.repl.evaluator && session.repl.evaluator != evaluator) session.repl.evaluator.cleanUp("elements"); session.repl.ensureLastInputCell(); session.repl.setEvaluator(evaluator); session.repl.session.setMode(evaluator.mode); evaluator.draw(statusbar); doc.tooltip = doc.title = "Immediate (" + evaluator.caption + ")"; }); }; doc.value = ""; session.changeType(session.type || defaultEvaluator || ddType.selectedType); }); plugin.on("documentActivate", function(e) { currentDocument = e.doc; var session = e.doc.getSession(); if (session.type) { ddType.setType(session.type); } if (session.repl) session.repl.attach(ace); }); plugin.on("documentUnload", function(e) { var session = e.doc.getSession(); if (session.repl) session.repl.detach(); // TODO: this breaks moving repl between splits // delete session.repl; }); plugin.on("getState", function(e) { var session = e.doc.getSession(); if (!session.repl || e.filter) return; e.state.type = session.type; var data = session.repl.history._data; var pos = session.repl.history.position; if (data.length > 1000) data = data.slice(-500); e.state.history = data; e.state.pos = pos; }); plugin.on("setState", function(e) { if (e.state.type) { var session = e.doc.getSession(); session.type = e.state.type; session.repl.history._data = e.state.history || []; if (typeof e.state.pos === "number") session.repl.history.position = e.state.pos; if (e.doc == currentDocument) ddType.setType(e.state.type); } }); plugin.on("clear", function() { if (currentDocument) { plugin.focus(); } }); plugin.on("focus", function() { }); plugin.on("enable", function() { }); plugin.on("disable", function() { }); plugin.on("unload", function() { }); /***** Register and define API *****/ /** * Immediate Pane for Cloud9 * @class immediate.Immediate * @extends Editor */ /** * The type of editor. Use this to create an immediate pane using * {@link tabManager#openEditor} or {@link editors#createEditor}. * @property {"immediate"} type * @readonly */ plugin.freezePublicAPI({ /** * */ get statusbar() { return statusbar; }, /** * */ setActiveEvaluator: setActiveEvaluator, /** * */ getActiveEvaluator: getActiveEvaluator, /** * @ignore This is here to overwrite default behavior */ isClipboardAvailable: function(e) { return !e.fromKeyboard; } }); plugin.load(null, "immediate"); return plugin; } /** * The immediate handle, provides an API for adding * {@link Evaluator evaluators} to the immediate panes. * An evaluator is a plugin that can take the expressions from the * multi-line REPL and return resuls. The results can be * rendered as HTML and are fully interactive. * * This is the object you get when you request the immediate service * in your plugin. * * Example: * * define(function(require, exports, module) { * main.consumes = ["immediate", "Plugin"]; * main.provides = ["myplugin"]; * return main; * * function main(options, imports, register) { * var immediate = imports.immediate; * var plugin = new imports.Plugin("Your Name", main.consumes); * * plugin.on("load", function(){ * var evaluator = { * mode : "ace/mode/go", * message : "", * canEvaluate : function(str) { return str.trim() ? true : false; }, * evaluate : function(expression, cell, done) { * * executeCommand(expression, function(result) { * cell.addWidget({ * html : "