You are in
Debug Mode. "
- + "Don't forget to open the browser's dev tools to see any errors.",
- false);
-
- // Insert relevant LESS libraries
- var theme = settings.get("user/general/@skin");
-
- ui.defineLessLibrary(require("text!../c9.ide.layout.classic/themes/default-" + theme + ".less"), plugin);
- ui.defineLessLibrary(require("text!../c9.ide.layout.classic/less/lesshat.less"), plugin);
-
- fs.readdir("~/.c9/plugins", function(err, list) {
- if (err) return console.error(err);
-
- var names = loader.plugins;
- var toLoad = [];
-
- list.forEach(function(stat) {
- var name = stat.name;
- // If the plugin doesn't exist
- if (names.indexOf(name) == -1 && name.charAt(0) != "." && name.charAt(0) != "_")
- toLoad.push(name);
- });
-
- loadPlugins(toLoad);
- });
-
- commands.addCommand({
- name: "reloadCustomPlugin",
- group: "Plugins",
- bindKey: {
- mac: "Command-Enter",
- win: "Ctrl-Enter"
- },
- exec: function() {
- reloadPluginUI();
- }
- }, plugin);
-
- menus.addItemByPath("Tools/Developer/Reload Custom Plugin", new ui.item({
- command: "reloadCustomPlugin"
- }), 1000, plugin);
- }
-
- /***** Methods *****/
-
- function loadPlugins(list) {
- if (!vfs.connected) {
- vfs.once("connect", loadPlugins.bind(this, config));
- return;
- }
-
- if (typeof list == "string")
- list = [list];
-
- var config = [];
- var loadConfig = function() {
- architect.loadAdditionalPlugins(config, function(err) {
- if (err) console.error(err);
- });
- };
-
- async.each(list, function(name, next) {
- var resourceHolder = new Plugin();
- var resourceVersion = "";
-
- resourceHolder.on("load", function() {
- if (inited) load();
- });
-
- resourceHolder.freezePublicAPI({
- get version() { return resourceVersion; },
- set version(v) { resourceVersion = v; }
- });
-
- var inited = false;
- function load() {
- async.parallel([
- function(next) {
- // Fetch package.json
- fs.readFile("~/.c9/plugins/" + name + "/package.json", function(err, data) {
- if (err)
- return next(err);
-
- try {
- var options = JSON.parse(data);
- if (!options.plugins)
- throw new Error("Missing plugins property in package.json of " + name);
- }
- catch (e) {
- return next(err);
- }
-
- var host = vfs.baseUrl + "/";
- var base = join(String(c9.projectId),
- "plugins", auth.accessToken);
-
- // Configure Require.js
- var pathConfig = {};
- pathConfig["plugins/" + name] = host + join(base, name);
- requirejs.config({ paths: pathConfig });
-
- // Add the plugin to the config
- Object.keys(options.plugins).forEach(function(path) {
- var pluginPath = name + "/" + path;
-
- // Watch project path
- watch("~/.c9/plugins/" + pluginPath);
- var cfg = options.plugins[path];
- cfg.packagePath = "plugins/" + name + "/" + path;
- cfg.staticPrefix = host + join(base, name);
- cfg.apikey = "0000000000000000000000000000=";
-
- // Set version for package manager
- cfg.version = options.version;
-
- config.push(cfg);
- plugins.push(name + "/" + path);
- });
-
- // Set version for package manager
- resourceHolder.version = options.version;
-
- // Start the installer if one is included
- if (options.installer) {
- addStaticPlugin("installer", name, options.installer,
- null, resourceHolder);
- }
-
- next();
- });
- },
- function(next) {
- var path = join("~/.c9/plugins", name);
- var rePath = new RegExp("^" + util.escapeRegExp(path.replace(/^~/, c9.home) + "/"), "gm");
- find.getFileList({
- path: path,
- nocache: true,
- buffer: true
- }, function(err, data) {
- if (err)
- return next(err);
-
- // Remove the base path
- data = data.replace(rePath, "");
-
- if (data.match(/^__installed__.js/))
- return next("installed");
-
- // Process all the submodules
- var parallel = processModules(path, data, resourceHolder);
- async.parallel(parallel, function(err, data) {
- if (err)
- return next(err);
-
- if (!inited)
- resourceHolder.load(name + ".bundle");
-
- // Done
- next();
- });
- });
- }
- ], function(err, results) {
- if (err) console.error(err);
-
- if (!inited) {
- next();
- inited = true;
- }
- });
- }
-
- load();
- }, function() {
- emit.sticky("ready");
-
- if (!config.length) return;
-
- // Load config
- if (installer.sessions.length) {
- installer.on("stop", function listen(err) {
- if (err)
- return console.error(err);
-
- if (!installer.sessions.length) {
- loadConfig();
- installer.off("stop", listen);
- }
- });
- return;
- }
-
- loadConfig();
- });
- }
-
- function processModules(path, data, plugin) {
- var parallel = [];
-
- data.split("\n").forEach(function(line) {
- if (!line.match(reParts)) return;
-
- var type = RegExp.$1;
- var filename = RegExp.$2;
- if (filename.indexOf("/") > -1) return;
-
- if (type == "modes" && (reModule.test(filename) || !jsExtRe.test(filename)))
- return;
-
- if (type == "snippets") {
- if (jsExtRe.test(filename)) {
- var snippetPath = join("plugins", basename(path), type, filename).replace(jsExtRe, "");
- require([snippetPath], function(m) {
- architect.services["language.complete"].addSnippet(m, plugin);
- });
- }
- if (!/\.snippets$/.test(filename))
- return;
- }
-
- parallel.push(function(next) {
- fs.readFile(join(path, type, filename), function(err, data) {
- if (err) {
- console.error(err);
- return next(err);
- }
-
- addStaticPlugin(type, basename(path), filename, data, plugin);
-
- next();
- });
- });
- });
-
- return parallel;
- }
-
- function addStaticPlugin(type, pluginName, filename, data, plugin) {
- var services = architect.services;
- var path = "plugins/" + pluginName + "/"
- + (type == "installer" ? "" : type + "/")
- + filename.replace(/\.js$/, "");
-
- var bundleName = pluginName + ".bundle";
- if (!services[bundleName] && type !== "installer") {
- services[bundleName] = plugin;
- architect.lut["~/.c9/plugins/" + pluginName] = {
- provides: []
- };
- architect.pluginToPackage[bundleName] = {
- path: "~/.c9/plugins/" + pluginName,
- package: pluginName,
- version: plugin.version,
- isAdditionalMode: true
- };
- if (!architect.packages[pluginName])
- architect.packages[pluginName] = [];
- architect.packages[pluginName].push(name);
- }
-
- switch (type) {
- case "builders":
- data = util.safeParseJson(data, function() {});
- if (!data) return;
-
- services.build.addBuilder(filename, data, plugin);
- break;
- case "keymaps":
- data = util.safeParseJson(data, function() {});
- if (!data) return;
-
- services["preferences.keybindings"].addCustomKeymap(filename, data, plugin);
- break;
- case "modes":
- var mode = {};
- var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim();
- firstLine.split(";").forEach(function(n) {
- if (!n) return;
- var info = n.split(":");
- mode[info[0].trim()] = info[1].trim();
- });
-
- services.ace.defineSyntax({
- name: path,
- caption: mode.caption,
- extensions: (mode.extensions || "").trim()
- .replace(/\s*,\s*/g, "|").replace(/(^|\|)\./g, "$1")
- });
- break;
- case "outline":
- if (!data) return;
-
- services.outline.addOutlinePlugin(path, data, plugin);
- break;
- case "runners":
- data = util.safeParseJson(data, function() {});
- if (!data) return;
-
- services.run.addRunner(data.caption || filename, data, plugin);
- break;
- case "snippets":
- services["language.complete"].addSnippet(data, plugin);
- break;
- case "themes":
- services.ace.addTheme(data, plugin);
- break;
- case "templates":
- services.newresource.addFileTemplate(data, plugin);
- break;
- case "installer":
- if (data) {
- installer.createSession(pluginName, data, function(v, o) {
- require([path], function(fn) {
- fn(v, o);
- });
- });
- }
- else {
- require([path], function(fn) {
- installer.createSession(pluginName, fn.version, function(v, o) {
- fn(v, o);
- });
- });
- }
- }
- }
-
- // Check if require.s.contexts._ can help watching all dependencies
- function watch(path) {
- watcher.watch(path);
-
- watcher.on("change", function(e) {
- if (e.path == path)
- reloadPackage(path.replace(/^~\/\.c9\//, ""));
- });
- watcher.on("delete", function(e) {
- if (e.path == path)
- reloadPackage(path.replace(/^~\/\.c9\//, ""));
- });
- watcher.on("failed", function(e) {
- if (e.path == path) {
- setTimeout(function() {
- watcher.watch(path); // Retries once after 1s
- });
- }
- });
- }
-
- function reloadPackage(path) {
- var unloaded = [];
-
- function recurUnload(name) {
- var plugin = architect.services[name];
- unloaded.push(name);
-
- // Find all the dependencies
- var deps = ext.getDependencies(plugin.name);
-
- // Unload all the dependencies (and their deps)
- deps.forEach(function(name) {
- recurUnload(name);
- });
-
- // Unload plugin
- plugin.unload();
- }
-
- // Recursively unload plugin
- var p = architect.lut[path];
- if (p.provides) { // Plugin might not been initialized all the way
- p.provides.forEach(function(name) {
- recurUnload(name);
- });
- }
-
- // create reverse lookup table
- var rlut = {};
- for (var packagePath in architect.lut) {
- var provides = architect.lut[packagePath].provides;
- if (provides) { // Plugin might not been initialized all the way
- provides.forEach(function(name) {
- rlut[name] = packagePath;
- });
- }
- }
-
- // Build config of unloaded plugins
- var config = [], done = {};
- unloaded.forEach(function(name) {
- var packagePath = rlut[name];
-
- // Make sure we include each plugin only once
- if (done[packagePath]) return;
- done[packagePath] = true;
-
- var options = architect.lut[packagePath];
- delete options.provides;
- delete options.consumes;
- delete options.setup;
-
- config.push(options);
-
- // Clear require cache
- requirejs.undef(options.packagePath); // global
- });
-
- // Load all plugins again
- architect.loadAdditionalPlugins(config, function(err) {
- if (err) console.error(err);
- });
- }
-
- function reloadPluginUI() {
- var list = [];
- Object.keys(architect.pluginToPackage).forEach(function(name) {
- if (architect.pluginToPackage[name].isAdditionalMode)
- list.push(architect.pluginToPackage[name].path);
- });
-
- var path = list[list.length - 1];
- reloadPackage(path.replace(/^~\/\.c9\//, ""));
-
- // Avoid confusion with "Reload Last Plugin"
- var href = document.location.href.replace(/[?&]reload=[^&]+/, "");
- window.history.replaceState(window.history.state, null, href);
- showInfo("Reloaded " + path + ".", 1000);
- }
-
- /***** Lifecycle *****/
-
- plugin.on("load", function() {
- load();
- });
- plugin.on("unload", function() {
- loaded = false;
- });
-
- /***** Register and define API *****/
-
- /**
- *
- **/
- plugin.freezePublicAPI({
- /**
- *
- */
- get architect() { throw new Error(); },
- set architect(v) { architect = v; },
- /**
- *
- */
- get plugins() { return plugins; },
-
- _events: [
- /**
- * @event ready
- */
- "ready"
- ],
-
- /**
- *
- */
- addStaticPlugin: addStaticPlugin,
-
- /**
- *
- */
- reloadPackage: reloadPackage,
- /**
- *
- */
- loadPackage: loadPlugins
- });
-
- register(null, {
- "plugin.debug": plugin
- });
- }
-});
diff --git a/plugins/c9.ide.plugins/gui.js b/plugins/c9.ide.plugins/gui.js
new file mode 100644
index 00000000..cc0bf006
--- /dev/null
+++ b/plugins/c9.ide.plugins/gui.js
@@ -0,0 +1,959 @@
+define(function(require, exports, module) {
+ main.consumes = [
+ "app", "ext", "c9", "PreferencePanel", "settings", "ui", "util",
+ "layout", "menus", "commands", "pluginManager",
+ "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs",
+ "preferences.experimental", "apf","dialog.notification"
+ ];
+ main.provides = ["pluginManagerUi"];
+ return main;
+
+
+ function main(options, imports, register) {
+ var PreferencePanel = imports.PreferencePanel;
+ var settings = imports.settings;
+ var layout = imports.layout;
+ var commands = imports.commands;
+ var menus = imports.menus;
+ var ui = imports.ui;
+ var c9 = imports.c9;
+ var fs = imports.fs;
+ var ext = imports.ext;
+ var util = imports.util;
+ var apf = imports.apf;
+ var showError = imports["dialog.error"].show;
+ var showInfo = imports["dialog.info"].show;
+ var notify = imports["dialog.notification"].show;
+ var experimental = imports["preferences.experimental"];
+ var pluginManager = imports.pluginManager;
+ var architectApp = imports.app;
+
+ var search = require("../c9.ide.navigate/search");
+ var Tree = require("ace_tree/tree");
+ var TreeData = require("./managerdp");
+ var qs = require("querystring");
+
+ var escapeHTML = require("ace/lib/lang").escapeHTML;
+
+ var CORE = {};
+ var TEMPLATES = {
+ "plugin.simple": "Empty Plugin",
+ "plugin.default": "Full Plugin",
+ "plugin.installer": "Installer Plugin",
+ "plugin.bundle": "Cloud9 Bundle"
+ };
+
+ /***** Initialization *****/
+
+ var DEBUG = c9.location.indexOf("debug=2") > -1;
+ var ENABLED = DEBUG || experimental.addExperiment(
+ "plugin-manager",
+ options.devel,
+ "SDK/Plugin Manager"
+ );
+
+ var plugin = new PreferencePanel("Ajax.org", main.consumes, {
+ caption: "Plugin Explorer",
+ className: "plugins",
+ form: false,
+ noscroll: true,
+ index: 200,
+ visible: ENABLED,
+ });
+ var emit = plugin.getEmitter();
+
+ var model;
+ var datagrid;
+ var filterbox;
+ var btnInstall;
+ var btnUninstall;
+ var btnServices;
+ var btnReadme;
+ var btnReloadLast;
+ var localPlugins;
+ var btnSettings;
+
+ var loaded = false;
+ function load() {
+ if (loaded) return false;
+ loaded = true;
+
+ menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin);
+ menus.addItemByPath("Tools/Developer", null, 100100, plugin);
+
+ if (!DEBUG) {
+ menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({
+ onclick: function() {
+ var url = location.href + (location.href.indexOf("?") > -1
+ ? "&debug=2"
+ : "?debug=2");
+ util.openNewWindow(url);
+ }
+ }), 900, plugin);
+ }
+ if (!ENABLED) {
+ return;
+ }
+
+ commands.addCommand({
+ name: "openPluginManager",
+ group: "Plugins",
+ exec: function() {
+ commands.exec("openpreferences", null, { panel: plugin });
+ }
+ }, plugin);
+
+ menus.addItemByPath("Tools/Developer/Open Plugin Explorer", new ui.item({
+ command: "openPluginManager"
+ }), 1100, plugin);
+
+ if (DEBUG) {
+ notify("
You are in
Debug Mode. "
+ + "
"
+ + "
",
+ false);
+
+ document.getElementById(".pm_btn1").onclick = function() { commands.exec("openPluginManager") };
+ btnReloadLast = document.getElementById(".pm_btn2");
+ btnReloadLast.onclick = function() { commands.exec("reloadLastPlugin") };
+ updateReloadLastButton();
+
+ pluginManager.readAvailablePlugins(function(err, available) {
+ if (err) return console.error(err);
+ localPlugins = available;
+ reloadModel();
+ if (sessionStorage.localPackages) {
+ pluginManager.loadPackage(
+ util.safeParseJson(sessionStorage.localPackages) || []
+ );
+ }
+ var updateSessionStorage = function() {
+ var packages = pluginManager.packages;
+ sessionStorage.localPackages = JSON.stringify(Object.keys(packages).map(function(x) {
+ if (packages[x] && packages[x].fromVfs && packages[x].enabled)
+ return packages[x].filePath;
+ }).filter(Boolean));
+ };
+ pluginManager.on("enablePackage", updateSessionStorage);
+ pluginManager.on("disablePackage", updateSessionStorage);
+ });
+
+ commands.addCommand({
+ name: "reloadLastPlugin",
+ bindKey: { mac: "F4", win: "F4" },
+ hint: "reload plugin last reloaded in plugin manager",
+ exec: function() {
+ var names = getLastReloaded();
+ if (!names)
+ return commands.exec("openPluginManager", null, { panel: plugin });
+ reload(names);
+ }
+ }, plugin);
+
+ menus.addItemByPath("Tools/Developer/Reload Last Plugin", new ui.item({
+ command: "reloadLastPlugin",
+ isAvailable: getLastReloaded
+ }), 1300, plugin);
+ }
+
+ menus.addItemByPath("File/New Plugin", null, 210, plugin);
+ Object.keys(TEMPLATES).forEach(function(name) {
+ menus.addItemByPath("File/New Plugin/" + TEMPLATES[name], new ui.item({
+ onclick: function() {
+ pluginManager.createNewPlugin(name);
+ }
+ }), 210, plugin);
+ });
+ }
+
+ var drawn;
+ function draw(e) {
+ if (drawn) return;
+ drawn = true;
+
+ function scheduleReloadModel() {
+ if (scheduleReloadModel.timer) return;
+ scheduleReloadModel.timer = setTimeout(function() {
+ scheduleReloadModel.timer = null;
+ reloadModel();
+ });
+ }
+ ext.on("register", scheduleReloadModel);
+ pluginManager.on("change", scheduleReloadModel);
+ pluginManager.on("enablePackage", scheduleReloadModel);
+ pluginManager.on("disablePackage", scheduleReloadModel);
+
+ ui.insertCss(require("text!./style.css"), plugin);
+
+ model = new TreeData();
+ model.emptyMessage = "No plugins found";
+
+ model.columns = [{
+ caption: "Name",
+ value: "name",
+ width: "250",
+ type: "tree"
+ }, {
+ caption: "Startup Time",
+ // value: "time",
+ width: "100",
+ getText: function(p) {
+ if (p.time !== undefined)
+ return (p.time || 0) + "ms";
+
+ var total = 0;
+ function recur(p) {
+ if (p.time != undefined)
+ return total += p.time;
+ if (p.items)
+ p.items.forEach(recur);
+ }
+ recur(p);
+ return (p.time = total) + "ms";
+ }
+ }, {
+ caption: "Enabled",
+ value: "enabled",
+ width: "100"
+ }];
+ model.columns = null;
+
+ layout.on("eachTheme", function(e) {
+ var height = parseInt(ui.getStyleRule(".bar-preferences .blackdg .tree-row", "height"), 10) || 24;
+ model.rowHeightInner = height;
+ model.rowHeight = height;
+
+ if (e.changed) datagrid.resize(true);
+ });
+
+ architectApp.on("ready-additional", function() {
+ reloadModel();
+ });
+ reloadModel();
+
+ var hbox = new ui.hbox({
+ htmlNode: e.html,
+ padding: 5,
+ edge: "10 10 0 10",
+ childNodes: [
+ filterbox = new apf.codebox({
+ realtime: true,
+ skin: "codebox",
+ "class": "dark",
+ clearbutton: true,
+ focusselect: true,
+ height: 27,
+ width: 250,
+ singleline: true,
+ "initial-message": "Search installed plugins"
+ }),
+ btnReadme = new ui.button({
+ skin: "c9-toolbarbutton-glossy",
+ caption: "Readme",
+ class: "serviceButton",
+ onclick: function() {
+ mode = "readme";
+ scheduleRedraw();
+ }
+ }),
+ btnServices = new ui.button({
+ skin: "c9-toolbarbutton-glossy",
+ caption: "services",
+ class: "serviceButton",
+ onclick: function() {
+ mode = "services";
+ scheduleRedraw();
+ }
+ }),
+ new ui.filler({}),
+ btnInstall = new ui.button({
+ skin: "c9-toolbarbutton-glossy",
+ caption: "Enable",
+ class: "btn-red",
+ onclick: function() {
+ reloadGridSelection(true);
+ }
+ }),
+ btnUninstall = new ui.button({
+ skin: "c9-toolbarbutton-glossy",
+ caption: "Disable",
+ class: "btn-red",
+ onclick: function() {
+ reloadGridSelection(false);
+ }
+ }),
+ new ui.button({
+ skin: "c9-toolbarbutton-glossy",
+ caption: "Reload",
+ onclick: function() {
+ reloadGridSelection();
+ }
+ })
+ ]
+ });
+ var treeBar;
+ var descriptionBar;
+ var hboxInner = new ui.hsplitbox({
+ anchor: "0 0 0 0",
+ htmlNode: e.html,
+ class: "bar-preferences",
+ splitter: true,
+ childNodes: [
+ treeBar = new ui.bar({ width: 250 }),
+ descriptionBar = new ui.bar({ textselect: true }),
+ ]
+ });
+
+ var div = treeBar.$ext.appendChild(document.createElement("div"));
+ div.style.position = "absolute";
+ div.style.left = "0px";
+ div.style.right = "0px";
+ div.style.bottom = "0px";
+ div.style.top = "0px";
+ hboxInner.$ext.style.position = "absolute";
+ hboxInner.$ext.style.left = "10px";
+ hboxInner.$ext.style.right = "10px";
+ hboxInner.$ext.style.bottom = "10px";
+ hboxInner.$ext.style.top = "50px";
+
+ datagrid = new Tree(div);
+ datagrid.setTheme({ cssClass: "blackdg" });
+ datagrid.setDataProvider(model);
+
+ layout.on("resize", function() { datagrid.resize() }, plugin);
+
+ function setTheme(e) {
+ filterbox.setAttribute("class",
+ e.theme.indexOf("dark") > -1 ? "dark" : "");
+ }
+ layout.on("themeChange", setTheme);
+ setTheme({ theme: settings.get("user/general/@skin") });
+
+ filterbox.ace.commands.addCommands([
+ {
+ bindKey: "Enter",
+ exec: function() { }
+ }, {
+ bindKey: "Esc",
+ exec: function(ace) { ace.setValue(""); }
+ }
+ ]);
+
+ filterbox.ace.on("input", function(e) {
+ applyFilter();
+ });
+
+ // when tab is restored datagrids size might be wrong
+ // todo: remove this when apf bug is fixed
+ datagrid.once("mousemove", function() {
+ datagrid.resize(true);
+ });
+
+ datagrid.on("changeSelection", scheduleRedraw);
+ model.on("change", scheduleRedraw);
+
+ plugin.on("reloadModel", scheduleRedraw);
+
+ model.getCheckboxHTML = function(node) {
+ var enabled = node.enabled;
+ if (enabled == null || node.isGroup) return "";
+ return "
";
+ };
+
+ var mode = "services";
+ var readmeCache = {};
+ function renderDetails() {
+ var items = datagrid.selection.getSelectedNodes();
+ var hasEnabled = 0;
+ var hasDisabled = 0;
+ items.forEach(function(x) {
+ if (x.enabled != 0)
+ hasEnabled = true;
+ if (x.enabled != 1)
+ hasDisabled = true;
+ });
+ btnUninstall.setProperty("visible", hasEnabled);
+ btnInstall.setProperty("visible", hasDisabled);
+
+ if (mode == "services") {
+ renderServiceDetails(items);
+ btnServices.$ext.classList.add("serviceButtonActive");
+ btnReadme.$ext.classList.remove("serviceButtonActive");
+ }
+ else if (mode == "readme") {
+ renderReadmeDetails(items);
+ btnReadme.$ext.classList.add("serviceButtonActive");
+ btnServices.$ext.classList.remove("serviceButtonActive");
+ }
+ else {
+ descriptionBar.$ext.innerHTML = "";
+ btnReadme.$ext.classList.remove("serviceButtonActive");
+ btnServices.$ext.classList.remove("serviceButtonActive");
+ }
+
+ if (items.length == 1 && items[0].__error) {
+ var errorContainer = document.createElement("div");
+ errorContainer.style.cssText = "padding: 5px; white-space: pre-wrap";
+ errorContainer.textContent = items[0].__error.message;
+ descriptionBar.$ext.insertBefore(errorContainer, descriptionBar.$ext.firstChild);
+ }
+ }
+
+ function renderReadmeDetails(items) {
+ if (items.length > 1) {
+ descriptionBar.$ext.textContent = "Multipe items selected.";
+ return;
+ }
+ var item = items[0];
+ while (item && !item.packageConfig) {
+ item = item.parent;
+ }
+ if (!item) {
+ descriptionBar.$ext.textContent = "Readme is not available.";
+ return;
+ }
+
+ if (readmeCache[item.name]) {
+ var div = document.createElement("div");
+ div.textContent = readmeCache[item.name];
+ div.style.cssText = "padding: 5px; white-space: pre-wrap";
+ descriptionBar.$ext.textContent = "";
+ descriptionBar.$ext.appendChild(div);
+ return;
+ }
+ descriptionBar.$ext.textContent = "Loading...";
+ if (item.packageConfig.filePath) {
+ var parts = item.packageConfig.filePath.split("/");
+ parts.pop();
+ parts.push("README.md");
+ var path = parts.join("/");
+ fs.readFile(path, function(e, v) {
+ if (e)
+ return readmeCache[item.name] = "Readme is not available.";
+ readmeCache[item.name] = v;
+ renderDetails();
+ });
+ }
+ }
+
+ function renderServiceDetails(items) {
+ function addUnique(item, array) {
+ if (array.indexOf(item) == -1) array.push(item);
+ }
+
+ var provided = [];
+
+ items.forEach(function addProvides(x) {
+ if (x.map) {
+ Object.keys(x.map).forEach(function(n) {
+ addProvides(x.map[n]);
+ });
+ }
+ if (x.provides) {
+ x.provides.forEach(function(p) {
+ addUnique(p, provided);
+ });
+ }
+ });
+
+ var consumedMap = Object.create(null);
+ provided.forEach(function(service) {
+ consumedMap[service] = 0;
+ });
+ pluginManager.addAllProviders(consumedMap);
+ var consumedList = Object.keys(consumedMap);
+ var consumedGroups = splitToGroups(consumedList, consumedMap);
+
+ var dependents = Object.create(null);
+ provided.forEach(function(service) {
+ dependents[service] = 0;
+ });
+ pluginManager.addAllDependents(dependents);
+ var depList = Object.keys(dependents);
+ var depGroups = splitToGroups(depList, dependents);
+
+ function splitToGroups(list, map) {
+ var groups = [];
+ list.forEach(function(n) {
+ var level = Math.abs(map[n]);
+ if (!groups[level])
+ groups[level] = [];
+ groups[level].push(n);
+ });
+ groups = groups.filter(Boolean).slice(1);
+ return groups;
+ }
+
+ function formatServices(list, style) {
+ return "[" + list.map(function(x) {
+ return '
' + escapeHTML(x) + '';
+ }).join(", ") + "]";
+ }
+
+ descriptionBar.$ext.style.overflow = "auto";
+ var serviceDesc = '\
+
' + (provided.length ? formatServices(provided) : "") + '
\
+ \
+
' + consumedGroups.map(formatServices).join("
") + '
\
+ \
+
' + depGroups.map(formatServices).join("
") + '
\
+
';
+ descriptionBar.$ext.innerHTML = '
\
+
' + (items.length == 1
+ ? '' + escapeHTML(items[0].path) + ''
+ : (items.length || "no") + " plugins selected") + '
\
+
'
+ + (items.length ? serviceDesc : "")
+ + '
';
+
+ }
+ descriptionBar.$ext.addEventListener("click", function(e) {
+ if (e.button) return;
+ var el = e.target;
+ var isButton = el.classList.contains("serviceButton");
+ var text = el.textContent;
+ var serviceToPlugin = architectApp.serviceToPlugin;
+ if (e.detail == 1 && isButton && text) {
+ var path = serviceToPlugin[text] ? serviceToPlugin[text].packagePath : text;
+ var node = function search(node) {
+ if (node.path == path)
+ return node;
+ if (node.items) {
+ for (var i = 0; i < node.items.length; i++) {
+ var result = search(node.items[i]);
+ if (result)
+ return result;
+ }
+ }
+ }(model.cachedRoot);
+
+ if (node)
+ datagrid.reveal(node);
+ }
+ });
+ var hoverDetails = { hovered: "", highlighted: "" };
+ descriptionBar.$ext.addEventListener("mousemove", function(e) {
+ if (e.button) return;
+ var el = e.target;
+ var isButton = el.classList.contains("serviceButton");
+ var text = el.textContent;
+ hoverDetails.parent = el.parentNode;
+ hoverDetails.hovered = isButton ? text : "";
+ hoverDetails.type = isButton ? el.parentNode.getAttribute("type") : "";
+ if (hoverDetails.hovered != hoverDetails.highlighted)
+ requestAnimationFrame(updateHighlights);
+ });
+ function updateHighlights() {
+ if (hoverDetails.hovered == hoverDetails.highlighted) return;
+ var serviceToPlugin = architectApp.serviceToPlugin;
+ if (!serviceToPlugin) return;
+
+ var nodes = descriptionBar.$ext.querySelectorAll(".serviceButton");
+
+ var deps = Object.create(null);
+ deps[hoverDetails.hovered] = 0;
+ pluginManager.addAllDependents(deps);
+ pluginManager.addAllProviders(deps);
+
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ var val = node.textContent;
+ var highlight1 = false;
+ var highlight2 = false;
+ if (hoverDetails.type == "dependent") {
+ if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") {
+ highlight1 = deps[val] < 0;
+ highlight2 = deps[val] > 0;
+ }
+ } else if (hoverDetails.type == "consumed") {
+ if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") {
+ highlight1 = deps[val] > 0;
+ highlight2 = deps[val] < 0;
+ }
+ }
+
+ nodes[i].style.borderBottom = highlight1 ? "1px solid" : "";
+ if (!highlight1) {
+ nodes[i].style.borderBottom = highlight2 ? "1px dashed rgba(125, 125, 125, 0.9)" : "";
+ }
+ }
+ hoverDetails.highlighted = hoverDetails.hovered;
+ }
+
+ function scheduleRedraw() {
+ window.requestAnimationFrame(renderDetails);
+ }
+
+ var mnuCtxTree = new ui.menu({
+ id: "mnuChat",
+ }, plugin);
+ menus.decorate(mnuCtxTree);
+ plugin.addElement(mnuCtxTree);
+
+
+ btnSettings = new ui.button({
+ skin: "header-btn",
+ class: "panel-settings",
+ submenu: mnuCtxTree
+ });
+ treeBar.appendChild(btnSettings);
+ datagrid.renderer.on("scrollbarVisibilityChanged", updateScrollBarSize);
+ function updateScrollBarSize() {
+ var scrollBarV = datagrid.renderer.scrollBarV;
+ var w = scrollBarV.isVisible ? scrollBarV.getWidth() : 0;
+ btnSettings.$ext.style.marginRight = Math.max(w - 2, 0) + "px";
+ }
+
+ menus.addItemByPath("context/pluginManager/", mnuCtxTree, 0, plugin);
+ menus.addItemByPath("context/pluginManager/Reveal in File Tree", new ui.item({
+ isAvailable: function() {
+ var selected = datagrid.selection.getCursor();
+ return selected && selected.packageConfig && selected.packageConfig.filePath
+ || c9.sourceDir && selected && selected.path;
+ },
+ onclick: function() {
+ var selected = datagrid.selection.getCursor();
+ var tabbehavior = architectApp.services.tabbehavior;
+ var filePath = selected.packageConfig && selected.packageConfig.filePath;
+ if (!filePath && c9.sourceDir)
+ filePath = c9.sourceDir + "/" + selected.path + (selected.items ? "" : ".js");
+ if (filePath)
+ tabbehavior.revealtab({ path: util.normalizePath(filePath) });
+ else
+ showInfo("Path is not available.");
+ },
+ }), plugin);
+ menus.addItemByPath("context/pluginManager/Refresh List", new ui.item({
+ onclick: function() {
+ pluginManager.readAvailablePlugins(function(err, available) {
+ if (err) return console.error(err);
+ localPlugins = available;
+ reloadModel();
+ });
+ },
+ }), plugin);
+ menus.addItemByPath("context/pluginManager/~", new ui.divider({}), plugin);
+ menus.addItemByPath("context/pluginManager/Disable", new ui.item({
+ isAvailable: function() {
+ var selected = datagrid.selection.getCursor();
+ return selected && selected.enabled != 0;
+ },
+ onclick: function() { reloadGridSelection(false); },
+ }), plugin);
+ menus.addItemByPath("context/pluginManager/Enable", new ui.item({
+ isAvailable: function() {
+ var selected = datagrid.selection.getCursor();
+ return selected && selected.enabled != 1;
+ },
+ onclick: function() { reloadGridSelection(true); },
+ }), plugin);
+ menus.addItemByPath("context/pluginManager/Reload", new ui.item({
+ isAvailable: function() {
+ var selected = datagrid.selection.getCursor();
+ return selected;
+ },
+ onclick: function() { reloadGridSelection(); },
+ }), plugin);
+ treeBar.setAttribute("contextmenu", mnuCtxTree);
+
+ model.on("toggleCheckbox", function(e) {
+ reloadGridSelection(!e.target.enabled, e.selectedNodes || [e.target]);
+ });
+ }
+
+
+ /***** Methods *****/
+
+ function reloadModel() {
+ if (!model) return;
+
+ var packages = pluginManager.packages;
+
+ if (!CORE.pluginManagerUi) {
+ CORE.pluginManagerUi = 1;
+ pluginManager.addAllProviders(CORE);
+ Object.keys(CORE).forEach(function(n) {
+ if (architectApp.serviceToPlugin[n])
+ CORE[architectApp.serviceToPlugin[n].packagePath] = 1;
+ });
+ }
+
+ var GROUPS = {
+ // "changed": "Recently Changed",
+ "remote": "Remote Plugins",
+ "vfs": "Locally Installed Plugins",
+ "pre": "Pre-installed Plugins",
+ "core": "Core Plugins",
+ };
+
+ var groups = model.groups || Object.create(null);
+ if (!model.groups) {
+ var root = [];
+ model.cachedRoot = { items: root };
+ model.groups = groups;
+ Object.keys(GROUPS).forEach(function(name) {
+ root.push(groups[name] = {
+ map: Object.create(null),
+ isOpen: name != "runtime",
+ className: "group",
+ isGroup: true,
+ isType: name,
+ noSelect: true,
+ name: GROUPS[name]
+ });
+ });
+ }
+
+ function addPlugin(plugin, node) {
+ if (!plugin.provides || !plugin.consumes)
+ return;
+ if (plugin.packagePath) {
+ var parts = plugin.packagePath.split("/");
+
+ var path = parts.shift();
+ parts.forEach(function(p, i) {
+ path = path + "/" + p;
+ if (!node.map)
+ node.map = Object.create(null);
+ if (!node.map[p]) {
+ node.map[p] = {
+ path: path,
+ parent: node,
+ };
+ }
+ node.map[p].name = p;
+ if (i == parts.length - 1) {
+ var enabled = 1;
+ node.map[p].provides = plugin.provides;
+ plugin.provides.forEach(function(x) {
+ var service = architectApp.services[x];
+ if (!service || (!service.loaded && service.unload)) {
+ enabled = 0;
+ }
+ });
+ node.map[p].enabled = enabled;
+ }
+ node = node.map[p];
+ node.className = plugin.__error ? "load-error" : "";
+ node.__error = plugin.__error;
+ });
+ }
+ }
+
+ function addPackage(name) {
+ var pkg = packages[name];
+ var parent = pkg.filePath ? groups.vfs : groups.remote;
+
+ var node = parent.map[name] = parent.map[name] || {
+ path: "plugins/" + name,
+ name: name,
+ enabled: 0,
+ parent: parent,
+ };
+
+ node.packageConfig = pkg;
+ node.className = pkg.__error ? "load-error" : "";
+ node.__error = pkg.__error;
+ node.loading = pkg.loading;
+ }
+
+ architectApp.config.forEach(function(plugin) {
+ var node = CORE[plugin.packagePath] ? groups.core : groups.pre;
+ addPlugin(plugin, node);
+ });
+
+ if (localPlugins) {
+ localPlugins.forEach(function(name) {
+ if (!packages[name]) {
+ packages[name] = {
+ name: name,
+ filePath: "~/.c9/plugins/" + name + "/package.json",
+ };
+ }
+
+ });
+ }
+
+ Object.keys(packages).forEach(function(n) {
+ var pkg = packages[n];
+ var node = pkg.filePath ? groups.vfs : groups.remote;
+
+ addPackage(n);
+
+ if (pkg.c9 && pkg.c9.plugins) {
+ pkg.c9.plugins.forEach(function(plugin) {
+ addPlugin(plugin, node);
+ });
+ }
+ });
+
+ function flatten(node, index) {
+ if (node.map) {
+ node.items = node.children = Object.keys(node.map).map(function(x) {
+ return node.map[x];
+ });
+ }
+ if (node.items) {
+ node.items.forEach(flatten);
+ if (node.items.length == 1 && !node.isGroup) {
+ var other = node.items[0];
+ if (node.parent && node.parent.items[index] == node) {
+ node.parent.items[index] = other;
+ other.name = node.name + "/" + other.name;
+ other.packageConfig = node.packageConfig;
+ other.filePath = node.filePath;
+ other.url = node.url;
+ other.loading = node.loading;
+ }
+ }
+ if (!node.isGroup) {
+ node.enabled = null;
+ node.items.some(function(i) {
+ if (node.enabled == null) {
+ node.enabled = i.enabled;
+ }
+ if (i.enabled != node.enabled) {
+ node.enabled = -1;
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ flatten(model.cachedRoot);
+
+ applyFilter();
+
+ emit("reloadModel");
+ }
+
+ function applyFilter() {
+ model.keyword = filterbox && filterbox.getValue();
+
+ if (!model.keyword) {
+ model.reKeyword = null;
+ model.setRoot(model.cachedRoot);
+
+ // model.isOpen = function(node) { return node.isOpen; }
+ }
+ else {
+ model.reKeyword = new RegExp("("
+ + util.escapeRegExp(model.keyword) + ")", 'i');
+ var root = search.treeSearch(model.cachedRoot.items, model.keyword, true);
+ model.setRoot(root);
+
+ // model.isOpen = function(node) { return true; };
+ }
+ }
+
+ function reload(names) {
+ var nodes = names.split(/\s*,\s*/).map(function(name) {
+ if (pluginManager.packages[name])
+ return { packageConfig: pluginManager.packages[name] };
+ return { path: name };
+ });
+
+ reloadGridSelection(null, nodes);
+ }
+
+ function reloadGridSelection(mode, nodes) {
+ if (!nodes)
+ nodes = datagrid.selection.getSelectedNodes();
+
+ var reloadLast = pluginManager.reload(nodes, mode);
+ if (reloadLast.length) {
+ var href = document.location.href.replace(/[?&]reload=[^&]+/, "");
+ href += (href.match(/\?/) ? "&" : "?") + "reload=" + reloadLast.join(",");
+ window.history.replaceState(window.history.state, null, href);
+ if (mode !== false) {
+ showReloadTip(reloadLast.join(","), mode);
+ updateReloadLastButton();
+ }
+ }
+ }
+
+ function updateReloadLastButton() {
+ var last = getLastReloaded();
+ if (last) {
+ btnReloadLast.visible = true;
+ btnReloadLast.textContent = "Reload " + last;
+ } else {
+ btnReloadLast.visible = false;
+ }
+ }
+
+ function showReloadTip(name, mode) {
+ if (options.devel) {
+ var key = commands.getPrettyHotkey("reloadLastPlugin");
+ if (!getLastReloaded()) {
+ showInfo("Loaded " + name + ". Press " + key + " to reload again.", 3000);
+ return;
+ }
+ }
+ showInfo("Loaded " + name + " for the duration of current browser session.", 3000);
+ }
+
+ function getLastReloaded() {
+ return qs.parse(document.location.search.substr(1)).reload;
+ }
+
+
+ /***** Lifecycle *****/
+
+ plugin.on("load", function() {
+ load();
+ });
+ plugin.on("draw", function(e) {
+ draw(e);
+ });
+ plugin.on("activate", function(e) {
+ datagrid && datagrid.resize();
+ });
+ plugin.on("resize", function(e) {
+ datagrid && datagrid.resize();
+ });
+ plugin.on("enable", function() {
+
+ });
+ plugin.on("disable", function() {
+
+ });
+ plugin.on("unload", function() {
+ loaded = false;
+ drawn = false;
+ btnServices = null;
+ btnReadme = null;
+ btnReloadLast = null;
+ model = null;
+ datagrid = null;
+ filterbox = null;
+ btnInstall = null;
+ btnUninstall = null;
+ localPlugins = null;
+ });
+
+ /***** Register and define API *****/
+
+ /**
+ *
+ **/
+ plugin.freezePublicAPI({
+
+ /*
+ * @ignore
+ */
+ get datagrid() { return datagrid; },
+ });
+
+ register(null, {
+ "pluginManagerUi": plugin,
+ });
+ }
+});
diff --git a/plugins/c9.ide.plugins/installer.js b/plugins/c9.ide.plugins/installer.js
deleted file mode 100644
index 6a6abff8..00000000
--- a/plugins/c9.ide.plugins/installer.js
+++ /dev/null
@@ -1,196 +0,0 @@
-define(function(require, exports, module) {
- main.consumes = [
- "Plugin", "proc", "c9", "pubsub", "auth", "util", "installer",
- "preferences.experimental"
- ];
- main.provides = ["plugin.installer"];
- return main;
-
- function main(options, imports, register) {
- var Plugin = imports.Plugin;
- var c9 = imports.c9;
- var util = imports.util;
- var proc = imports.proc;
- var auth = imports.auth;
- var pubsub = imports.pubsub;
- var installer = imports.installer;
- var experimental = imports["preferences.experimental"];
-
- var async = require("async");
-
- var escapeShell = util.escapeShell;
- var updates = options.updates;
- var architect;
-
- /***** Initialization *****/
-
- var plugin = new Plugin("Ajax.org", main.consumes);
- var emit = plugin.getEmitter();
-
- var DEBUG_MODE = c9.location.indexOf("debug=2") > -1;
- var HASSDK = DEBUG_MODE || experimental.addExperiment("sdk", false, "SDK/Load Custom Plugins");
-
- var installing;
-
- var loaded = false;
- function load() {
- if (loaded) return false;
- loaded = true;
-
- if (!HASSDK) return;
-
- pubsub.on("message", function(message) {
- if (message.type != "package")
- return;
-
- console.log("PubSub package API message", message);
- var action = message.action;
- var body = message.body;
-
- // Only accept packages that are installed for this project
- if (body.pid && body.pid != c9.projectId)
- return;
-
- // Only accept packages that are installed for this user
- if (body.uid && body.uid != c9.uid)
- return;
-
- if (action == "install") {
- installPlugins([body.config], function() {});
- }
- else if (action == "uninstall") {
- uninstallPlugin(body.name, function() {});
- }
- });
-
- installPlugins(updates);
- }
-
- /***** Methods *****/
-
- function installPlugins(config, callback) {
- // if (!vfs.connected) {
- // vfs.once("connect", loadPackages.bind(this, config));
- // return;
- // }
-
- if (!config.length)
- return callback && callback();
-
- // Only run one installer at a time
- if (installing) {
- return plugin.once("finished", function() {
- installPlugins(config, callback);
- });
- }
-
- installing = true;
-
- var found = {}, packages = [];
- config.forEach(function(item) {
- if (typeof item === "string") {
- item = { name: item, version: null };
- }
-
- if (found[item.name]) return;
- found[item.name] = true;
-
- packages.push({ name: item.name, version: item.version });
- });
-
- async.eachSeries(packages, function(pkg, next) {
- installPlugin(pkg.name, pkg.version, next);
- }, function(err) {
- installing = false;
- emit("finished");
-
- if (err) {
- console.error(err.message);
- return callback && callback(err);
- }
-
- architect.loadAdditionalPlugins(config, callback);
- });
- }
-
- function installPlugin(name, version, callback) {
- // Headless installation of the plugin
- installer.createSession(name, version, function(session, options) {
- var cmd = [
- "c9",
- "install",
- "--local",
- "--force",
- "--accessToken=" + auth.accessToken,
- ];
-
- if (version == null)
- cmd.push(escapeShell(name));
- else
- cmd.push(escapeShell(name + "@" + version));
-
- session.install({
- "bash": cmd.join(" ")
- });
-
- // Force to start immediately
- session.start(callback, true);
- }, function() {}, 2); // Force to not be administered
- }
-
- function uninstallPlugin(name, callback) {
- // Headless uninstallation of the plugin
- installer.createSession(name, -1, function(session, options) {
- session.install({
- "bash": "c9 remove --local --force --accessToken=" + auth.accessToken
- + " " + escapeShell(name)
- });
-
- // Force to start immediately
- session.start(callback, true);
- }, function() {}, 2); // Force to not be administered
- }
-
- /***** Lifecycle *****/
-
- plugin.on("load", function() {
- load();
- });
- plugin.on("unload", function() {
- loaded = false;
- installing = false;
- });
-
- /***** Register and define API *****/
-
- /**
- *
- **/
- plugin.freezePublicAPI({
- /**
- *
- */
- get architect() { throw new Error(); },
- set architect(v) { architect = v; },
-
- /**
- *
- */
- installPlugins: installPlugins,
-
- /**
- *
- */
- installPlugin: installPlugin,
-
- /**
- *
- */
- uninstallPlugin: uninstallPlugin
- });
-
- register(null, {
- "plugin.installer": plugin
- });
- }
-});
\ No newline at end of file
diff --git a/plugins/c9.ide.plugins/loader.js b/plugins/c9.ide.plugins/loader.js
deleted file mode 100644
index 08411773..00000000
--- a/plugins/c9.ide.plugins/loader.js
+++ /dev/null
@@ -1,340 +0,0 @@
-/*global requirejs*/
-define(function(require, exports, module) {
- main.consumes = [
- "Plugin", "vfs", "c9", "plugin.installer", "fs", "auth",
- "preferences.experimental"
- ];
- main.provides = ["plugin.loader"];
- return main;
-
- function main(options, imports, register) {
- var Plugin = imports.Plugin;
- var vfs = imports.vfs;
- var c9 = imports.c9;
- var fs = imports.fs;
- var auth = imports.auth;
- var installer = imports["plugin.installer"];
- var experimental = imports["preferences.experimental"];
-
- var dirname = require("path").dirname;
- var join = require("path").join;
- var async = require("async");
- var _ = require("lodash");
-
- var architect;
-
- /***** Initialization *****/
-
- var plugin = new Plugin("Ajax.org", main.consumes);
- // var emit = plugin.getEmitter();
-
- var DEBUG_MODE = c9.location.indexOf("debug=2") > -1;
- var ENABLED = DEBUG_MODE || experimental.addExperiment("plugins", false, "SDK/Load Plugins From Workspace");
- var HASSDK = DEBUG_MODE || experimental.addExperiment("sdk", false, "SDK/Load Custom Plugins");
-
- var plugins = options.plugins;
- var loadFromDisk = options.loadFromDisk;
-
- var names = [];
-
- function load() {
- if (!HASSDK) return;
- if (!ENABLED) return;
-
- loadPlugins(plugins);
- }
-
- /***** Methods *****/
-
- // TODO the resolution alghoritm used here is very inefficient
- // ideally we will use a simpler method that doesn't need to scan directories
- function loadPlugins(loaderConfig) {
- if (!vfs.connected) {
- vfs.once("connect", loadPlugins.bind(this, loaderConfig));
- return;
- }
-
- if (!loaderConfig.length && !loadFromDisk)
- return;
-
- listAllPackages(function(err, resolved) {
- if (err) return console.error(err);
-
- var extraPackages = {};
- // convert old format from db to the new one
- loaderConfig.forEach(function(p) {
- if (!extraPackages[p.packageName]) {
- var path = "plugins/" + p.packageName;
- extraPackages[path] = {
- apikey: p.apikey,
- packagePath: path,
- version: p.version,
- name: p.packageName
- };
- }
- });
- if (!loadFromDisk) {
- // filter packages by config instead of loading
- // everything from disk
- resolved = resolved.filter(function(config) {
- if (extraPackages[config.packagePath])
- return true;
-
- console.warn("[c9.ide.loader] Not loading package "
- + config.path + " because it is not installed, "
- + "according to the database");
- return false;
- });
- }
- resolved.filter(function(config) {
- if (extraPackages[config.packagePath]) {
- _.assign(config, extraPackages[config.packagePath], function(x, y) { return x || y; });
- delete extraPackages[config.packagePath];
- }
- });
- Object.keys(extraPackages).forEach(function(packagePath) {
- console.warn("[c9.ide.loader] Package "
- + packagePath + " should be installed, according "
- + "to the database, but was not found on the filesystem. "
- + "Try reinstalling it.");
- });
-
- async.each(resolved, loadPackage, function(err) {
- if (err) console.error(err);
- });
- });
- }
-
- /**
- * List all packages on disk by scanning `~/.c9` and resolve the
- * detected packages by order of override priority:
- *
- * - Developer plugins in `~/.c9/dev/plugins` have the highest
- * priority to allow local development of new functionality without
- * the risk of having your changes overwritten by any update
- * mechanism.
- *
- * - Managed plugins in `~/.c9/managed/plugins` are pre-installed and
- * updated by the Cloud9 system and have a higher priority that
- * possibly outdated or unknown packages installed by the user.
- *
- * - User-installed in `~/.c9/plugins` plugins installed plugins are
- * the default priority have the lowest priority.
- *
- * @param {Function} callback
- * @param {Error=} callback.err
- */
- function listAllPackages(callback) {
- async.parallel({
- "plugins": async.apply(listPackages, "~/.c9/plugins"),
- "managed": async.apply(listPackages, "~/.c9/managed/plugins"),
- "dev": async.apply(listPackages, "~/.c9/dev/plugins"),
- }, function(err, packages) {
- if (err && err.code === "EDISCONNECT") {
- c9.once("connect", function() {
- listAllPackages(callback);
- });
- return;
- }
-
- if (err) return callback(err);
-
- var resolved = {};
-
- // default: ~/.c9/plugins
- packages.plugins.forEach(function(config) {
- if (!config) return;
- resolved[config.name] = config;
- });
-
- // high: ~/.c9/managed/plugins
- packages.managed.forEach(function(config) {
- if (!config) return;
- resolved[config.name] = config;
- });
-
- // higher: ~/.c9/dev/plugins
- packages.dev.forEach(function(config) {
- if (!config) return;
- resolved[config.name] = config;
- });
-
- callback(null, _.values(resolved));
- });
- }
-
- /**
- * List packages in the given directory
- *
- * @param {String} dirPath Path to the directory to scan
- *
- * @param {Function} callback
- * @param {Error=} callback.err
- * @param {Object[]} callback.packages
- */
- function listPackages(dirPath, callback) {
- fs.readdir(dirPath, function(err, stats) {
- if (err && err.code === "ENOENT")
- return callback(null, []);
-
- if (err)
- return callback(err);
-
- async.map(stats, function(stat, done) {
- // check for folder or symlink with folder target
- if (stat.mime !== "inode/directory"
- && (stat.mime === "inode/symlink" && stat.linkStat.mime !== "inode/directory")
- ) {
- return done();
- }
-
- // check folers not prefixed with [._]
- if (stat.name[0] === "." || stat.name[0] === "_") {
- return done();
- }
-
- // check and load package.json
- var config = {
- name: stat.name,
- path: absolutePath([dirPath, stat.name].join("/")),
- packagePath: ["plugins", stat.name].join("/"),
- staticPrefix: vfs.url([dirPath, stat.name].join("/")),
- };
- done(null, config);
- }, callback);
- });
- }
-
- /**
- * Load a the `package.json` metadata of the given package
- *
- * @param {Object} config The package configuration
- *
- * @param {Function} callback
- * @param {Error=} callback.err
- * @param {Object} callback.metadata
- */
- function loadPackageMetadata(config, callback) {
- fs.readfile([config.path, "package.json" ].join("/"), function(metadataStr) {
- var metadata;
-
- try {
- metadata = JSON.parse(metadataStr);
- } catch (e) {
- return callback(e);
- }
-
- callback(null, metadata);
- }, function(err) {
- callback(err);
- });
- }
-
- /**
- * Load a the `__installed__.js` definition of the given package
- *
- * @param {Object} config The package configuration
- *
- * @param {Function} callback
- * @param {Error=} callback.err
- * @param {Array
} callback.installed
- */
- function loadPackageInstalledJs(config, callback) {
- var paths = {};
- paths[config.packagePath] = config.staticPrefix;
-
- requirejs.config({ paths: paths });
- requirejs.undef(config.packagePath, true);
-
- require([[config.packagePath, "__installed__"].join("/")], function(installed) {
- callback(null, installed);
- }, function(err) {
- callback(err);
- });
- }
-
- /**
- * Load the given package by checking its `__installed__.js` file or
- * its `package.json#plugins`, then call `Architect#loadAdditionalPlugins()`.
- *
- * @param {Object} config The package configuration
- *
- * @param {Function} callback
- * @param {Error=} callback.err
- */
- function loadPackage(config, callback) {
- loadPackageInstalledJs(config, function addToArchitect(err, installed) {
- var plugins = installed;
- if (err) {
- return callback(err);
- // TODO disbled since this doesn't handle bundles and breaks debug=2
- // loadPackageMetadata(config, function(err, metadata) {
- // if (err) return callback(err);
- // config.metadata = metadata;
- // var plugins = _.map(config.metadata.plugins, function(value, key) {
- // return [ "plugins", config.name, key ].join("/");
- // });
- // addToArchitect(err, plugins);
- // });
- }
-
- var architectConfig = plugins.map(function(plugin) {
- if (typeof plugin == "string")
- plugin = { packagePath: plugin };
-
- plugin.staticPrefix = config.staticPrefix;
-
- plugin.packageName = config.name;
- plugin.packageMetadata = config.metadata;
- plugin.packageDir = config.path;
-
- plugin.apikey = config.apikey;
- plugin.version = config.version;
-
- return plugin;
- });
-
- names.push(config.name);
-
- architect.loadAdditionalPlugins(architectConfig, function(err) {
- callback(err);
- });
- });
- }
-
- function absolutePath(fromPath) {
- var toPath = fromPath.replace(/^~/, c9.home);
- return toPath;
- }
-
- /***** Lifecycle *****/
-
- plugin.on("load", function() {
- load();
- });
- plugin.on("unload", function() {
- });
-
- /***** Register and define API *****/
-
- /**
- *
- **/
- plugin.freezePublicAPI({
- /**
- *
- */
- get architect() { throw new Error(); },
- set architect(v) { architect = v; },
-
- /**
- *
- */
- get plugins() { return names; }
- });
-
- register(null, {
- "plugin.loader": plugin
- });
- }
-});
diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js
index 6897162f..266fcc4b 100644
--- a/plugins/c9.ide.plugins/manager.js
+++ b/plugins/c9.ide.plugins/manager.js
@@ -1,95 +1,30 @@
/*global requirejs*/
define(function(require, exports, module) {
main.consumes = [
- "PreferencePanel", "settings", "ui", "util", "Form", "ext", "c9", "apf",
- "dialog.alert", "dialog.confirm", "layout", "proc", "menus", "commands",
- "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs", "plugin.debug",
- "preferences.experimental"
+ "app", "ext", "c9", "Plugin", "proc", "fs", "vfs", "dialog.error"
];
- main.provides = ["plugin.manager", "pluginManager"];
+ main.provides = ["pluginManager", "plugin.manager", "plugin.debug"];
return main;
- /*
- - Show Packages to be updated
- - Open Plugin Store
- - Open Cloud9 in Debug Mode
- - List all installed packages
- - Filter
- - Core packages
- - Name
- - Version
- - Description
- - Load Time
- - Plugin profile
- - Pre-installed
- - *
- - Custom packages
- - *
-
- - Actions:
- - Uninstall
- - Report Issue
- - Open README
- - Open in Cloud9
-
- DataProvider.variableHeightRowMixin.call(this) in datagrid constructor
- and set node.height
-
- harutyun [1:11 PM]
- or add a custom getItemHeight function like https://github.com/c9/newclient/blob/master/node_modules/ace_tree/demo/demo.js#L63 (edited)
- */
function main(options, imports, register) {
- var PreferencePanel = imports.PreferencePanel;
- var settings = imports.settings;
- var layout = imports.layout;
- var ui = imports.ui;
var c9 = imports.c9;
- var menus = imports.menus;
var fs = imports.fs;
- var commands = imports.commands;
var ext = imports.ext;
- var tree = imports.tree;
var proc = imports.proc;
+ var Plugin = imports.Plugin;
var util = imports.util;
- var qs = require("querystring");
- var apf = imports.apf;
var vfs = imports.vfs;
- var alert = imports["dialog.alert"].show;
- var confirm = imports["dialog.confirm"].show;
var showError = imports["dialog.error"].show;
- var showInfo = imports["dialog.info"].show;
- var favs = imports["tree.favorites"];
- var pluginDebug = imports["plugin.debug"];
- var experimental = imports["preferences.experimental"];
+ var architectApp = imports.app;
- var search = require("../c9.ide.navigate/search");
- var Tree = require("ace_tree/tree");
- var TreeData = require("./managerdp");
var join = require("path").join;
var basename = require("path").basename;
var dirname = require("path").dirname;
var async = require("async");
-
+
var staticPrefix = options.staticPrefix;
- var architect;
- var CORE = {
- "c9.core": 1, "c9.fs": 1, "c9.ide.preferences": 1, "c9.ide.panels": 1,
- "c9.ide.plugins": 1, "c9.ide.login": 1, "c9.vfs.client": 1,
- "c9.ide.console": 1, "c9.ide.editors": 1, "c9.ide.dialog.common": 1,
- "c9.ide.dialog.file": 1, "c9.ide.dialog.login": 1, "c9.ide.errorhandler": 1,
- "c9.ide.help": 1, "c9.ide.keys": 1, "c9.ide.restore": 1, "c9.ide.watcher": 1,
- "c9.ide.tree": 1, "c9.ide.info": 1,
- "c9.ide.layout.classic": 1, "c9.ide.terminal": 1, "c9.ide.ace": 1,
- "c9.ide.clipboard": 1, "c9.nodeapi": 1
- };
- var GROUPS = {
- "custom": "Installed Plugins",
- "pre": "Pre-installed plugins",
- "core": "Core Plugins",
- "runtime": "Plugins created runtime"
- };
var TEMPLATES = {
"plugin.simple": "Empty Plugin",
"plugin.default": "Full Plugin",
@@ -97,511 +32,36 @@ define(function(require, exports, module) {
"plugin.bundle": "Cloud9 Bundle"
};
- // @TODO add sorting
-
/***** Initialization *****/
- var ENABLED = c9.location.indexOf("debug=2") > -1
- || experimental.addExperiment(
- "plugin-manager",
- options.devel,
- "SDK/Plugin Manager"
- );
+ var plugin = new Plugin();
+ var emit = plugin.getEmitter();
- var plugin = new PreferencePanel("Ajax.org", main.consumes, {
- caption: "Plugin Manager",
- className: "plugins",
- form: false,
- noscroll: true,
- index: 200,
- visible: ENABLED,
- });
- // var emit = plugin.getEmitter();
-
- var model, datagrid, filterbox;
- var btnUninstall, btnReport, btnReadme, btnCloud9, btnReload;
-
- var loaded = false;
- function load() {
- if (loaded) return false;
- loaded = true;
-
- if (!ENABLED) return;
-
- // @TODO enable/disable plugins -> move to ext
-
- // settings.on("read", function(e) {
- // updateCommandsFromSettings();
- // }, plugin);
-
- // commands.on("update", function(){
- // changed = true;
- // updateCommandsFromSettings();
- // }, plugin);
-
-
- if (options.devel) {
- commands.addCommand({
- name: "reloadLastPlugin",
- bindKey: { mac: "F4", win: "F4" },
- hint: "reload plugin last reloaded in plugin manager",
- exec: function() {
- var name = getLastReloaded();
- if (!name)
- return commands.exec("reloadPlugin", null, { panel: plugin });
- reload(name);
- }
- }, plugin);
- commands.addCommand({
- name: "reloadPlugin",
- group: "Plugins",
- exec: function() {
- commands.exec("openpreferences", null, { panel: plugin });
- }
- }, plugin);
-
- menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin);
- menus.addItemByPath("Tools/Developer", null, 100100, plugin);
-
- menus.addItemByPath("Tools/Developer/Reload Built-in Plugin...", new ui.item({
- command: "reloadPlugin"
- }), 1100, plugin);
-
- menus.addItemByPath("Tools/Developer/Reload Last Plugin", new ui.item({
- command: "reloadLastPlugin",
- isAvailable: getLastReloaded
- }), 1200, plugin);
- }
-
- menus.addItemByPath("File/New Plugin", null, 210, plugin);
- Object.keys(TEMPLATES).forEach(function(name) {
- menus.addItemByPath("File/New Plugin/" + TEMPLATES[name], new ui.item({
- onclick: function() {
- createNewPlugin(name);
- }
- }), 210, plugin);
- });
-
- ext.on("register", function() {
- setTimeout(reloadModel);
- });
- }
-
- var drawn;
- function draw(e) {
- if (drawn) return;
- drawn = true;
-
- model = new TreeData();
- model.emptyMessage = "No plugins found";
-
- model.columns = [{
- caption: "Name",
- value: "name",
- // getText: function(p){
- // return p.name + " (" + p.items.length + ")";
- // },
- width: "250",
- type: "tree"
- }, {
- caption: "Version",
- // value: "version",
- getText: function(p) {
- return p.version ||
- (p.isPackage
- ? p.items.length && p.items[0].version || ""
- : "");
- },
- width: "100"
- }, {
- caption: "Startup Time",
- // value: "time",
- width: "100",
- getText: function(p) {
- if (p.time !== undefined)
- return (p.time || 0) + "ms";
-
- var total = 0;
- if (p.isPackage || p.name == "runtime") {
- p.items.forEach(function(item) { total += item.time || 0; });
- }
- else {
- p.items.forEach(function(p) {
- p.items && p.items.forEach(function(item) { total += item.time || 0; });
- });
- }
- return (p.time = total) + "ms";
- }
- }, {
- caption: "Enabled",
- value: "enabled",
- width: "100"
- // }, {
- // caption: "Package",
- // value: "package", // @todo make a link
- // width: "100%"
- }, {
- caption: "Developer",
- // value: "developer",
- getText: function(p) {
- return p.developer ||
- (p.isPackage
- ? p.items.length && p.items[0].developer || ""
- : "");
- },
- width: "150"
- }];
-
- layout.on("eachTheme", function(e) {
- var height = parseInt(ui.getStyleRule(".bar-preferences .blackdg .tree-row", "height"), 10) || 24;
- model.rowHeightInner = height;
- model.rowHeight = height;
-
- if (e.changed) datagrid.resize(true);
- });
-
- reloadModel();
-
- // type: "custom",
- // title: "Introduction",
- // position: 1,
- // node: intro = new ui.bar({
- // height: 149,
- // "class" : "intro",
- // style: "padding:12px;position:relative;"
- // })
-
- // intro.$int.innerHTML =
- // 'Keybindings
Change these settings to configure '
- // + 'how Cloud9 responds to your keyboard commands.
'
- // + 'You can also manually edit your keymap file.
'
- // + 'Hint: Double click on the keystroke cell in the table below to change the keybinding.
';
-
- // intro.$int.querySelector("a").onclick = function(){ editUserKeys(); };
-
- var hbox = new ui.hbox({
- htmlNode: e.html,
- padding: 5,
- edge: "10 10 0 10",
- childNodes: [
- filterbox = new apf.codebox({
- realtime: true,
- skin: "codebox",
- "class": "dark",
- clearbutton: true,
- focusselect: true,
- height: 27,
- width: 250,
- singleline: true,
- "initial-message": "Search installed plugins"
- }),
- new ui.filler({}),
- btnUninstall = new ui.button({
- skin: "btn-default-css3",
- caption: "Uninstall",
- class: "btn-red",
- onclick: function() {
- var item = datagrid.selection.getCursor();
- if (item.isPackage)
- uninstall(item.name, function() {});
- else if (item.enabled == "true")
- disable(item.name, function() {});
- else
- enable(item.name, function() {});
- }
- }),
- btnReport = new ui.button({
- skin: "btn-default-css3",
- caption: "Report Issue"
- }),
- btnReadme = new ui.button({
- skin: "btn-default-css3",
- caption: "Open README"
- }),
- btnCloud9 = new ui.button({
- skin: "btn-default-css3",
- caption: "Open in Cloud9"
- }),
- btnReload = new ui.button({
- skin: "btn-default-css3",
- caption: "Reload",
- onclick: function() {
- var item = datagrid.selection.getCursor();
- if (item.enabled && item.name)
- reload(item.name);
- }
- })
- ]
- });
-
- var div = e.html.appendChild(document.createElement("div"));
- div.style.position = "absolute";
- div.style.left = "10px";
- div.style.right = "10px";
- div.style.bottom = "10px";
- div.style.top = "50px";
-
- datagrid = new Tree(div);
- datagrid.setTheme({ cssClass: "blackdg" });
- datagrid.setDataProvider(model);
-
- layout.on("resize", function() { datagrid.resize(); }, plugin);
-
- function setTheme(e) {
- filterbox.setAttribute("class",
- e.theme.indexOf("dark") > -1 ? "dark" : "");
- }
- layout.on("themeChange", setTheme);
- setTheme({ theme: settings.get("user/general/@skin") });
-
- filterbox.ace.commands.addCommands([
- {
- bindKey: "Enter",
- exec: function() { }
- }, {
- bindKey: "Esc",
- exec: function(ace) { ace.setValue(""); }
- }
- ]);
-
- filterbox.ace.on("input", function(e) {
- applyFilter();
- });
-
- // when tab is restored datagrids size might be wrong
- // todo: remove this when apf bug is fixed
- datagrid.once("mousemove", function() {
- datagrid.resize(true);
- });
-
- datagrid.on("changeSelection", function(e) {
- var item = datagrid.selection.getCursor();
-
- if (item.isGroup) {
- btnUninstall.disable();
- btnReport.disable();
- btnReadme.disable();
- btnCloud9.disable();
- btnReload.disable();
- }
- else {
- if (item.isPackage) {
- btnUninstall.setCaption("Uninstall");
- btnUninstall.setAttribute("class", "btn-red");
- }
- else {
- btnUninstall.setCaption(item.enabled == "true" ? "Disable" : "Enable");
- btnUninstall.setAttribute("class", item.enabled == "true" ? "btn-red" : "btn-green");
- }
-
- if (CORE[item.name] || item.parent.parent && item.parent.parent.isType == "core") {
- btnUninstall.disable();
- } else {
- btnUninstall.enable();
- }
-
- if (item.isPackage || CORE[item.name] || item.parent.parent && item.parent.parent.isType == "core") {
- btnReload.disable();
- } else {
- btnReload.enable();
- }
-
- btnReport.enable();
- btnReadme.enable();
- btnCloud9.enable();
- }
- });
- }
+ var disabledPlugins = Object.create(null);
+ var packages = Object.create(null);
/***** Methods *****/
-
- function reloadModel() {
- if (!model) return;
-
- var groups = {};
- var packages = {};
- var root = [];
-
- ["custom", "pre", "core", "runtime"].forEach(function(name) {
- root.push(groups[name] = {
- items: [],
- isOpen: name != "runtime",
- className: "group",
- isGroup: true,
- isType: name,
- noSelect: true,
- name: GROUPS[name]
+
+ function readAvailablePlugins(callback) {
+ fs.readdir("~/.c9/plugins", function(err, list) {
+ if (err) return callback && callback(err);
+
+ var available = [];
+ list.forEach(function(stat) {
+ var name = stat.name;
+ if (!/(directory|folder)$/.test(stat.mime)) return;
+ if (!/[._]/.test(name[0])) available.push(name);
});
+ callback && callback(null, available);
});
-
- var lut = ext.named;
-
- ext.plugins.forEach(function(plugin) {
- var info = architect.pluginToPackage[plugin.name];
- var packageName = info && info.package || "runtime";
-
- var groupName;
- if (CORE[packageName]) groupName = "core";
- else if (info && info.isAdditionalMode) groupName = "custom";
- else groupName = "pre";
-
- var package;
- if (packageName == "runtime") {
- package = groups.runtime;
- }
- else {
- package = packages[packageName];
- if (!package)
- groups[groupName].items.push(package = packages[packageName] = {
- items: [],
- isPackage: true,
- className: "package",
- parent: groups[groupName],
- name: packageName
- });
- }
-
- package.items.push({
- name: plugin.name,
- enabled: lut[plugin.name].loaded ? "true" : "false",
- time: plugin.time,
- version: info && info.version || "N/A",
- parent: package,
- package: packageName,
- developer: plugin.developer == "Ajax.org"
- ? "Cloud9"
- : plugin.developer
- });
- });
-
- model.cachedRoot = { items: root };
- applyFilter();
- }
-
- function applyFilter() {
- model.keyword = filterbox && filterbox.getValue();
-
- if (!model.keyword) {
- model.reKeyword = null;
- model.setRoot(model.cachedRoot);
-
- // model.isOpen = function(node){ return node.isOpen; }
- }
- else {
- model.reKeyword = new RegExp("("
- + util.escapeRegExp(model.keyword) + ")", 'i');
- var root = search.treeSearch(model.cachedRoot.items, model.keyword, true);
- model.setRoot(root);
-
- // model.isOpen = function(node){ return true; };
- }
- }
-
- function uninstall(name) {
- btnUninstall.setAttribute("caption", "...");
- btnUninstall.disable();
-
- // @TODO first disable the plugin
-
- proc.spawn("c9", { args: ["uninstall", name]}, function(err, p) {
- p.stdout.on("data", function(c) {
-
- });
- p.stderr.on("data", function(c) {
-
- });
- p.on("exit", function(code) {
- if (code) {
- return alert("Could not uninstall plugin",
- "Could not uninstall plugin",
- "Could not uninstall plugin");
- }
-
- btnUninstall.setAttribute("caption", "Uninstall");
- btnUninstall.enable();
- });
- });
- }
-
- function enable(name) {
- try { ext.enablePlugin(name); }
- catch (e) {
- alert("Could not disable plugin",
- "Got an error when disabling plugin: " + name,
- e.message);
- return false;
- }
-
- reloadModel();
- }
-
- function disable(name, callback) {
- var deps = ext.getDependencies(name);
- var plugins = ext.named;
-
- for (var i = 0; i < deps.length; i++) {
- ext.getDependencies(deps[i]).forEach(function(name) {
- if (deps.indexOf(name) == -1)
- deps.push(name);
- });
- }
-
- if (deps.length) {
- confirm("Found " + deps.length + " plugins that depend on this plugin.",
- "Would you like to disable all the plugins that depend on '" + name + "'?",
- "These plugins would also be disabled: " + deps.join(", "),
- // Yes
- function() {
- if (deps.reverse().every(function(name) {
- console.log("Disabling", name);
- return !recurDisable(name);
- })) {
- disable(name);
- reloadModel();
- }
- },
- // No
- function() {
- callback(new Error("User Cancelled"));
- });
- }
- else {
- var e = disable(name);
- if (!e) reloadModel();
- callback(e);
- }
-
- function recurDisable(name) {
- var deps = ext.getDependencies(name);
-
- if (deps.length) {
- if (!deps.every(function(name) {
- return !recurDisable(name);
- })) return false;
- }
-
- return disable(name);
- }
-
- function disable(name) {
- if (!plugins[name].loaded) return;
-
- try { ext.disablePlugin(name); }
- catch (e) {
- alert("Could not disable plugin",
- "Got an error when disabling plugin: " + name,
- e.message);
- return e;
- }
- }
}
function createNewPlugin(template) {
if (!template)
template = "c9.ide.default";
-
- var url = staticPrefix + "/" + join("templates", template + ".tar.gz");
+
+ var tarSourcePath = join("templates", template + ".tar.gz");
+ var url = staticPrefix + "/" + tarSourcePath;
if (!url.match(/^http/))
url = location.origin + url;
@@ -633,8 +93,16 @@ define(function(require, exports, module) {
args: ["-c", [
// using mkdirp since "--create-dirs" is broken on windows
"mkdir", "-p", util.escapeShell(dirname(tarPathAbsolute)), ";",
- "curl", "-L", util.escapeShell(url), "-o", util.escapeShell(tarPathAbsolute)].join(" ")
- ]
+ ].concat(
+ c9.sourceDir
+ ? [ "cp", util.escapeShell(c9.sourceDir + "/plugins/c9.ide.plugins/" + tarSourcePath),
+ util.escapeShell(tarPathAbsolute) ]
+ : [ "curl", "-L",
+ (architectApp.services.onlinedev_helper ? "-k" : ""), // ignore certificate errors in dev mode
+ util.escapeShell(url),
+ "-o", util.escapeShell(tarPathAbsolute)
+ ].filter(Boolean)
+ ).join(" ")]
}, function(err, stderr, stdout) {
if (err)
return handleError(err);
@@ -654,12 +122,19 @@ define(function(require, exports, module) {
// Remove .tar.gz
fs.unlink(tarPath, function() {
+ // using this to allow reloading the tree
+ var tree = architectApp.services["tree"];
+ var favs = architectApp.services["tree.favorites"];
// Add plugin to favorites
favs.addFavorite(dirname(pluginsDir), "plugins");
// Select and expand the folder of the plugin
tree.expandAndSelect(path);
+
+ readAvailablePlugins(function(e) {
+ emit("change");
+ });
});
});
});
@@ -667,40 +142,129 @@ define(function(require, exports, module) {
});
}
- function reload(name) {
- showReloadTip(name);
-
- var href = document.location.href.replace(/[?&]reload=[^&]+/, "");
- href += (href.match(/\?/) ? "&" : "?") + "reload=" + name;
- window.history.replaceState(window.history.state, null, href);
-
- for (var plugin in architect.lut) {
- if (architect.lut[plugin].provides.indexOf(name) < 0)
- continue;
-
- pluginDebug.reloadPackage(plugin);
- return;
- }
- }
-
- function showReloadTip(name) {
- if (options.devel) {
- var key = commands.getHotkey("reloadLastPlugin");
- if (commands.platform == "mac")
- key = apf.hotkeys.toMacNotation(key);
- if (!getLastReloaded()) {
- showInfo("Reloaded " + name + ". Press " + key + " to reload again.", 3000);
- return;
+ function reload(nodes, mode) {
+ var reloadLast = [];
+ nodes.forEach(function(node) {
+ var id;
+ if (node.packageConfig) {
+ var config = node.packageConfig;
+ if (!mode)
+ unloadPackage(config.name);
+ if (mode != false)
+ loadPackage(config.filePath || config.url);
+ id = config.name;
}
+ else {
+ if (!mode)
+ unloadPlugins({ path: node.path });
+ if (mode != false)
+ loadPlugins({ path: node.path });
+ id = node.path;
+ }
+
+ reloadLast.push(id);
+ if (mode == false)
+ disabledPlugins[id] = true;
+ else
+ delete disabledPlugins[id];
+ });
+ return reloadLast;
+ }
+
+ function checkPluginsWithMissingDependencies() {
+ var services = architectApp.services;
+ var plugins = [];
+ getAllPlugins().forEach(function(p) {
+ if (!p.provides.length) return;
+ var packagePath = p.packagePath;
+ var isDisabled = p.provides.every(function(name) {
+ if (!services[name]) return true;
+ if (!services[name].loaded && typeof services[name].load == "function")
+ return true;
+ });
+ if (isDisabled && packagePath) {
+ var packageName = packagePath.split("/")[1];
+ if (disabledPlugins[packageName]) return;
+ while (packagePath && !disabledPlugins[packagePath]) {
+ var i = packagePath.lastIndexOf("/");
+ packagePath = packagePath.slice(0, i > 0 ? i : 0);
+ }
+ if (!packagePath) plugins.push(p);
+ }
+ });
+ if (!plugins.length) return;
+ architectApp.loadAdditionalPlugins(plugins, function(err) {
+ if (err) return showError(err);
+ });
+ }
+
+ function getAllPlugins(includeDisabled) {
+ var config = architectApp.config;
+ Object.keys(packages).forEach(function(n) {
+ if (packages[n] && packages[n].c9 && packages[n].c9.plugins)
+ config = config.concat(packages[n].c9.plugins);
+ });
+ return includeDisabled ? config : config.filter(function(x) {
+ return x.consumes && x.provides;
+ });
+ }
+
+ function addAllDependents(plugins) {
+ var config = getAllPlugins();
+ var level = 0;
+ do {
+ level++;
+ var changed = false;
+ var packageNames = Object.keys(plugins);
+ config.forEach(function(x) {
+ packageNames.forEach(function(p) {
+ if (x.consumes.indexOf(p) != -1) {
+ x.provides.forEach(function(name) {
+ if (plugins[name] == null) {
+ changed = true;
+ plugins[name] = level;
+ }
+ });
+ }
+ });
+ });
+ } while (changed);
+ return plugins;
+ }
+
+ function addAllProviders(plugins) {
+ var serviceToPlugin = architectApp.serviceToPlugin;
+ var level = 0;
+ do {
+ level--;
+ var changed = false;
+ var packageNames = Object.keys(plugins);
+ packageNames.forEach(function(p) {
+ var service = serviceToPlugin[p];
+ if (service && service.consumes) {
+ service.consumes.forEach(function(name) {
+ if (plugins[name] == null) {
+ changed = true;
+ plugins[name] = level;
+ }
+ });
+ }
+ });
+ } while (changed);
+ return plugins;
+ }
+
+ function unloadPackage(options, callback) {
+ if (Array.isArray(options))
+ return async.map(options, unloadPackage, callback || function() {});
+ var name = typeof options == "object" ? options.name : options;
+ if (packages[name]) {
+ packages[name].enabled = false;
+ unloadPlugins(packages[name].path);
+ emit("disablePackage");
}
- showInfo("Reloaded " + name + ".", 1000);
}
- function getLastReloaded() {
- return qs.parse(document.location.search.substr(1)).reload;
- }
-
- var packages = {};
function loadPackage(options, callback) {
if (Array.isArray(options))
return async.map(options, loadPackage, callback || function() {});
@@ -715,8 +279,10 @@ define(function(require, exports, module) {
}
}
+ var url = options.url;
+
if (!options.url && options.path)
- options.url = vfs.vfsUrl(options.path);
+ options.url = vfs.url(options.path);
var parts = options.url.split("/");
var root = parts.pop();
@@ -729,7 +295,7 @@ define(function(require, exports, module) {
if (!options.name || options.name == "json")
options.name = parts[parts.length - 1];
// try parent folder name
- if (/^(.?build|master)/.test(options.name))
+ if (/^(.?build|master|c9build)/.test(options.name))
options.name = parts[parts.length - 2];
// remove version from the name
options.name = options.name.replace(/@.*$/, "");
@@ -744,6 +310,10 @@ define(function(require, exports, module) {
var id = options.rootDir + "/" + name;
var pathMappings = {};
+ if (packages[name]) packages[name].loading = true;
+
+ unloadPlugins("plugins/" + options.name);
+
pathMappings[id] = options.url;
requirejs.config({ paths: pathMappings });
requirejs.undef(id + "/", true);
@@ -751,6 +321,12 @@ define(function(require, exports, module) {
if (/\.js$/.test(root)) {
require([options.url + "/" + root], function(json) {
json = json || require(id + "/" + options.packageName);
+ if (!json) {
+ var err = new Error("Didn't provide " + id + "/" + options.packageName);
+ return addError("Error loading plugin", err);
+ }
+ if (json.name && json.name != name)
+ name = json.name;
getPluginsFromPackage(json, callback);
}, function(err) {
addError("Error loading plugin", err);
@@ -765,6 +341,8 @@ define(function(require, exports, module) {
return addError("Error parsing package.json", e);
}
json.fromVfs = true;
+ // handle the old format
+ if (!json.c9) loadBundleFiles(json, options);
getPluginsFromPackage(json, callback);
});
}
@@ -788,10 +366,11 @@ define(function(require, exports, module) {
if (!packages[name])
packages[name] = {};
packages[name].filePath = options.path;
- packages[name].url = options.url;
+ packages[name].url = url;
packages[name].__error = new Error(message + "\n" + err.message);
+ packages[name].loading = false;
- reloadModel();
+ emit("change");
callback && callback(err);
}
@@ -799,7 +378,7 @@ define(function(require, exports, module) {
function getPluginsFromPackage(json, callback) {
var plugins = [];
if (json.name != name)
- name = json.name;
+ json.name = name;
var unhandledPlugins = json.c9 && json.c9.plugins || json.plugins;
if (unhandledPlugins) {
Object.keys(unhandledPlugins).forEach(function(name) {
@@ -815,7 +394,7 @@ define(function(require, exports, module) {
packages[json.name] = json;
json.filePath = options.path;
- json.url = options.url;
+ json.url = url;
if (!json.c9)
json.c9 = {};
@@ -824,49 +403,287 @@ define(function(require, exports, module) {
json.enabled = true;
json.path = id;
- loadPlugins(plugins, callback);
+ emit("enablePackage", json);
+ loadPlugins(plugins, function(err, result) {
+ if (err) return addError("Error loading plugins", err);
+ if (packages[name])
+ packages[name].loading = false;
+ emit("change");
+ callback && callback(err, result);
+ });
+ }
+
+ function loadBundleFiles(json, options, callback) {
+ var cwd = dirname(options.path);
+ var resourceHolder = new Plugin();
+ fs.readdir(cwd, function(err, files) {
+ if (err) return callback && callback(err);
+ function forEachFile(dir, fn) {
+ fs.readdir(dir, function(err, files) {
+ if (err) return callback && callback(err);
+ files.forEach(function(stat) {
+ fs.readFile(dir + "/" + stat.name, function(err, value) {
+ if (err) return callback && callback(err);
+ fn(stat.name, value);
+ });
+ });
+ });
+ }
+ function parseHeader(data, filename) {
+ var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim();
+ var info = {};
+ firstLine.split(";").forEach(function(n) {
+ var key = n.split(":");
+ if (key.length != 2)
+ return console.error("Ignoring invalid key " + n + " in " + filename);
+ info[key[0].trim()] = key[1].trim();
+ });
+ info.data = firstLine;
+ return info;
+ }
+ function addResource(type) {
+ forEachFile(cwd + "/" + type, function(filename, data) {
+ addStaticPlugin(type, options.name, filename, data, plugin);
+
+ });
+ }
+ function addMode(type) {
+ forEachFile(cwd + "/modes", function(filename, data) {
+ if (/(?:_highlight_rules|_test|_worker|_fold|_behaviou?r)\.js$/.test(filename))
+ return;
+ if (!/\.js$/.test(filename))
+ return;
+ var info = parseHeader(data, cwd + "/modes/" + filename);
+
+ if (!info.caption) info.caption = filename;
+
+ info.type = "modes";
+ info.filename = filename;
+ addStaticPlugin(type, options.name, filename, data, plugin);
+ });
+ }
+ var handlers = {
+ templates: addResource,
+ snippets: addResource,
+ builders: addResource,
+ keymaps: addResource,
+ outline: addResource,
+ runners: addResource,
+ themes: addResource,
+ modes: addMode,
+ };
+ files.forEach(function(stat) {
+ var type = stat.name;
+ if (handlers.hasOwnProperty(type))
+ handlers[type](type);
+ });
+
+ var name = options.name + ".bundle";
+ var bundle = {
+ packagePath: id + "/" + name,
+ consumes: [],
+ provides: [name],
+ setup: function(imports, options, register) {
+ var ret = {};
+ ret[name] = resourceHolder;
+ register(null, ret);
+ }
+ };
+ json.c9.plugins.push(bundle);
+ architectApp.loadAdditionalPlugins([bundle], function() {});
+ });
}
}
function loadPlugins(plugins, callback) {
- architect.loadAdditionalPlugins(plugins, function(err) {
+ if (!Array.isArray(plugins)) {
+ options = plugins;
+ plugins = [];
+ Object.keys(getServiceNamesByPath(options)).forEach(function(n) {
+ var plugin = architectApp.serviceToPlugin[n];
+ if (plugin && plugin.packagePath) {
+ unloadPluginConfig(plugin);
+ plugins.push(plugin);
+ }
+ });
+ }
+
+ architectApp.loadAdditionalPlugins(plugins, function(err) {
+ setTimeout(checkPluginsWithMissingDependencies);
callback && callback && callback(err);
});
}
+
+ function getServiceNamesByPath(options) {
+ var toUnload = Object.create(null);
+ var config = getAllPlugins();
+ function addPath(path) {
+ config.forEach(function(p) {
+ if (!p.packagePath) return;
+ if (p.packagePath.startsWith(path)) {
+ p.provides.forEach(function(name) {
+ toUnload[name] = 0;
+ });
+ }
+ });
+ }
+ if (!options) {
+ return toUnload;
+ }
+ if (typeof options == "string") {
+ addPath(options);
+ }
+ if (options.path) {
+ addPath(options.path);
+ }
+ if (options.paths) {
+ options.path.forEach(addPath);
+ }
+ if (options.services) {
+ options.services.forEach(function(name) {
+ toUnload[name] = 0;
+ });
+ }
+ return toUnload;
+ }
+
+ function unloadPlugins(options, callback) {
+ var toUnload = getServiceNamesByPath(options);
+ addAllDependents(toUnload);
+
+ var services = architectApp.services;
+ var serviceToPlugin = architectApp.serviceToPlugin;
+ Object.keys(toUnload).forEach(function(name) {
+ recurUnload(name);
+ });
+
+ function recurUnload(name) {
+ var service = services[name];
+
+ if (!service || !service.loaded)
+ return;
+
+ // Find all the dependencies
+ var deps = ext.getDependents(service.name);
+
+ // Unload all the dependencies (and their deps)
+ deps.forEach(function(name) {
+ recurUnload(name);
+ });
+
+ console.log(name);
+
+ // Unload plugin
+ service.unload();
+
+ var pluginConfig = serviceToPlugin[name];
+ if (pluginConfig && toUnload[name] == 0)
+ pluginConfig.__userDisabled = true;
+ }
+
+ emit("change");
+ }
+
+ function unloadPluginConfig(plugin) {
+ var url = requirejs.toUrl(plugin.packagePath, ".js");
+ if (!define.fetchedUrls[url]) return false;
+
+ delete plugin.provides;
+ delete plugin.consumes;
+ delete plugin.setup;
+
+ requirejs.undef(plugin.packagePath);
+ return true;
+ }
+
+ // TODO optimize this
+ function addStaticPlugin(type, pluginName, filename, data, plugin) {
+ var services = architectApp.services;
+ var path = "plugins/" + pluginName + "/"
+ + (type == "installer" ? "" : type + "/")
+ + filename.replace(/\.js$/, "");
+
+ switch (type) {
+ case "builders":
+ data = util.safeParseJson(data, function() {});
+ if (!data) return;
+ if (!services.build.addBuilder) return;
+
+ services.build.addBuilder(filename, data, plugin);
+ break;
+ case "keymaps":
+ data = util.safeParseJson(data, function() {});
+ if (!data) return;
+ if (!services["preferences.keybindings"].addCustomKeymap) return;
+
+ services["preferences.keybindings"].addCustomKeymap(filename, data, plugin);
+ break;
+ case "modes":
+ if (!services.ace) return;
+ var mode = {};
+ var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim();
+ firstLine.split(";").forEach(function(n) {
+ var info = n.split(":");
+ if (info.length != 2) return;
+ mode[info[0].trim()] = info[1].trim();
+ });
+
+ services.ace.defineSyntax({
+ name: path,
+ caption: mode.caption || filename,
+ extensions: (mode.extensions || "").trim()
+ .replace(/\s*,\s*/g, "|").replace(/(^|\|)\./g, "$1")
+ });
+ break;
+ case "outline":
+ if (!data) return;
+ if (!services.outline.addOutlinePlugin) return;
+
+ services.outline.addOutlinePlugin(path, data, plugin);
+ break;
+ case "runners":
+ data = util.safeParseJson(data, function() {});
+ if (!data) return;
+
+ services.run.addRunner(data.caption || filename, data, plugin);
+ break;
+ case "snippets":
+ services["language.complete"].addSnippet(data, plugin);
+ break;
+ case "themes":
+ services.ace.addTheme(data, plugin);
+ break;
+ case "templates":
+ services.newresource.addFileTemplate(data, plugin);
+ break;
+ case "installer":
+ if (data) {
+ services.installer.createSession(pluginName, data, function(v, o) {
+ require([path], function(fn) {
+ fn(v, o);
+ });
+ });
+ }
+ else {
+ require([path], function(fn) {
+ services.installer.createSession(pluginName, fn.version, function(v, o) {
+ fn(v, o);
+ });
+ });
+ }
+ default:
+ console.error("Unsupported type", type);
+ }
+ }
+
/***** Lifecycle *****/
plugin.on("load", function() {
- load();
- });
- plugin.on("draw", function(e) {
- draw(e);
- });
- plugin.on("activate", function(e) {
- datagrid && datagrid.resize();
- });
- plugin.on("resize", function(e) {
- datagrid && datagrid.resize();
- });
- plugin.on("enable", function() {
-
- });
- plugin.on("disable", function() {
-
});
plugin.on("unload", function() {
- loaded = false;
- drawn = false;
-
- architect = null;
- model = null;
- datagrid = null;
- filterbox = null;
- btnUninstall = null;
- btnReport = null;
- btnReadme = null;
- btnCloud9 = null;
- btnReload = null;
+ disabledPlugins = null;
+ plugin = null;
});
/***** Register and define API *****/
@@ -878,19 +695,33 @@ define(function(require, exports, module) {
/**
*
*/
- get architect() { throw new Error(); },
- set architect(v) {
- architect = v;
- architect.on("ready-additional", function() {
- reloadModel();
- });
- },
-
+ readAvailablePlugins: readAvailablePlugins,
+
/**
*
*/
createNewPlugin: createNewPlugin,
-
+
+ /**
+ *
+ */
+ getAllPlugins: getAllPlugins,
+
+ /**
+ *
+ */
+ addAllDependents: addAllDependents,
+
+ /**
+ *
+ */
+ addAllProviders: addAllProviders,
+
+ /**
+ *
+ */
+ unloadPackage: unloadPackage,
+
/**
*
*/
@@ -899,27 +730,45 @@ define(function(require, exports, module) {
/**
*
*/
- uninstall: uninstall,
-
+ loadPlugins: loadPlugins,
+
/**
*
*/
- enable: enable,
-
+ reload: reload,
+
/**
*
*/
- disable: disable,
-
+ getServiceNamesByPath: getServiceNamesByPath,
+
/**
*
*/
- reload: reload
+ unloadPlugins: unloadPlugins,
+
+ /**
+ *
+ */
+ unloadPluginConfig: unloadPluginConfig,
+
+ /**
+ *
+ */
+ addStaticPlugin: addStaticPlugin,
+
+
+ get packages() { return packages; },
+ get disabledPlugins() { return disabledPlugins; },
});
+ var shim = new Plugin();
+ shim.addStaticPlugin = addStaticPlugin;
+
register(null, {
"pluginManager": plugin,
- "plugin.manager": plugin
+ "plugin.manager": shim,
+ "plugin.debug": shim,
});
}
});
diff --git a/plugins/c9.ide.plugins/managerdp.js b/plugins/c9.ide.plugins/managerdp.js
index cf695978..eb638e7d 100644
--- a/plugins/c9.ide.plugins/managerdp.js
+++ b/plugins/c9.ide.plugins/managerdp.js
@@ -9,32 +9,6 @@ define(function(require, exports, module) {
oop.inherits(DataProvider, TreeData);
(function() {
-
- this.getChildren = function(node) {
- var children = node.items;
- var ch = children && children[0] && children[0];
- if (ch) {
- var d = (node.$depth + 1) || 0;
- children.forEach(function(n) {
- n.$depth = d;
- n.parent = node;
- });
- }
-
- if (this.$sortNodes && !node.$sorted) {
- children && this.sort(children);
- }
- return children;
- };
-
- // this.getRowIndent = function(p){
- // return !p.className ? 2 : p.className == "package" ? 1 : 0;
- // }
-
- this.hasChildren = function(node) {
- return node.items && node.items.length;
- };
-
this.getCaptionHTML = function(node) {
if (!node.name) return "";
return node.name.replace(this.reKeyword, "$1");
diff --git a/plugins/c9.ide.plugins/packages.js b/plugins/c9.ide.plugins/packages.js
deleted file mode 100644
index 2cfd588b..00000000
--- a/plugins/c9.ide.plugins/packages.js
+++ /dev/null
@@ -1,202 +0,0 @@
-define(function(require, exports, module) {
- main.consumes = [
- "Editor", "editors", "ui", "commands", "menus", "layout",
- "tabManager", "util", "settings", "api", "c9",
- "preferences.experimental"
- ];
- main.provides = ["plugin.packages"];
- return main;
-
- function main(options, imports, register) {
- var Editor = imports.Editor;
- var editors = imports.editors;
- var tabs = imports.tabManager;
- var commands = imports.commands;
- var settings = imports.settings;
- var api = imports.api;
- var c9 = imports.c9;
- var menus = imports.menus;
- var util = imports.util;
- var layout = imports.layout;
- var ui = imports.ui;
- var experimental = imports["preferences.experimental"];
-
- /***** Initialization *****/
-
- var extensions = [];
- var packages = {};
-
- var handle = editors.register("plugin.packages", "Package Browser",
- PackageBrowser, extensions);
- var emit = handle.getEmitter();
- emit.setMaxListeners(1000);
-
- var DEBUG_MODE = c9.location.indexOf("debug=2") > -1;
- var HASSDK = DEBUG_MODE || experimental.addExperiment("sdk", false, "SDK/Load Custom Plugins");
-
- function focusOpenPackages() {
- var pages = tabs.getTabs();
- for (var i = 0, tab = pages[i]; tab; tab = pages[i++]) {
- if (tab.editorType == "plugin.packages") {
- tabs.focusTab(tab);
- return true;
- }
- }
- }
-
- handle.on("load", function() {
- if (!HASSDK) return;
-
- settings.on("read", function() {
- settings.setDefaults("user/general", [["animateui", true]]);
- });
-
- commands.addCommand({
- name: "openpackagebrowser",
- hint: "open the package browser",
- group: "General",
- // bindKey: { mac: "Command-,", win: "Ctrl-," },
- exec: function () {
- var tab = tabs.focussedTab;
- if (tab && tab.editor.type == "plugin.packages") {
- tab.close();
- return;
- }
- if (focusOpenPackages())
- return;
-
- tabs.open({
- editorType: "plugin.packages",
- active: true
- }, function() {});
- }
- }, handle);
-
- // menus.addItemByPath("Cloud9/~", new ui.divider(), 1000, handle);
- // menus.addItemByPath("Cloud9/Package Browser", new ui.item({
- // command: "openpackagebrowser"
- // }), 1100, handle);
- });
-
- /***** Methods *****/
-
- function installPlugin(name, target, callback) {
- var config = packages[name];
- if (!config)
- return callback(new Error("Could not find plugin: " + name));
-
- if (target != "project" && target != "user")
- return callback(new Error("Invalid installation target: " + target));
-
- api[target].put("install/" + name, function(err, state) {
- callback(err);
- });
- }
-
- function uninstallPlugin(name, target, callback) {
- var config = packages[name];
- if (!config)
- return callback(new Error("Could not find plugin: " + name));
-
- if (target != "project" && target != "user")
- return callback(new Error("Invalid installation target: " + target));
-
- api[target].put("uninstall/" + name, function(err, state) {
- callback(err);
- });
- }
-
- /***** Register and define API *****/
-
- /**
- *
- * @extends Plugin
- * @singleton
- */
- handle.freezePublicAPI({
- /**
- *
- */
- installPlugin: installPlugin,
-
- /**
- *
- */
- uninstallPlugin: uninstallPlugin
- });
-
- /***** Editor *****/
-
- function PackageBrowser() {
- var plugin = new Editor("Ajax.org", main.consumes, extensions);
- //var emit = plugin.getEmitter();
- var tab, iframe;
-
- plugin.on("resize", function(e) {
- emit("resize", e);
- });
-
- plugin.on("draw", function(e) {
- tab = e.tab;
- var htmlNode = e.htmlNode;
-
- htmlNode.style.paddingTop = 0;
-
- iframe = htmlNode.appendChild(document.createElement("iframe"));
- iframe.style.width = "100%";
- iframe.style.height = "100%";
- iframe.style.border = 0;
- iframe.style.backgroundColor = "#fbfbfb";
-
- iframe.src = location.origin.replace("ide.", "") + "/profile/packages?nobar=1&pid=" + c9.projectId;
- });
-
- plugin.on("getState", function(e) {
- });
- plugin.on("setState", function(e) {
- });
-
- /***** Lifecycle *****/
-
- plugin.on("documentLoad", function(e) {
- var doc = e.doc;
- doc.title = "Package Browser";
-
- function setTheme() {
- var bg = "#fbfbfb";
- doc.tab.backgroundColor = bg;
-
- if (util.shadeColor(bg, 1).isLight)
- doc.tab.classList.remove("dark");
- else
- doc.tab.classList.add("dark");
- }
-
- layout.on("themeChange", setTheme, e.doc);
- setTheme();
- });
-
- plugin.on("documentActivate", function(e) {
-
- });
-
- /***** Register and define API *****/
-
- /**
- *
- * @extends Editor
- */
- plugin.freezePublicAPI({
-
- });
-
- plugin.load(null, "plugin.packages");
-
- return plugin;
- }
-
- register(null, {
- "plugin.packages": handle
- });
- }
-});
\ No newline at end of file
diff --git a/plugins/c9.ide.plugins/style.css b/plugins/c9.ide.plugins/style.css
index ce014210..7436e995 100644
--- a/plugins/c9.ide.plugins/style.css
+++ b/plugins/c9.ide.plugins/style.css
@@ -4,3 +4,17 @@
.bk-window.dialog-updater .bk-win-footer {
display: none;
}
+.serviceButton {
+ cursor: default;
+}
+.serviceButton:hover, .serviceButtonActive {
+ color: @preferences-intro-link-color;
+ text-decoration : underline;
+}
+.tree-row.load-error {
+ color: #f99145;
+}
+h1.pluginManagerHeader:after {
+ background: none;
+ content: "-";
+}
\ No newline at end of file
diff --git a/plugins/c9.ide.plugins/updater-npm.js b/plugins/c9.ide.plugins/updater-npm.js
deleted file mode 100644
index 13d89abc..00000000
--- a/plugins/c9.ide.plugins/updater-npm.js
+++ /dev/null
@@ -1,417 +0,0 @@
-define(function(require, exports, module) {
- "use strict";
-
- main.consumes = [
- "Plugin",
- "c9", "proc", "Dialog", "ui",
- ];
- main.provides = ["plugin.updater.npm"];
- return main;
-
- function main(options, imports, register) {
- var Plugin = imports["Plugin"];
- var c9 = imports["c9"];
- var proc = imports["proc"];
- var Dialog = imports["Dialog"];
- var ui = imports["ui"];
-
- var async = require("async");
- var path = require("path");
- var semverCompare = require("semver-compare");
-
- var NPM_MIN_VERSION = "2.6.0";
-
- /***** Initialization *****/
-
- var npmBin = options.npmBin || "/home/ubuntu/.nvm/nvm-exec";
- var managedPath = options.managedPath || "/home/ubuntu/.c9/managed";
-
- var managedRcPath = [managedPath, ".npmrc"].join("/");
- var managedNpmPath = [managedPath, "npm"].join("/");
- var managedEtcPath = [managedNpmPath, "etc"].join("/");
- var managedCachePath = [managedPath, "npm", "cache"].join("/");
- var managedPluginsPath = [managedPath, "plugins"].join("/");
- var managedModulesPath = [managedPath, "node_modules"].join("/");
-
- var plugin = new Plugin("Ajax.org", main.consumes);
-
- function load() {
- ui.insertCss(require("text!./style.css"), false, plugin);
-
- var pkgs = options.packages;
-
- if (!pkgs) {
- console.warn("[plugin.updater.npm] no managed packages configured, not loading.");
- return;
- }
-
- // TODO: DRY error handling
-
- fsMkdirs([ managedPath, managedEtcPath, managedModulesPath, managedPluginsPath ], function(err) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- showErrorDialog(err);
- return;
- }
-
- fsWriteNpmrc(function(err) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- showErrorDialog(err);
- return;
- }
-
- npmCheckVersion(function(err) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- showErrorDialog(err);
- return;
- }
-
- // TODO: clean up the flow for detecting and installing missing
- // packages. the nested functions are messy.
-
- async.filter(pkgs, function(pkg, done) {
- npmExplorePath(pkg, function(err, path) {
- var isMissing = !!err;
-
- if (isMissing)
- debug("missing package:", pkg, err);
-
- done(isMissing);
- });
- }, function(missing) {
- if (missing.length) {
- console.info("[plugin.updater.npm] Installing missing plugins:", missing);
- showUpdateDialog();
-
- npmInstall(missing, function(err) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- showErrorDialog(err);
- return;
- }
-
- buildLinks(pkgs, function(err) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- showErrorDialog(err);
- return;
- }
-
- // reload browser
- window.location.reload();
- });
- });
- }
- else {
- npmOutdated(pkgs, function(err, outdated) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- showErrorDialog(err);
- return;
- }
-
- if (!outdated) {
- debug("Plugins up-to-date.");
- return;
- }
-
- console.info("[plugin.updater.npm] Updating outdated plugins:", outdated);
- showUpdateDialog();
-
- buildLinks(pkgs, function(err) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- showErrorDialog(err);
- return;
- }
-
- npmUpdate(outdated, function(err) {
- if (err) {
- console.error("[plugin.updater.npm]", err);
- alert("[plugin.updater.npm] Error: " + err.message);
- }
-
- // reload browser
- window.location.reload();
- });
- });
- });
- }
- });
- });
- });
- });
- }
-
- function unload() {
- }
-
- /* -- Helper Functions ----- */
-
- function debug(format) {
- if (c9.debug) {
- var util = require("util");
- console.info(util.format.apply(null,
- Array.prototype.slice.call(arguments)
- ));
- }
- }
-
- /* -- npm Management ----- */
-
- /**
- * Check that the installed npm version matches the NPM_MIN_VERSION
- * required
- *
- * @param {Function} callback
- * @param {Error=} callback.err An error if the version is lower than required
- */
- function npmCheckVersion(callback) {
- npmExec("npm", ["-v"], function(err, stdout, stderr) {
- if (err) return callback(err, stdout, stderr);
-
- var version = stdout;
-
- debug("npm version", version);
-
- if (semverCompare(version, NPM_MIN_VERSION) === -1) {
- var error = new Error("npm version " + NPM_MIN_VERSION + " or greater required");
- return callback(error);
- }
-
- callback();
- });
- }
-
- function npmExec(command, args, callback) {
- debug(npmBin, { args: [ "npm", command ].concat(args) });
-
- proc.execFile(npmBin, {
- args: [ "npm", command ].concat(args),
- cwd: managedPath,
- env: {
- "npm_config_production": "true",
- "npm_config_depth": 0,
- "npm_config_userconfig": "/dev/null",
- "npm_config_prefix": managedNpmPath,
- "npm_config_cache": managedCachePath,
- },
- }, function(err, stdout, stderr) {
- debug([err, stdout, stderr]);
-
- if (err) return callback(err, stdout, err.message);
-
- stdout = stdout.trimRight();
- stderr = stderr.trimRight();
-
- callback(null, stdout, stderr);
- });
- }
-
- /* -- Package Management ----- */
-
- function npmOutdated(pkgs, callback) {
- npmExec("outdated", ["--json"], function(err, stdout, stderr) {
- if (err)
- return callback(err, null, stdout, stderr);
-
- if (!stdout)
- return callback(null, null, stdout, stderr);
-
- var outdated = JSON.parse(stdout);
- outdated = Object.keys(outdated);
-
- callback(null, outdated, stdout, stderr);
- });
- }
-
- function npmInstall(pkgs, callback) {
- npmExec("install", ["--"].concat(pkgs), function(err, stdout, stderr) {
- callback(err, stdout, stderr);
- });
- }
-
- function npmUpdate(pkgs, callback) {
- npmExec("update", ["--"].concat(pkgs), function(err, stdout, stderr) {
- callback(err, stdout, stderr);
- });
- }
-
- function npmExplorePath(pkg, callback) {
- npmExec("explore", [pkg, "--", "pwd"], function(err, stdout, stderr) {
- if (err)
- return callback(err, null, stderr);
-
- callback(null, stdout, stderr);
- });
- }
-
- /**
- * Build the symbolic links from `~/.c9/plugins` to the managed plugins.
- *
- * @param {String[]} pkgs A list of package names to link.
- *
- * @param {Function} callback
- * @param {Error=} callback.err
- */
- function buildLinks(pkgs, callback) {
- async.each(pkgs, function(pkg, done) {
- npmExplorePath(pkg, function(err, pkgPath) {
- if (err) return done(err);
- fsLink(pkgPath, done);
- });
- }, callback);
- }
-
- /**
- * Removes symbolic links from the `~/.c9/managed/plugins` folder.
- */
- function fsRmLinks(callback) {
- debug("find", { args: [ managedPluginsPath, "-maxdepth", "1", "-type", "l", "-exec", "rm", "{}", ";" ]});
-
- // find . -maxdepth 1 -type l -exec rm {} \;
-
- proc.execFile("find", {
- args: [
- managedPluginsPath,
- "-maxdepth", "1",
- "-type", "l",
- "-exec", "rm", "{}", ";"
- ],
- }, function(err, stdout, stderr) {
- debug([err, stdout, stderr]);
- callback(err, stdout, stderr);
- });
- }
-
- /**
- * Create a symbolic link in `~/.c9/plugins` pointing to the given
- * plugin path.
- *
- * @param {String} pkgPath Path to the source package folder
- */
- function fsLink(pkgPath, callback) {
- debug("ln", { args: [ "-s", "-f", pkgPath, [ managedPluginsPath, "." ].join("/") ]});
-
- proc.execFile("ln", {
- args: [
- "-s", "-f",
- pkgPath,
- managedPluginsPath + "/.",
- ],
- }, function(err, stdout, stderr) {
- debug([err, stdout, stderr]);
- callback(err, stdout, stderr);
- });
- }
-
- function fsMkdirs(dirPaths, callback) {
- debug("mkdir", { args: [ "-p", "--" ].concat(dirPaths) });
-
- proc.execFile("mkdir", {
- args: [ "-p", "--" ].concat(dirPaths),
- }, function(err, stdout, stderr) {
- callback(err, stdout, stderr);
- });
- }
-
- function fsWriteNpmrc(callback) {
- var config = [
- "//registry.npmjs.org/:_authToken = a7c61f6e-5b10-41db-947f-8bc8f1f9468b",
- "production = true",
- "depth = 0",
- "userconfig = /dev/null",
- "prefix = " + managedNpmPath,
- "cache = " + managedCachePath,
- ];
-
- //
- // HACK: - fs.writeFile() does not always work? we are using echo
- // instead
- //
- // - config is not escaped
- //
-
- debug("sh", { args: [ "-c", "echo '" + config.join("\\n") + "' > " + managedRcPath ]});
-
- proc.execFile("sh", {
- args: [
- "-c",
- "echo '" + config.join("\\n") + "' > " + managedRcPath
- ],
- }, function(err, stdout, stderr) {
- callback(err, stdout, stderr);
- });
- }
-
- /* -- Interface ----- */
-
- /**
- * Show a modal upgrade progress dialog, blocking the IDE interface
- * while we are updating the plugins.
- */
- function showUpdateDialog() {
- var dialog = new Dialog("Ajax.org", [], {
- name: "plugin.updater.npm.dialog",
- class: "dialog-updater",
- allowClose: false,
- modal: true,
- elements: [
- ],
- });
-
- dialog.title = "Installing Updates";
- dialog.heading = "";
- dialog.body = "Your workspace will be updated to the newest version and reload automatically.";
-
- dialog.show();
-
- return dialog;
- }
-
- /**
- * Show an upgrade error dialog, requesting to delete and recreate the
- * workspace. This is shown for critical update errors.
- */
- function showErrorDialog(err) {
- var dialog = new Dialog("Ajax.org", [], {
- name: "plugin.updater.npm.error_dialog",
- allowClose: true,
- modal: true,
- elements: [
- ],
- });
-
- var errorMessage = (err && err.message) ? "" + err.message : err;
-
- dialog.title = "Error installing updates";
- dialog.heading = "";
- dialog.body = "Important updates could not be installed on this workspace.
"
- + "Please delete this workspace and create a new one, in order to continue "
- + "working in an up-to-date environment.
"
- + "" + errorMessage + "
";
-
- dialog.show();
-
- return dialog;
- }
-
- /***** Register and define API *****/
-
- plugin.on("load", load);
- plugin.on("unload", unload);
-
- /**
- * @class salesforc.sync
- */
- plugin.freezePublicAPI({
- });
-
- register(null, {
- "plugin.updater.npm": plugin
- });
- }
-});
-
diff --git a/plugins/c9.ide.save/save.js b/plugins/c9.ide.save/save.js
index c8eed027..c52361af 100644
--- a/plugins/c9.ide.save/save.js
+++ b/plugins/c9.ide.save/save.js
@@ -98,7 +98,6 @@ define(function(require, exports, module) {
name: "reverttosavedall",
hint: "downgrade the all open tabs to the last saved version",
bindKey: { mac: "Option-Shift-Q", win: "Alt-Shift-Q" },
- isAvailable: available,
exec: function () {
revertToSavedAll();
}
diff --git a/plugins/c9.ide.ui/lib/menu/menu.js b/plugins/c9.ide.ui/lib/menu/menu.js
index d5e2e405..1118e70e 100644
--- a/plugins/c9.ide.ui/lib/menu/menu.js
+++ b/plugins/c9.ide.ui/lib/menu/menu.js
@@ -1520,7 +1520,7 @@ apf.item = function(struct, tagName) {
return;
var opener = this.parentNode.opener;
- if (opener && opener.parentNode.$showingSubMenu)
+ if (opener && opener.parentNode && opener.parentNode.$showingSubMenu)
selectItem(opener);
if (!force && (apf.isChildOf(this.$ext, e.toElement || e.explicitOriginalTarget)
diff --git a/plugins/c9.ide.ui/widgets.less b/plugins/c9.ide.ui/widgets.less
index adc65842..45dc6776 100644
--- a/plugins/c9.ide.ui/widgets.less
+++ b/plugins/c9.ide.ui/widgets.less
@@ -65,6 +65,22 @@
background-color: #565656;
background-position: -2px -18px;
}
+.ace_tree .tree-row .checkbox.loading:after{
+ content:" ";
+ position:relative;
+ display:block;
+ top:0; left:0;
+ width: 100%;
+ height: 100%;
+ border-radius: 20%;
+ background-color: @color;
+ animation: scaleout 1.2s infinite ease-in-out
+}
+@keyframes scaleout {
+ 0% { transform: scale(0) }
+ 80% { transform: scale(1); opacity: 0;}
+ 100% { transform: scale(1); opacity: 0;}
+}
.custom-tree.ace_tree .tree-row .toggler{
.image-2x("@{tree-arrow}", 20px, 10px);
diff --git a/plugins/c9.vfs.standalone/views/standalone.html.ejs b/plugins/c9.vfs.standalone/views/standalone.html.ejs
index 1468c436..d51634aa 100644
--- a/plugins/c9.vfs.standalone/views/standalone.html.ejs
+++ b/plugins/c9.vfs.standalone/views/standalone.html.ejs
@@ -14,7 +14,7 @@
return obj;
}
window.isLocalVersion = nRequire ? true : false;
- window.require = undefined;
+ window.require = window.module = undefined;
@@ -64,8 +64,6 @@
}
});
- app.lut = {};
-
app.on("error", function(err){
console.error(err.stack);
if (!errored)
@@ -73,20 +71,11 @@
});
app.on("service", function(name, plugin, options){
- if (name == "plugin.loader" || name == "plugin.installer"
- || name == "plugin.debug" || name == "plugin.manager"
- || name == "plugin.test")
- plugin.architect = app;
if (!plugin.name)
plugin.name = name;
- if (options)
- app.lut[(options.packagePath || "").replace(/^.*\/home\/.c9\//, "")] = options;
});
- app.on("ready", function(){
- if (app.services.configure)
- app.services.configure.services = app.services;
-
+ app.once("ready", function() {
window.app = app.services;
window.app.__defineGetter__("_ace", function(){
return this.tabManager.focussedTab.editor.ace;
diff --git a/plugins/c9.vfs.standalone/www/ide.html b/plugins/c9.vfs.standalone/www/ide.html
index c5479419..e20a98f1 100644
--- a/plugins/c9.vfs.standalone/www/ide.html
+++ b/plugins/c9.vfs.standalone/www/ide.html
@@ -124,29 +124,18 @@
}
});
- app.lut = {};
-
app.on("error", function(err){
console.error(err.stack);
if (!errored)
alert(err);
});
- app.on("service", function(name, plugin, options){
- if (name == "plugin.loader" || name == "plugin.installer"
- || name == "plugin.debug" || name == "plugin.manager"
- || name == "plugin.test")
- plugin.architect = app;
+ app.on("service", function(name, plugin, options) {
if (!plugin.name)
plugin.name = name;
- if (options)
- app.lut[(options.packagePath || "").replace(/^.*\/home\/.c9\//, "")] = options;
});
app.on("ready", function() {
- if (app.services.configure)
- app.services.configure.services = app.services;
-
window.app = app.services;
window.app.__defineGetter__("_ace", function(){
return this.tabManager.focussedTab.editor.ace;