summaryrefslogtreecommitdiff
path: root/toolkit/devtools/projecteditor/lib/shells.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/devtools/projecteditor/lib/shells.js')
-rw-r--r--toolkit/devtools/projecteditor/lib/shells.js243
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;