diff options
Diffstat (limited to 'toolkit/devtools/projecteditor/lib/shells.js')
-rw-r--r-- | toolkit/devtools/projecteditor/lib/shells.js | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/toolkit/devtools/projecteditor/lib/shells.js b/toolkit/devtools/projecteditor/lib/shells.js new file mode 100644 index 000000000..fd8530b10 --- /dev/null +++ b/toolkit/devtools/projecteditor/lib/shells.js @@ -0,0 +1,243 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { Cu } = require("chrome"); +const { Class } = require("sdk/core/heritage"); +const { EventTarget } = require("sdk/event/target"); +const { emit } = require("sdk/event/core"); +const { EditorTypeForResource } = require("projecteditor/editors"); +const NetworkHelper = require("devtools/toolkit/webconsole/network-helper"); +const promise = require("promise"); + +/** + * The Shell is the object that manages the editor for a single resource. + * It is in charge of selecting the proper Editor (text/image/plugin-defined) + * and instantiating / appending the editor. + * This object is not exported, it is just used internally by the ShellDeck. + * + * This object has a promise `editorAppended`, that will resolve once the editor + * is ready to be used. + */ +var Shell = Class({ + extends: EventTarget, + + /** + * @param ProjectEditor host + * @param Resource resource + */ + initialize: function(host, resource) { + this.host = host; + this.doc = host.document; + this.resource = resource; + this.elt = this.doc.createElement("vbox"); + this.elt.classList.add("view-project-detail"); + this.elt.shell = this; + + let constructor = this._editorTypeForResource(); + + this.editor = constructor(this.host); + this.editor.shell = this; + this.editorAppended = this.editor.appended; + + this.editor.on("load", () => { + this.editorDeferred.resolve(); + }); + this.elt.appendChild(this.editor.elt); + }, + + /** + * Start loading the resource. The 'load' event happens as + * a result of this function, so any listeners to 'editorAppended' + * need to be added before calling this. + */ + load: function() { + this.editorDeferred = promise.defer(); + this.editorLoaded = this.editorDeferred.promise; + this.editor.load(this.resource); + }, + + /** + * Destroy the shell and its associated editor + */ + destroy: function() { + this.editor.destroy(); + this.resource.destroy(); + }, + + /** + * Make sure the correct editor is selected for the resource. + * @returns Type:Editor + */ + _editorTypeForResource: function() { + let resource = this.resource; + let constructor = EditorTypeForResource(resource); + + if (this.host.plugins) { + this.host.plugins.forEach(plugin => { + if (plugin.editorForResource) { + let pluginEditor = plugin.editorForResource(resource); + if (pluginEditor) { + constructor = pluginEditor; + } + } + }); + } + + return constructor; + } +}); + +/** + * The ShellDeck is in charge of managing the list of active Shells for + * the current ProjectEditor instance (aka host). + * + * This object emits the following events: + * - "editor-created": When an editor is initially created + * - "editor-activated": When an editor is ready to use + * - "editor-deactivated": When an editor is ready to use + */ +var ShellDeck = Class({ + extends: EventTarget, + + /** + * @param ProjectEditor host + * @param Document document + */ + initialize: function(host, document) { + this.doc = document; + this.host = host; + this.deck = this.doc.createElement("deck"); + this.deck.setAttribute("flex", "1"); + this.elt = this.deck; + + this.shells = new Map(); + + this._activeShell = null; + }, + + /** + * Open a resource in a Shell. Will create the Shell + * if it doesn't exist yet. + * + * @param Resource resource + * The file to be opened + * @returns Shell + */ + open: function(defaultResource) { + let shell = this.shellFor(defaultResource); + if (!shell) { + shell = this._createShell(defaultResource); + this.shells.set(defaultResource, shell); + } + this.selectShell(shell); + return shell; + }, + + /** + * Create a new Shell for a resource. Called by `open`. + * + * @returns Shell + */ + _createShell: function(defaultResource) { + let shell = Shell(this.host, defaultResource); + + shell.editorAppended.then(() => { + this.shells.set(shell.resource, shell); + emit(this, "editor-created", shell.editor); + if (this.currentShell === shell) { + this.selectShell(shell); + } + + }); + + shell.load(); + this.deck.appendChild(shell.elt); + return shell; + }, + + /** + * Remove the shell for a given resource. + * + * @param Resource resource + */ + removeResource: function(resource) { + let shell = this.shellFor(resource); + if (shell) { + this.shells.delete(resource); + shell.destroy(); + } + }, + + destroy: function() { + for (let [resource, shell] of this.shells.entries()) { + this.shells.delete(resource); + shell.destroy(); + } + }, + + /** + * Select a given shell and open its editor. + * Will fire editor-deactivated on the old selected Shell (if any), + * and editor-activated on the new one once it is ready + * + * @param Shell shell + */ + selectShell: function(shell) { + // Don't fire another activate if this is already the active shell + if (this._activeShell != shell) { + if (this._activeShell) { + emit(this, "editor-deactivated", this._activeShell.editor, this._activeShell.resource); + } + this.deck.selectedPanel = shell.elt; + this._activeShell = shell; + + // Only reload the shell if the editor doesn't have local changes. + if (shell.editor.isClean()) { + shell.load(); + } + shell.editorLoaded.then(() => { + // Handle case where another shell has been requested before this + // one is finished loading. + if (this._activeShell === shell) { + emit(this, "editor-activated", shell.editor, shell.resource); + } + }); + } + }, + + /** + * Find a Shell for a Resource. + * + * @param Resource resource + * @returns Shell + */ + shellFor: function(resource) { + return this.shells.get(resource); + }, + + /** + * The currently active Shell. Note: the editor may not yet be available + * on the current shell. Best to wait for the 'editor-activated' event + * instead. + * + * @returns Shell + */ + get currentShell() { + return this._activeShell; + }, + + /** + * The currently active Editor, or null if it is not ready. + * + * @returns Editor + */ + get currentEditor() { + let shell = this.currentShell; + return shell ? shell.editor : null; + }, + +}); +exports.ShellDeck = ShellDeck; |