summaryrefslogtreecommitdiffstats
path: root/devtools/client/projecteditor/lib/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/projecteditor/lib/plugins')
-rw-r--r--devtools/client/projecteditor/lib/plugins/app-manager/app-project-editor.js56
-rw-r--r--devtools/client/projecteditor/lib/plugins/app-manager/moz.build10
-rw-r--r--devtools/client/projecteditor/lib/plugins/app-manager/plugin.js77
-rw-r--r--devtools/client/projecteditor/lib/plugins/core.js83
-rw-r--r--devtools/client/projecteditor/lib/plugins/delete/delete.js67
-rw-r--r--devtools/client/projecteditor/lib/plugins/delete/moz.build9
-rw-r--r--devtools/client/projecteditor/lib/plugins/dirty/dirty.js47
-rw-r--r--devtools/client/projecteditor/lib/plugins/dirty/moz.build9
-rw-r--r--devtools/client/projecteditor/lib/plugins/image-view/image-editor.js50
-rw-r--r--devtools/client/projecteditor/lib/plugins/image-view/moz.build10
-rw-r--r--devtools/client/projecteditor/lib/plugins/image-view/plugin.js28
-rw-r--r--devtools/client/projecteditor/lib/plugins/logging/logging.js29
-rw-r--r--devtools/client/projecteditor/lib/plugins/logging/moz.build9
-rw-r--r--devtools/client/projecteditor/lib/plugins/moz.build21
-rw-r--r--devtools/client/projecteditor/lib/plugins/new/moz.build9
-rw-r--r--devtools/client/projecteditor/lib/plugins/new/new.js80
-rw-r--r--devtools/client/projecteditor/lib/plugins/rename/moz.build9
-rw-r--r--devtools/client/projecteditor/lib/plugins/rename/rename.js74
-rw-r--r--devtools/client/projecteditor/lib/plugins/save/moz.build9
-rw-r--r--devtools/client/projecteditor/lib/plugins/save/save.js93
-rw-r--r--devtools/client/projecteditor/lib/plugins/status-bar/moz.build9
-rw-r--r--devtools/client/projecteditor/lib/plugins/status-bar/plugin.js105
22 files changed, 893 insertions, 0 deletions
diff --git a/devtools/client/projecteditor/lib/plugins/app-manager/app-project-editor.js b/devtools/client/projecteditor/lib/plugins/app-manager/app-project-editor.js
new file mode 100644
index 000000000..9a66770b0
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/app-manager/app-project-editor.js
@@ -0,0 +1,56 @@
+/* -*- 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 promise = require("promise");
+const { ItchEditor } = require("devtools/client/projecteditor/lib/editors");
+
+var AppProjectEditor = Class({
+ extends: ItchEditor,
+
+ hidesToolbar: true,
+
+ initialize: function (host) {
+ ItchEditor.prototype.initialize.apply(this, arguments);
+ this.appended = promise.resolve();
+ this.host = host;
+ this.label = "app-manager";
+ },
+
+ destroy: function () {
+ this.elt.remove();
+ this.elt = null;
+ },
+
+ load: function (resource) {
+ let {appManagerOpts} = this.host.project;
+
+ // Only load the frame the first time it is selected
+ if (!this.iframe || this.iframe.getAttribute("src") !== appManagerOpts.projectOverviewURL) {
+
+ this.elt.textContent = "";
+ let iframe = this.iframe = this.elt.ownerDocument.createElement("iframe");
+ let iframeLoaded = this.iframeLoaded = promise.defer();
+
+ iframe.addEventListener("load", function onLoad() {
+ iframe.removeEventListener("load", onLoad);
+ iframeLoaded.resolve();
+ });
+
+ iframe.setAttribute("flex", "1");
+ iframe.setAttribute("src", appManagerOpts.projectOverviewURL);
+ this.elt.appendChild(iframe);
+
+ }
+
+ promise.all([this.iframeLoaded.promise, this.appended]).then(() => {
+ this.emit("load");
+ });
+ }
+});
+
+exports.AppProjectEditor = AppProjectEditor;
diff --git a/devtools/client/projecteditor/lib/plugins/app-manager/moz.build b/devtools/client/projecteditor/lib/plugins/app-manager/moz.build
new file mode 100644
index 000000000..8aae52725
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/app-manager/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'app-project-editor.js',
+ 'plugin.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/app-manager/plugin.js b/devtools/client/projecteditor/lib/plugins/app-manager/plugin.js
new file mode 100644
index 000000000..82bbab34b
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/app-manager/plugin.js
@@ -0,0 +1,77 @@
+const { Cu } = require("chrome");
+const { Class } = require("sdk/core/heritage");
+const { EventTarget } = require("sdk/event/target");
+const { emit } = require("sdk/event/core");
+const promise = require("promise");
+var { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+const { AppProjectEditor } = require("./app-project-editor");
+const OPTION_URL = "chrome://devtools/skin/images/tool-options.svg";
+const Services = require("Services");
+const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
+
+var AppManagerRenderer = Class({
+ extends: Plugin,
+
+ isAppManagerProject: function () {
+ return !!this.host.project.appManagerOpts;
+ },
+ editorForResource: function (resource) {
+ if (!resource.parent && this.isAppManagerProject()) {
+ return AppProjectEditor;
+ }
+ },
+ getUI: function (parent) {
+ let doc = parent.ownerDocument;
+ if (parent.childElementCount == 0) {
+ let image = doc.createElement("image");
+ let optionImage = doc.createElement("image");
+ let flexElement = doc.createElement("div");
+ let nameLabel = doc.createElement("span");
+ let statusElement = doc.createElement("div");
+
+ image.className = "project-image";
+ optionImage.className = "project-options";
+ optionImage.setAttribute("src", OPTION_URL);
+ nameLabel.className = "project-name-label";
+ statusElement.className = "project-status";
+ flexElement.className = "project-flex";
+
+ parent.appendChild(image);
+ parent.appendChild(nameLabel);
+ parent.appendChild(flexElement);
+ parent.appendChild(statusElement);
+ parent.appendChild(optionImage);
+ }
+
+ return {
+ image: parent.querySelector(".project-image"),
+ nameLabel: parent.querySelector(".project-name-label"),
+ statusElement: parent.querySelector(".project-status")
+ };
+ },
+ onAnnotate: function (resource, editor, elt) {
+ if (resource.parent || !this.isAppManagerProject()) {
+ return;
+ }
+
+ let {appManagerOpts} = this.host.project;
+ let doc = elt.ownerDocument;
+
+ let {image, nameLabel, statusElement} = this.getUI(elt);
+ let name = appManagerOpts.name || resource.basename;
+ let url = appManagerOpts.iconUrl || "icon-sample.png";
+ let status = appManagerOpts.validationStatus || "unknown";
+ let tooltip = Strings.formatStringFromName("status_tooltip",
+ [Strings.GetStringFromName("status_" + status)], 1);
+
+ nameLabel.textContent = name;
+ image.setAttribute("src", url);
+ statusElement.setAttribute("status", status);
+ statusElement.setAttribute("tooltiptext", tooltip);
+
+ return true;
+ }
+});
+
+exports.AppManagerRenderer = AppManagerRenderer;
+registerPlugin(AppManagerRenderer);
diff --git a/devtools/client/projecteditor/lib/plugins/core.js b/devtools/client/projecteditor/lib/plugins/core.js
new file mode 100644
index 000000000..933eda043
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/core.js
@@ -0,0 +1,83 @@
+/* -*- 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/. */
+
+// This is the core plugin API.
+
+const { Class } = require("sdk/core/heritage");
+
+var Plugin = Class({
+ initialize: function (host) {
+ this.host = host;
+ this.init(host);
+ },
+
+ destroy: function (host) { },
+
+ init: function (host) {},
+
+ showForCategories: function (elt, categories) {
+ this._showFor = this._showFor || [];
+ let set = new Set(categories);
+ this._showFor.push({
+ elt: elt,
+ categories: new Set(categories)
+ });
+ if (this.host.currentEditor) {
+ this.onEditorActivated(this.host.currentEditor);
+ } else {
+ elt.classList.add("plugin-hidden");
+ }
+ },
+
+ priv: function (item) {
+ if (!this._privData) {
+ this._privData = new WeakMap();
+ }
+ if (!this._privData.has(item)) {
+ this._privData.set(item, {});
+ }
+ return this._privData.get(item);
+ },
+ onTreeSelected: function (resource) {},
+
+
+ // Editor state lifetime...
+ onEditorCreated: function (editor) {},
+ onEditorDestroyed: function (editor) {},
+
+ onEditorActivated: function (editor) {
+ if (this._showFor) {
+ let category = editor.category;
+ for (let item of this._showFor) {
+ if (item.categories.has(category)) {
+ item.elt.classList.remove("plugin-hidden");
+ } else {
+ item.elt.classList.add("plugin-hidden");
+ }
+ }
+ }
+ },
+ onEditorDeactivated: function (editor) {
+ if (this._showFor) {
+ for (let item of this._showFor) {
+ item.elt.classList.add("plugin-hidden");
+ }
+ }
+ },
+
+ onEditorLoad: function (editor) {},
+ onEditorSave: function (editor) {},
+ onEditorChange: function (editor) {},
+ onEditorCursorActivity: function (editor) {},
+});
+exports.Plugin = Plugin;
+
+function registerPlugin(constr) {
+ exports.registeredPlugins.push(constr);
+}
+exports.registerPlugin = registerPlugin;
+
+exports.registeredPlugins = [];
diff --git a/devtools/client/projecteditor/lib/plugins/delete/delete.js b/devtools/client/projecteditor/lib/plugins/delete/delete.js
new file mode 100644
index 000000000..b28d6a0ef
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/delete/delete.js
@@ -0,0 +1,67 @@
+/* -*- 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 { Class } = require("sdk/core/heritage");
+const { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+const { confirm } = require("devtools/client/projecteditor/lib/helpers/prompts");
+const { getLocalizedString } = require("devtools/client/projecteditor/lib/helpers/l10n");
+
+var DeletePlugin = Class({
+ extends: Plugin,
+ shouldConfirm: true,
+
+ init: function (host) {
+ this.host.addCommand(this, {
+ id: "cmd-delete"
+ });
+ this.contextMenuItem = this.host.createMenuItem({
+ parent: this.host.contextMenuPopup,
+ label: getLocalizedString("projecteditor.deleteLabel"),
+ command: "cmd-delete"
+ });
+ },
+
+ confirmDelete: function (resource) {
+ let deletePromptMessage = resource.isDir ?
+ getLocalizedString("projecteditor.deleteFolderPromptMessage") :
+ getLocalizedString("projecteditor.deleteFilePromptMessage");
+ return !this.shouldConfirm || confirm(
+ getLocalizedString("projecteditor.deletePromptTitle"),
+ deletePromptMessage
+ );
+ },
+
+ onContextMenuOpen: function (resource) {
+ // Do not allow deletion of the top level items in the tree. In the
+ // case of the Web IDE in particular this can leave the UI in a weird
+ // state. If we'd like to add ability to delete the project folder from
+ // the tree in the future, then the UI could be cleaned up by listening
+ // to the ProjectTree's "resource-removed" event.
+ if (!resource.parent) {
+ this.contextMenuItem.setAttribute("hidden", "true");
+ } else {
+ this.contextMenuItem.removeAttribute("hidden");
+ }
+ },
+
+ onCommand: function (cmd) {
+ if (cmd === "cmd-delete") {
+ let tree = this.host.projectTree;
+ let resource = tree.getSelectedResource();
+
+ if (!this.confirmDelete(resource)) {
+ return;
+ }
+
+ resource.delete().then(() => {
+ this.host.project.refresh();
+ });
+ }
+ }
+});
+
+exports.DeletePlugin = DeletePlugin;
+registerPlugin(DeletePlugin);
diff --git a/devtools/client/projecteditor/lib/plugins/delete/moz.build b/devtools/client/projecteditor/lib/plugins/delete/moz.build
new file mode 100644
index 000000000..4b1d00466
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/delete/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'delete.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/dirty/dirty.js b/devtools/client/projecteditor/lib/plugins/dirty/dirty.js
new file mode 100644
index 000000000..f976c626f
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/dirty/dirty.js
@@ -0,0 +1,47 @@
+/* -*- 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 { Class } = require("sdk/core/heritage");
+const { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+const { emit } = require("sdk/event/core");
+
+var DirtyPlugin = Class({
+ extends: Plugin,
+
+ onEditorSave: function (editor) { this.onEditorChange(editor); },
+ onEditorLoad: function (editor) { this.onEditorChange(editor); },
+
+ onEditorChange: function (editor) {
+ // Only run on a TextEditor
+ if (!editor || !editor.editor) {
+ return;
+ }
+
+ // Dont' force a refresh unless the dirty state has changed...
+ let priv = this.priv(editor);
+ let clean = editor.isClean();
+ if (priv.isClean !== clean) {
+ let resource = editor.shell.resource;
+ emit(resource, "label-change", resource);
+ priv.isClean = clean;
+ }
+ },
+
+ onAnnotate: function (resource, editor, elt) {
+ // Only run on a TextEditor
+ if (!editor || !editor.editor) {
+ return;
+ }
+
+ if (!editor.isClean()) {
+ elt.textContent = "*" + resource.displayName;
+ return true;
+ }
+ }
+});
+exports.DirtyPlugin = DirtyPlugin;
+
+registerPlugin(DirtyPlugin);
diff --git a/devtools/client/projecteditor/lib/plugins/dirty/moz.build b/devtools/client/projecteditor/lib/plugins/dirty/moz.build
new file mode 100644
index 000000000..b86c5a9af
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/dirty/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'dirty.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/image-view/image-editor.js b/devtools/client/projecteditor/lib/plugins/image-view/image-editor.js
new file mode 100644
index 000000000..668fcbeb2
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/image-view/image-editor.js
@@ -0,0 +1,50 @@
+/* -*- 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 promise = require("promise");
+const { ItchEditor } = require("devtools/client/projecteditor/lib/editors");
+
+var ImageEditor = Class({
+ extends: ItchEditor,
+
+ initialize: function () {
+ ItchEditor.prototype.initialize.apply(this, arguments);
+ this.label = "image";
+ this.appended = promise.resolve();
+ },
+
+ load: function (resource) {
+ this.elt.innerHTML = "";
+ let image = this.image = this.doc.createElement("image");
+ image.className = "editor-image";
+ image.setAttribute("src", resource.uri);
+
+ let box1 = this.doc.createElement("box");
+ box1.appendChild(image);
+
+ let box2 = this.doc.createElement("box");
+ box2.setAttribute("flex", 1);
+
+ this.elt.appendChild(box1);
+ this.elt.appendChild(box2);
+
+ this.appended.then(() => {
+ this.emit("load");
+ });
+ },
+
+ destroy: function () {
+ if (this.image) {
+ this.image.remove();
+ this.image = null;
+ }
+ }
+
+});
+
+exports.ImageEditor = ImageEditor;
diff --git a/devtools/client/projecteditor/lib/plugins/image-view/moz.build b/devtools/client/projecteditor/lib/plugins/image-view/moz.build
new file mode 100644
index 000000000..d67370e5b
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/image-view/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'image-editor.js',
+ 'plugin.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/image-view/plugin.js b/devtools/client/projecteditor/lib/plugins/image-view/plugin.js
new file mode 100644
index 000000000..626ea3c9a
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/image-view/plugin.js
@@ -0,0 +1,28 @@
+/* -*- 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 promise = require("promise");
+const { ImageEditor } = require("./image-editor");
+const { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+
+var ImageEditorPlugin = Class({
+ extends: Plugin,
+
+ editorForResource: function (node) {
+ if (node.contentCategory === "image") {
+ return ImageEditor;
+ }
+ },
+
+ init: function (host) {
+
+ }
+});
+
+exports.ImageEditorPlugin = ImageEditorPlugin;
+registerPlugin(ImageEditorPlugin);
diff --git a/devtools/client/projecteditor/lib/plugins/logging/logging.js b/devtools/client/projecteditor/lib/plugins/logging/logging.js
new file mode 100644
index 000000000..cd5757b72
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/logging/logging.js
@@ -0,0 +1,29 @@
+/* -*- 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/. */
+
+var { Class } = require("sdk/core/heritage");
+var { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+
+var LoggingPlugin = Class({
+ extends: Plugin,
+
+ // Editor state lifetime...
+ onEditorCreated: function (editor) { console.log("editor created: " + editor); },
+ onEditorDestroyed: function (editor) { console.log("editor destroyed: " + editor);},
+
+ onEditorSave: function (editor) { console.log("editor saved: " + editor); },
+ onEditorLoad: function (editor) { console.log("editor loaded: " + editor); },
+
+ onEditorActivated: function (editor) { console.log("editor activated: " + editor);},
+ onEditorDeactivated: function (editor) { console.log("editor deactivated: " + editor);},
+
+ onEditorChange: function (editor) { console.log("editor changed: " + editor);},
+
+ onCommand: function (cmd) { console.log("Command: " + cmd); }
+});
+exports.LoggingPlugin = LoggingPlugin;
+
+registerPlugin(LoggingPlugin);
diff --git a/devtools/client/projecteditor/lib/plugins/logging/moz.build b/devtools/client/projecteditor/lib/plugins/logging/moz.build
new file mode 100644
index 000000000..5d8d98fbe
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/logging/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'logging.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/moz.build b/devtools/client/projecteditor/lib/plugins/moz.build
new file mode 100644
index 000000000..17bff7ce0
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ 'app-manager',
+ 'delete',
+ 'dirty',
+ 'image-view',
+ 'logging',
+ 'new',
+ 'rename',
+ 'save',
+ 'status-bar',
+]
+
+DevToolsModules(
+ 'core.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/new/moz.build b/devtools/client/projecteditor/lib/plugins/new/moz.build
new file mode 100644
index 000000000..3caacefb1
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/new/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'new.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/new/new.js b/devtools/client/projecteditor/lib/plugins/new/new.js
new file mode 100644
index 000000000..220cb4977
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/new/new.js
@@ -0,0 +1,80 @@
+/* -*- 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 { Class } = require("sdk/core/heritage");
+const { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+const { getLocalizedString } = require("devtools/client/projecteditor/lib/helpers/l10n");
+
+// Handles the new command.
+var NewFile = Class({
+ extends: Plugin,
+
+ init: function () {
+ this.command = this.host.addCommand(this, {
+ id: "cmd-new",
+ key: getLocalizedString("projecteditor.new.commandkey"),
+ modifiers: "accel"
+ });
+ this.host.createMenuItem({
+ parent: this.host.fileMenuPopup,
+ label: getLocalizedString("projecteditor.newLabel"),
+ command: "cmd-new",
+ key: "key_cmd-new"
+ });
+ this.host.createMenuItem({
+ parent: this.host.contextMenuPopup,
+ label: getLocalizedString("projecteditor.newLabel"),
+ command: "cmd-new"
+ });
+ },
+
+ onCommand: function (cmd) {
+ if (cmd === "cmd-new") {
+ let tree = this.host.projectTree;
+ let resource = tree.getSelectedResource();
+ parent = resource.isDir ? resource : resource.parent;
+ sibling = resource.isDir ? null : resource;
+
+ if (!("createChild" in parent)) {
+ return;
+ }
+
+ let extension = sibling ? sibling.contentCategory : parent.store.defaultCategory;
+ let template = "untitled{1}." + extension;
+ let name = this.suggestName(parent, template);
+
+ tree.promptNew(name, parent, sibling).then(name => {
+
+ // XXX: sanitize bad file names.
+
+ // If the name is already taken, just add/increment a number.
+ if (parent.hasChild(name)) {
+ let matches = name.match(/([^\d.]*)(\d*)([^.]*)(.*)/);
+ template = matches[1] + "{1}" + matches[3] + matches[4];
+ name = this.suggestName(parent, template, parseInt(matches[2]) || 2);
+ }
+
+ return parent.createChild(name);
+ }).then(resource => {
+ tree.selectResource(resource);
+ this.host.currentEditor.focus();
+ }).then(null, console.error);
+ }
+ },
+
+ suggestName: function (parent, template, start = 1) {
+ let i = start;
+ let name;
+ do {
+ name = template.replace("\{1\}", i === 1 ? "" : i);
+ i++;
+ } while (parent.hasChild(name));
+
+ return name;
+ }
+});
+exports.NewFile = NewFile;
+registerPlugin(NewFile);
diff --git a/devtools/client/projecteditor/lib/plugins/rename/moz.build b/devtools/client/projecteditor/lib/plugins/rename/moz.build
new file mode 100644
index 000000000..2b1612452
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/rename/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'rename.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/rename/rename.js b/devtools/client/projecteditor/lib/plugins/rename/rename.js
new file mode 100644
index 000000000..850401869
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/rename/rename.js
@@ -0,0 +1,74 @@
+/* -*- 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 { Class } = require("sdk/core/heritage");
+const { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+const { getLocalizedString } = require("devtools/client/projecteditor/lib/helpers/l10n");
+
+var RenamePlugin = Class({
+ extends: Plugin,
+
+ init: function (host) {
+ this.host.addCommand(this, {
+ id: "cmd-rename"
+ });
+ this.contextMenuItem = this.host.createMenuItem({
+ parent: this.host.contextMenuPopup,
+ label: getLocalizedString("projecteditor.renameLabel"),
+ command: "cmd-rename"
+ });
+ },
+
+ onContextMenuOpen: function (resource) {
+ if (resource.isRoot) {
+ this.contextMenuItem.setAttribute("hidden", "true");
+ } else {
+ this.contextMenuItem.removeAttribute("hidden");
+ }
+ },
+
+ onCommand: function (cmd) {
+ if (cmd === "cmd-rename") {
+ let tree = this.host.projectTree;
+ let resource = tree.getSelectedResource();
+ let parent = resource.parent;
+ let oldName = resource.basename;
+
+ tree.promptEdit(oldName, resource).then(name => {
+ if (name === oldName) {
+ return resource;
+ }
+ if (parent.hasChild(name)) {
+ let matches = name.match(/([^\d.]*)(\d*)([^.]*)(.*)/);
+ let template = matches[1] + "{1}" + matches[3] + matches[4];
+ name = this.suggestName(resource, template, parseInt(matches[2]) || 2);
+ }
+ return parent.rename(oldName, name);
+ }).then(resource => {
+ this.host.project.refresh();
+ tree.selectResource(resource);
+ if (!resource.isDir) {
+ this.host.currentEditor.focus();
+ }
+ }).then(null, console.error);
+ }
+ },
+
+ suggestName: function (resource, template, start = 1) {
+ let i = start;
+ let name;
+ let parent = resource.parent;
+ do {
+ name = template.replace("\{1\}", i === 1 ? "" : i);
+ i++;
+ } while (parent.hasChild(name));
+
+ return name;
+ }
+});
+
+exports.RenamePlugin = RenamePlugin;
+registerPlugin(RenamePlugin);
diff --git a/devtools/client/projecteditor/lib/plugins/save/moz.build b/devtools/client/projecteditor/lib/plugins/save/moz.build
new file mode 100644
index 000000000..66df054eb
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/save/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'save.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/save/save.js b/devtools/client/projecteditor/lib/plugins/save/save.js
new file mode 100644
index 000000000..43b2185d2
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/save/save.js
@@ -0,0 +1,93 @@
+/* -*- 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 { Class } = require("sdk/core/heritage");
+const { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+const picker = require("devtools/client/projecteditor/lib/helpers/file-picker");
+const { getLocalizedString } = require("devtools/client/projecteditor/lib/helpers/l10n");
+
+// Handles the save command.
+var SavePlugin = Class({
+ extends: Plugin,
+
+ init: function (host) {
+
+ this.host.addCommand(this, {
+ id: "cmd-save",
+ key: getLocalizedString("projecteditor.save.commandkey"),
+ modifiers: "accel"
+ });
+ this.host.addCommand(this, {
+ id: "cmd-saveas",
+ key: getLocalizedString("projecteditor.save.commandkey"),
+ modifiers: "accel shift"
+ });
+ this.host.createMenuItem({
+ parent: this.host.fileMenuPopup,
+ label: getLocalizedString("projecteditor.saveLabel"),
+ command: "cmd-save",
+ key: "key_cmd-save"
+ });
+ this.host.createMenuItem({
+ parent: this.host.fileMenuPopup,
+ label: getLocalizedString("projecteditor.saveAsLabel"),
+ command: "cmd-saveas",
+ key: "key_cmd-saveas"
+ });
+ },
+
+ isCommandEnabled: function (cmd) {
+ let currentEditor = this.host.currentEditor;
+ return currentEditor.isEditable;
+ },
+
+ onCommand: function (cmd) {
+ if (cmd === "cmd-save") {
+ this.onEditorSaveRequested();
+ } else if (cmd === "cmd-saveas") {
+ this.saveAs();
+ }
+ },
+
+ saveAs: function () {
+ let editor = this.host.currentEditor;
+ let project = this.host.resourceFor(editor);
+
+ let resource;
+ picker.showSave({
+ window: this.host.window,
+ directory: project && project.parent ? project.parent.path : null,
+ defaultName: project ? project.basename : null,
+ }).then(path => {
+ return this.createResource(path);
+ }).then(res => {
+ resource = res;
+ return this.saveResource(editor, resource);
+ }).then(() => {
+ this.host.openResource(resource);
+ }).then(null, console.error);
+ },
+
+ onEditorSaveRequested: function () {
+ let editor = this.host.currentEditor;
+ let resource = this.host.resourceFor(editor);
+ if (!resource) {
+ return this.saveAs();
+ }
+
+ return this.saveResource(editor, resource);
+ },
+
+ createResource: function (path) {
+ return this.host.project.resourceFor(path, { create: true });
+ },
+
+ saveResource: function (editor, resource) {
+ return editor.save(resource);
+ }
+});
+exports.SavePlugin = SavePlugin;
+registerPlugin(SavePlugin);
diff --git a/devtools/client/projecteditor/lib/plugins/status-bar/moz.build b/devtools/client/projecteditor/lib/plugins/status-bar/moz.build
new file mode 100644
index 000000000..87ce21584
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/status-bar/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ 'plugin.js',
+)
diff --git a/devtools/client/projecteditor/lib/plugins/status-bar/plugin.js b/devtools/client/projecteditor/lib/plugins/status-bar/plugin.js
new file mode 100644
index 000000000..9450baef3
--- /dev/null
+++ b/devtools/client/projecteditor/lib/plugins/status-bar/plugin.js
@@ -0,0 +1,105 @@
+/* -*- 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 promise = require("promise");
+const { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
+
+/**
+ * Print information about the currently opened file
+ * and the state of the current editor
+ */
+var StatusBarPlugin = Class({
+ extends: Plugin,
+
+ init: function () {
+ this.box = this.host.createElement("hbox", {
+ parent: "#projecteditor-toolbar-bottom"
+ });
+
+ this.activeMode = this.host.createElement("label", {
+ parent: this.box,
+ class: "projecteditor-basic-display"
+ });
+
+ this.cursorPosition = this.host.createElement("label", {
+ parent: this.box,
+ class: "projecteditor-basic-display"
+ });
+
+ this.fileLabel = this.host.createElement("label", {
+ parent: "#plugin-toolbar-left",
+ class: "projecteditor-file-label"
+ });
+ },
+
+ destroy: function () {
+ },
+
+ /**
+ * Print information about the current state of the editor
+ *
+ * @param Editor editor
+ */
+ render: function (editor, resource) {
+ if (!resource || resource.isDir) {
+ this.fileLabel.textContent = "";
+ this.cursorPosition.value = "";
+ return;
+ }
+
+ this.fileLabel.textContent = resource.basename;
+ this.activeMode.value = editor.toString();
+ if (editor.editor) {
+ let cursorStart = editor.editor.getCursor("start");
+ let cursorEnd = editor.editor.getCursor("end");
+ if (cursorStart.line === cursorEnd.line && cursorStart.ch === cursorEnd.ch) {
+ this.cursorPosition.value = cursorStart.line + " " + cursorStart.ch;
+ } else {
+ this.cursorPosition.value = cursorStart.line + " " + cursorStart.ch + " | " +
+ cursorEnd.line + " " + cursorEnd.ch;
+ }
+ } else {
+ this.cursorPosition.value = "";
+ }
+ },
+
+
+ /**
+ * Print the current file name
+ *
+ * @param Resource resource
+ */
+ onTreeSelected: function (resource) {
+ if (!resource || resource.isDir) {
+ this.fileLabel.textContent = "";
+ return;
+ }
+ this.fileLabel.textContent = resource.basename;
+ },
+
+ onEditorDeactivated: function (editor) {
+ this.fileLabel.textContent = "";
+ this.cursorPosition.value = "";
+ },
+
+ onEditorChange: function (editor, resource) {
+ this.render(editor, resource);
+ },
+
+ onEditorCursorActivity: function (editor, resource) {
+ this.render(editor, resource);
+ },
+
+ onEditorActivated: function (editor, resource) {
+ this.render(editor, resource);
+ },
+
+});
+
+exports.StatusBarPlugin = StatusBarPlugin;
+registerPlugin(StatusBarPlugin);