diff --git a/.gitignore b/.gitignore index 1ee326ab..0253b158 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build/installer/ -build/win32 +build/sdk +build/win32* build/node.exe build/node*.zip build/Cloud9-dev.app @@ -9,6 +10,10 @@ plugins/c9.vfs.standalone/www/less.html plugins/c9.ide.run/todo plugins/c9.vfs.standalone/www/charts plugins/c9.ide.language.javascript.tern/util/sigs_ts +plugins/c9.account.billing/node_modules/ +plugins/c9.account/node_modules/ +plugins/c9.api.project/coverage/ +plugins/c9.db.redis/coverage/ scripts/build build/output build/standalone/ @@ -76,3 +81,4 @@ plugins/c9.account/node_modules/ npm-debug.log foo.js hello.js +plugins/c9.docker/containers/ci/files/github_c9_newclient diff --git a/README.md b/README.md index c16e3ba8..2cdc0732 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,18 @@ The following options can be used: Now visit [http://localhost:8181/ide.html](http://localhost:8181/ide.html) to load Cloud9. +#### Running Tests #### + +Run server side tests with: + + npm run test + +Run client side tests with: + + npm run ctest + +Then visit [http://localhost:8181/static/test](http://localhost:8181/static/test) in your browser. + #### Contributing #### We actively encourage and support contributions. We accept pull requests to the core as well as to any of the open source plugins and libraries that we maintain under the c9 organization on GitHub. diff --git a/configs/client-default.js b/configs/client-default.js index 5f42b148..69da96b9 100644 --- a/configs/client-default.js +++ b/configs/client-default.js @@ -1,6 +1,5 @@ var assert = require("assert"); - module.exports = function(options) { assert(options.staticPrefix, "Option 'staticPrefix' must be set"); assert(options.workspaceDir, "Option 'workspaceDir' must be set"); @@ -14,7 +13,6 @@ module.exports = function(options) { var collab = options.collab; var packaging = options.packaging; var staticPrefix = options.staticPrefix; - var ssh = options.ssh; var nodeBin = options.nodeBin || ["node"]; var nodePath = options.nodePath || ""; @@ -48,6 +46,7 @@ module.exports = function(options) { env: options.env || "devel", home: options.home, platform: options.platform, + arch: options.arch, installed: options.installed, projectId: options.project.id, projectName: options.projectName || "Project", @@ -93,7 +92,6 @@ module.exports = function(options) { "plugins/c9.vfs.client/vfs.ping", { packagePath: "plugins/c9.vfs.client/vfs_client", - withInstall: false, debug: debug, installPath: options.installPath, dashboardUrl: options.dashboardUrl, @@ -157,6 +155,10 @@ module.exports = function(options) { staticPrefix: staticPrefix + "/plugins/c9.ide.layout.classic", defaultShow: options.local }, + { + packagePath: "plugins/c9.ide.metrics/metrics", + hosted: hosted + }, // Ace && Commands "plugins/c9.ide.keys/commands", @@ -220,7 +222,10 @@ module.exports = function(options) { autoInit: !options.local }, "plugins/c9.ide.ui/forms", - "plugins/c9.ide.ui/widgets.list", + { + packagePath: "plugins/c9.ide.ui/widgets.list", + staticPrefix: staticPrefix + "/plugins/c9.ide.layout.classic" + }, "plugins/c9.ide.ui/widgets.tree", "plugins/c9.ide.ui/widgets.datagrid", "plugins/c9.ide.ui/focus", @@ -355,6 +360,8 @@ module.exports = function(options) { "plugins/c9.ide.run.debug/variables", "plugins/c9.ide.run.debug/watches", "plugins/c9.ide.run.debug/liveinspect", + + "plugins/c9.ide.run.debug.xdebug/xdebug", // Console { @@ -390,7 +397,34 @@ module.exports = function(options) { }, { packagePath: "plugins/c9.ide.console/console", - staticPrefix: staticPrefix + "/plugins/c9.ide.layout.classic" + staticPrefix: staticPrefix + "/plugins/c9.ide.layout.classic", + defaultState: options.project.scmurl ? { + type: "pane", + nodes: [{ + type: "tab", + editorType: "terminal", + active: "true", + document: { + changed: false, + meta: { + timestamp: Date.now() + }, + filter: true, + title: "bash - \"Cloning ...\"", + tooltip: "bash - \"Cloning ...\"", + terminal: { + id: "clone", + cwd: "" + } + } + }, { + type: "tab", + editorType: "immediate", + document: { + title: "Immediate" + } + }] + } : null }, // Layout & Panels @@ -463,7 +497,28 @@ module.exports = function(options) { }, "plugins/c9.ide.panels/panel", "plugins/c9.ide.panels/area", - "plugins/c9.ide.installer/installer_mock", + + // Installer + { + packagePath: "plugins/c9.ide.installer/gui", + staticPrefix: staticPrefix + "/plugins/c9.ide.layout.classic", + }, + "plugins/c9.automate/automate", + "plugins/c9.ide.installer/commands/centos", + "plugins/c9.ide.installer/commands/bash", + "plugins/c9.ide.installer/commands/npm", + "plugins/c9.ide.installer/commands/symlink", + { + packagePath: "plugins/c9.ide.installer/commands/tar.gz", + bashBin: options.bashBin + }, + "plugins/c9.ide.installer/commands/ubuntu", + { + packagePath: "plugins/c9.ide.installer/installer", + homeDir: options.homeDir, + installSelfCheck: true, + installPath: options.installPath + }, // Previewer { @@ -501,6 +556,8 @@ module.exports = function(options) { "plugins/c9.ide.format/formatters/jsbeautify", // Other + "plugins/c9.ide.download/download", + { packagePath: "plugins/c9.ide.info/info", installPath: options.installPath, diff --git a/configs/standalone.js b/configs/standalone.js index 22a63d77..40a6cd7f 100644 --- a/configs/standalone.js +++ b/configs/standalone.js @@ -139,6 +139,7 @@ module.exports = function(config, optimist) { whitelist: { "c9.core": true, "c9.fs": true, + "c9.automate": true, "c9.login.client": true, "c9.vfs.client": true, "c9.cli.bridge": true, @@ -150,6 +151,7 @@ module.exports = function(config, optimist) { "./c9.nodeapi/nodeapi", { packagePath: "./c9.vfs.standalone/standalone", + sdk: config.sdk, local: config.local, packed: argv.packed, collab: config.collab, diff --git a/local/index.html b/local/index.html index 35c9e6fc..7a262984 100644 --- a/local/index.html +++ b/local/index.html @@ -428,7 +428,11 @@ fatalError(err); }); - app.on("service", function(name, plugin){ + app.on("service", function(name, plugin) { + if (name == "plugin.loader" || name == "plugin.installer" || name == "plugin.debug" || name == "plugin.manager") + plugin.architect = app; // TODO why we rename app to architect? + if (name == "c9") + plugin.startLoadTime = start; if (!plugin.name) plugin.name = name; }); diff --git a/node_modules/ace/lib/ace/editor.js b/node_modules/ace/lib/ace/editor.js index 1dc7321b..1768c0a2 100644 --- a/node_modules/ace/lib/ace/editor.js +++ b/node_modules/ace/lib/ace/editor.js @@ -275,6 +275,10 @@ var Editor = function(renderer, session) { this.setSession = function(session) { if (this.session == session) return; + + // make sure operationEnd events are not emitted to wrong session + if (this.curOp) this.endOperation(); + this.curOp = {}; var oldSession = this.session; if (oldSession) { @@ -372,6 +376,8 @@ var Editor = function(renderer, session) { oldSession: oldSession }); + this.curOp = null; + oldSession && oldSession._signal("changeEditor", {oldEditor: this}); session && session._signal("changeEditor", {editor: this}); }; diff --git a/node_modules/ace/lib/ace/selection.js b/node_modules/ace/lib/ace/selection.js index b712fc6b..ed57682f 100644 --- a/node_modules/ace/lib/ace/selection.js +++ b/node_modules/ace/lib/ace/selection.js @@ -923,7 +923,7 @@ var Selection = function(session) { this.toSingleRange(data[0]); for (var i = data.length; i--; ) { var r = Range.fromPoints(data[i].start, data[i].end); - if (data.isBackwards) + if (data[i].isBackwards) r.cursor = r.start; this.addRange(r, true); } diff --git a/node_modules/ace_tree/lib/ace_tree/data_provider.js b/node_modules/ace_tree/lib/ace_tree/data_provider.js index b57d6a77..1f4926c3 100644 --- a/node_modules/ace_tree/lib/ace_tree/data_provider.js +++ b/node_modules/ace_tree/lib/ace_tree/data_provider.js @@ -319,7 +319,6 @@ var DataProvider = function(root) { index = Math.min(Math.max(0, index), this.visibleItems.length - 1); return this.visibleItems[index]; }; - this.getIconHTML = function(node) { return ""; }; diff --git a/node_modules/ace_tree/lib/ace_tree/layer/cells.js b/node_modules/ace_tree/lib/ace_tree/layer/cells.js index 58721e89..fa583eff 100644 --- a/node_modules/ace_tree/lib/ace_tree/layer/cells.js +++ b/node_modules/ace_tree/lib/ace_tree/layer/cells.js @@ -98,6 +98,7 @@ var Cells = function(parentEl) { ? (provider.isOpen(datarow) ? "open" : "closed") : "empty") + "'>" + + (provider.getCheckboxHTML ? provider.getCheckboxHTML(datarow) : "") + provider.getIconHTML(datarow) + ( provider.getContentHTML ? provider.getContentHTML(datarow) : "" diff --git a/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js b/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js index 2cab0499..7fd87c06 100644 --- a/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js +++ b/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js @@ -136,6 +136,14 @@ function DefaultHandlers(mouseHandler) { } } this.$clickNode = null; + } else if (dom.hasCssClass(target, "checkbox")) { + node.isChecked = !node.isChecked; + if (inSelection) { + var nodes = editor.selection.getSelectedNodes(); + nodes.forEach(function(n){ n.isChecked = node.isChecked }); + } + provider._signal(node.isChecked ? "check" : "uncheck", inSelection ? nodes : [node]); + provider._signal("change") } else if (dom.hasCssClass(target, "icon-ok")) { if (ev.getShiftKey()) { editor.selection.expandSelection(node, null, true); diff --git a/node_modules/architect-build/build.js b/node_modules/architect-build/build.js index 48888813..f59581f3 100644 --- a/node_modules/architect-build/build.js +++ b/node_modules/architect-build/build.js @@ -1,8 +1,9 @@ -var fs = require("fs"); -var path = require("path"); -var async = require("async"); -var mkdirp = require("mkdirp"); +var async = require("async"); +var fs = require("fs"); +var less = require("less"); +var mkdirp = require("mkdirp"); var moduleDeps = require("./module-deps"); +var path = require("path"); function build(config, opts, callback){ if (!opts.configFile) { @@ -157,7 +158,6 @@ function build(config, opts, callback){ }); } -// TODO function createOutputFolder(opts, cb) { var output = (opts.outputFolder || ".") + "/" + (opts.outputFile || ""); output = path.dirname(output); @@ -213,43 +213,80 @@ function compileLess(opts, sources, callback) { }); } + +function compileLibRules(libs, ctx, next) { + // Libs is an array of paths; adds property .compiled to keep a state(cache). + if (libs.compiled) + return next(null, libs.compiled.rules, libs.compiled.css); + + var src = lessPathLib(libs.staticPrefix) + libs.join("\n"); + + less.parse(src, ctx, function(err, root, imports, options) { + if (err) return next(err); + + toCss(root, imports, options, function(err, css) { + if (err) return next(err); + + libs.compiled = { + rules: root.rules, + css: css, + }; + + next(null, libs.compiled.rules, libs.compiled.css); + }); + }); +} + function compileLessFragment(css, libs, root, staticPrefix, path, compress, callback) { - var less = require("less"); - var baseLib = lessPathLib(staticPrefix || libs.staticPrefix); - var parser = new less.Parser({ + + var ctx = { paths: ["/"], filename: root + '/unknown.less', - }); - - if (!libs.compiled) { - parser.parse(lessPathLib(libs.staticPrefix) + libs.join("\n"), function(err, tree) { - libs.compiled = tree; // actually parse isn't async - }); - - libs.css = libs.compiled.toCSS().trim(); - } - // Parse Less Code - var code = baseLib + "\n" + css; - - // Complete paths, but not subdirectories like foo/images/bar.png - code = code.replace(/(["(])(images|icons)\//g, "$1" + staticPrefix + "/$2/"); + compress: !!compress + }; - console.log("[Less] compiling ", path || "skin", root); - parser.parse(code, function (err, tree) { - if (err) - return callback(err); - if (libs.compiled) - tree.rules = libs.compiled.rules.concat(tree.rules); - var css = tree.toCSS({ compress: compress }); - if (libs.css && css.substring(0, libs.css.length) == libs.css) { - css = css.substr(libs.css.length); - } else { - console.warn("couldn't strip default less"); - } - callback(null, css); + compileLibRules( libs, ctx, function(err, libRules, libCss) { + if (err) return callback(err); + + var baseLib = lessPathLib(staticPrefix || libs.staticPrefix); + var code = baseLib + "\n" + css; + + // Complete paths, but not subdirectories like foo/images/bar.png + code = code.replace(/(["(])(images|icons)\//g, "$1" + staticPrefix + "/$2/"); + + console.log("[Less] compiling ", path || "skin", root); + + less.parse(code, ctx, function (err, tree, imports, options) { + if (err) return callback(err); + tree.rules = libRules.concat(tree.rules); + + toCss(tree, imports, options, function(err, css) { + if (err) return callback(err); + + if (css.substring(0, libCss.length) == libCss) { + css = css.substr(libCss.length); + } else { + console.warn("couldn't strip default less"); + } + callback(null, css); + + }); + }); }); } +function toCss(tree, imports, options, callback) { + var parseTree = new less.ParseTree(tree, imports); + var css; + try { + css = parseTree.toCSS(options).css; + } + catch (err) { + return callback(err); + } + callback(null, css); +} + function lessPathLib(staticPrefix) { if (!staticPrefix) return ""; return "@base-path : \"" + staticPrefix + "\";\n" diff --git a/node_modules/architect/architect.js b/node_modules/architect/architect.js index 512f2994..17c5fe43 100644 --- a/node_modules/architect/architect.js +++ b/node_modules/architect/architect.js @@ -448,7 +448,8 @@ function Architect(config) { }); } - var packageName = (plugin.packagePath || "").replace(/.*plugins\/([^\/]*)\/.*$/, "$1"); + var m = /^plugins\/([^\/]+)|\/plugins\/[^\/]+\/([^\/]+)/.exec(plugin.packagePath); + var packageName = m && (m[1] || m[2]); if (!app.packages[packageName]) app.packages[packageName] = []; if (DEBUG) { diff --git a/node_modules/c9/git.js b/node_modules/c9/git.js index 307d5402..dea02285 100644 --- a/node_modules/c9/git.js +++ b/node_modules/c9/git.js @@ -5,7 +5,7 @@ require("amd-loader"); var Fs = require("fs"); var Path = require("path"); var exec = require("child_process").exec; -exports.parse = require("./git_url_parse"); +exports.parse = require("./scm_url_parse"); exports.isValidUrl = function(url) { return !!exports.parse(url); diff --git a/node_modules/c9/git_test.js b/node_modules/c9/git_test.js index 04234bfc..4954438a 100644 --- a/node_modules/c9/git_test.js +++ b/node_modules/c9/git_test.js @@ -18,37 +18,6 @@ module.exports = { assert.equal(git.isValidUrl("god"), false); }, - "test parse ssh url" : function() { - var url = git.parse("git@github.com:fjakobs/lispjs.git"); - assert.equal(url.protocol, "ssh:"); - assert.equal(url.auth, "git"); - assert.equal(url.hostname, "github.com"); - assert.equal(url.pathname, "fjakobs/lispjs.git"); - }, - - "test parse git url" : function() { - var url = git.parse("git://github.com/fjakobs/lispjs.git"); - assert.equal(url.protocol, "git:"); - assert.equal(url.hostname, "github.com"); - assert.equal(url.pathname, "fjakobs/lispjs.git"); - }, - - "test parse https url" : function() { - var url = git.parse("https://fjakobs@github.com/fjakobs/lispjs.git"); - assert.equal(url.protocol, "https:"); - assert.equal(url.auth, "fjakobs"); - assert.equal(url.hostname, "github.com"); - assert.equal(url.pathname, "fjakobs/lispjs.git"); - }, - - "test parse Bitbucket url" : function() { - var url = git.parse("git@bitbucket.org/Richard/expressling.git"); - assert.equal(url.protocol, "ssh:"); - assert.equal(url.auth, "git"); - assert.equal(url.hostname, "bitbucket.org"); - assert.equal(url.pathname, "Richard/expressling.git"); - }, - "test get head revision": function(next) { git.getHeadRevision(__dirname, function(err, rev) { assert.equal(err, null); diff --git a/node_modules/c9/git_url_parse.js b/node_modules/c9/scm_url_parse.js similarity index 50% rename from node_modules/c9/git_url_parse.js rename to node_modules/c9/scm_url_parse.js index e701228a..f7789bdd 100644 --- a/node_modules/c9/git_url_parse.js +++ b/node_modules/c9/scm_url_parse.js @@ -3,11 +3,19 @@ define(function(require, exports, module) { var Url = require("url"); + var providers = { + "bitbucket.org": "bitbucket", + "github.com": "github" + }; + var defaultProvider = "unknown"; + module.exports = function(url) { var m = url.match(/^(git)@([\w\.\d\-\_]+)(?:\/|:)([\w\.\d\-\_\/]+)/); if (m) { return { protocol: "ssh:", + scm: "git", + provider: providers[m[2]] || defaultProvider, auth: m[1], hostname: m[2], pathname: m[3] @@ -15,22 +23,39 @@ define(function(require, exports, module) { } var parsed = Url.parse(url); + if ( parsed && parsed.protocol && - parsed.protocol.match(/^(git|http|https):$/) && + parsed.protocol.match(/^(git|http|https|ssh):$/) && parsed.hostname && parsed.slashes && parsed.pathname - ) + ) { + var scm; + var provider = providers[parsed.hostname] || defaultProvider; + switch (provider) { + case "github": + scm = "git"; + break; + case "bitbucket": + scm = parsed.pathname.match(/\.git$/) ? "git": "hg"; + break; + default: + scm = parsed.pathname.match(/\.git$/) ? "git": "hg"; + } return { protocol: parsed.protocol, + scm: scm, + provider: provider, auth: parsed.auth || "", hostname: parsed.hostname, pathname: parsed.pathname.replace(/^\/+/, ""), full: url }; - else + } + else { return null; + } }; }); \ No newline at end of file diff --git a/node_modules/c9/scm_url_parse_test.js b/node_modules/c9/scm_url_parse_test.js new file mode 100644 index 00000000..44151967 --- /dev/null +++ b/node_modules/c9/scm_url_parse_test.js @@ -0,0 +1,79 @@ +#!/usr/bin/env node +/*global describe it before after beforeEach afterEach */ +"use strict"; + +"use server"; +"use mocha"; + +require("c9/inline-mocha")(module); +require("amd-loader"); + +var assert = require("assert"); +var parse = require("./scm_url_parse"); + +describe(__filename, function(){ + + describe("#parse", function() { + it("should parse ssh url", function(done) { + var url = parse("git@github.com:fjakobs/lispjs.git"); + assert.equal(url.scm, "git"); + assert.equal(url.protocol, "ssh:"); + assert.equal(url.auth, "git"); + assert.equal(url.hostname, "github.com"); + assert.equal(url.pathname, "fjakobs/lispjs.git"); + done(); + + }), + + it("should parse git url", function(done) { + var url = parse("git://github.com/fjakobs/lispjs.git"); + assert.equal(url.scm, "git"); + assert.equal(url.protocol, "git:"); + assert.equal(url.hostname, "github.com"); + assert.equal(url.pathname, "fjakobs/lispjs.git"); + done(); + + }), + + it("should parse https url", function(done) { + var url = parse("https://fjakobs@github.com/fjakobs/lispjs.git"); + assert.equal(url.protocol, "https:"); + assert.equal(url.scm, "git"); + assert.equal(url.auth, "fjakobs"); + assert.equal(url.hostname, "github.com"); + assert.equal(url.pathname, "fjakobs/lispjs.git"); + done(); + + }), + + it("should parse Bitbucket url", function(done) { + var url = parse("git@bitbucket.org/Richard/expressling.git"); + assert.equal(url.protocol, "ssh:"); + assert.equal(url.scm, "git"); + assert.equal(url.auth, "git"); + assert.equal(url.hostname, "bitbucket.org"); + assert.equal(url.pathname, "Richard/expressling.git"); + done(); + }); + + it("should parse Bitbucket hg ssh url", function(done) { + var url = parse("ssh://hg@bitbucket.org/fjakobs/juhu"); + assert.equal(url.protocol, "ssh:"); + assert.equal(url.scm, "hg"); + assert.equal(url.hostname, "bitbucket.org"); + assert.equal(url.pathname, "fjakobs/juhu"); + done(); + }); + + it("should parse github URL without .git", function(done) { + var url = parse("https://github.com/arunoda/meteor-streams"); + assert.equal(url.protocol, "https:"); + assert.equal(url.scm, "git"); + assert.equal(url.provider, "github"); + assert.equal(url.hostname, "github.com"); + assert.equal(url.pathname, "arunoda/meteor-streams"); + done(); + }); + }); + +}); \ No newline at end of file diff --git a/node_modules/c9/ssh.js b/node_modules/c9/ssh.js index 3dddbeb9..7d8f7917 100644 --- a/node_modules/c9/ssh.js +++ b/node_modules/c9/ssh.js @@ -5,9 +5,12 @@ * @license http://github.com/ajaxorg/node-sftp/blob/master/LICENSE MIT License */ +"use strict"; + var child_process = require("child_process"); var fs = require("fs"); -var uuid = require("node-uuid"); +var tmp = require("tmp"); +var debug = require("debug")("ssh"); exports.buildArgs = function(prvkeyFile, host) { var args = [ @@ -35,37 +38,42 @@ exports.buildArgs = function(prvkeyFile, host) { exports.spawnWithKeyFile = function(prvkeyFile, host, command, args) { var sshArgs = exports.buildArgs(prvkeyFile, host); - var args = sshArgs.concat(command ? [command] : []).concat(args || []); - console.log("executing: ssh " + args.join(" ")); + args = sshArgs.concat(command ? [command] : []).concat(args || []); + debug("executing: ssh " + args.join(" ")); return child_process.spawn("ssh", args); }; exports.writeKeyFile = function(prvkey, callback) { - var filename = Util.DEFAULT_TMPDIR + "/" + uuid(); - fs.writeFile(filename, prvkey, function(err) { - if (err) - return callback(err); - - fs.chmod(filename, "0600", function(err) { - callback(err, filename); + tmp.tmpName(function(err, filename) { + if (err) return callback(err); + + fs.writeFile(filename, prvkey, function(err) { + if (err) return callback(err); + + fs.chmod(filename, "0600", function(err) { + callback(err, filename); + }); }); }); }; exports.writeKeyFiles = function(prvkey, pubkey, callback) { - var filename = Util.DEFAULT_TMPDIR + "/" + uuid(); - fs.writeFile(filename, prvkey, function(err) { - if (err) - return callback(err); - - fs.chmod(filename, "0600", function(err) { - fs.writeFile(filename + ".pub", pubkey, function(err) { - if (err) - return callback(err); - - fs.chmod(filename + ".pub", "0600", function(err) { - callback(err, filename); + tmp.tmpName(function(err, filename) { + if (err) return callback(err); + + fs.writeFile(filename, prvkey, function(err) { + if (err) return callback(err); + + fs.chmod(filename, "0600", function(err) { + if (err) return callback(err); + + fs.writeFile(filename + ".pub", pubkey, function(err) { + if (err) return callback(err); + + fs.chmod(filename + ".pub", "0600", function(err) { + callback(err, filename); + }); }); }); }); @@ -74,6 +82,8 @@ exports.writeKeyFiles = function(prvkey, pubkey, callback) { exports.spawn = function(prvkey, host, command, args, callback) { exports.writeKeyFile(prvkey, function(err, filename) { + if (err) return callback(err); + var child = exports.spawnWithKeyFile(filename, host, command, args); child.on("exit", function(code) { @@ -106,33 +116,31 @@ exports.exec = function(prvkey, host, command, args, callback) { }; exports.generateKeyPair = function(email, callback) { - var tmp = process.env.TMP || process.env.TMPDIR || process.env.TMP_DIR || "/tmp"; - var filename = tmp + "/" + uuid(); - var phrase = ""; - - var command = "ssh-keygen -t rsa " + - "-f \"" + filename + "\" " + - "-P \"" + phrase + "\" " + - "-C \"" + email + "\" "; - - child_process.exec(command, function (err, stdout, stderr) { - if (err) - return callback(err); - - fs.readFile(filename + ".pub", function (err, pubkey) { - if (err) - return callback(err); - - fs.readFile(filename, function (err, prvkey) { - if (err) - return callback(err); - - fs.unlink(filename + ".pub", function() { - fs.unlink(filename, function() { - callback(null, pubkey.toString(), prvkey.toString()); + tmp.tmpName(function(err, filename) { + if (err) return callback(err); + + var phrase = ""; + + var command = "ssh-keygen -t rsa " + + "-f \"" + filename + "\" " + + "-P \"" + phrase + "\" " + + "-C \"" + email + "\" "; + + child_process.exec(command, function (err, stdout, stderr) { + if (err) return callback(err); + + fs.readFile(filename + ".pub", function (err, pubkey) { + if (err) return callback(err); + + fs.readFile(filename, function (err, prvkey) { + if (err) return callback(err); + + fs.unlink(filename + ".pub", function() { + fs.unlink(filename, function() { + callback(null, pubkey.toString(), prvkey.toString()); + }); }); }); - }); }); }); @@ -140,9 +148,9 @@ exports.generateKeyPair = function(email, callback) { exports.validateSSHKey = function(prvkey, host, callback) { exports.exec(prvkey, host, "", [], function(err, stdout, stderr) { - //console.log("out >> " + stdout) - //console.log("err >> " + stderr) - //console.log(err) + // console.log("out >> " + stdout); + // console.log("err >> " + stderr); + // console.log(err); callback(null, !stderr.match(/Permission denied \(.*publickey/)); }); }; diff --git a/node_modules/frontdoor/lib/types.js b/node_modules/frontdoor/lib/types.js index 9256c8aa..1980238a 100644 --- a/node_modules/frontdoor/lib/types.js +++ b/node_modules/frontdoor/lib/types.js @@ -53,6 +53,7 @@ exports.RegExp.prototype.parse = function(value) { return value.toString(); }; exports.RegExp.prototype.check = function(value) { + if (typeof value !== "string") return false; value = value.toString(); var match = value.match(this.re); return match && value === match[0]; diff --git a/node_modules/vfs-local/localfs.js b/node_modules/vfs-local/localfs.js index 6168cdd1..2ecf079e 100644 --- a/node_modules/vfs-local/localfs.js +++ b/node_modules/vfs-local/localfs.js @@ -211,7 +211,7 @@ module.exports = function setup(fsOptions) { var args = Array.prototype.slice.apply(arguments); var d = domain.create(); d.on("error", function(e) { - console.error("VFS Exception in function '" + func + "':\n" + e.stack); + console.error("VFS Exception in function '" + func + "':\n", (e.stack || e)); vfs.emit("error", { message: e.message, func: func, @@ -220,7 +220,7 @@ module.exports = function setup(fsOptions) { }); console.log("Scheduling process exit"); setTimeout(function() { - console.log("Exiting after uncaught exception in '" + func + "':\n" + e.stack) + console.log("Exiting after uncaught exception in '" + func + "':\n", (e.stack || e)) process.exit(1); }, 2000); }); diff --git a/package.json b/package.json index a58adde9..25512621 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "engine.io-client": "~1.5.1", "eslint": "git://github.com/cloud9ide/eslint.git#e2d052aafd81ea0aa6d1d4fd9f88f3613e386160", "http-error": "~0.0.5", - "less": "~1.5.1", + "less": "^2.4.0", "mime": "~1.2.9", "mkdirp": "~0.3.5", "msgpack-js": "~0.1.1", @@ -49,23 +49,23 @@ }, "licenses": [], "c9plugins": { - "c9.ide.language": "#de3c644b88", + "c9.ide.language": "#afda452919", "c9.ide.language.css": "#afda1f867c", "c9.ide.language.generic": "#87a4a44671", "c9.ide.language.html": "#fa4833e117", "c9.ide.language.html.diff": "#a7311cfc9f", - "c9.ide.language.javascript": "#ec9ecf31cf", + "c9.ide.language.javascript": "#8479d0a9c1", "c9.ide.language.javascript.immediate": "#9a2cce9121", "c9.ide.language.javascript.eslint": "#8832423ad1", "c9.ide.language.javascript.tern": "#7aab8b0b6a", "c9.ide.language.javascript.infer": "#ebb2daf81a", - "c9.ide.language.jsonalyzer": "#d82c60fcb9", - "c9.ide.collab": "#7b09419b5c", + "c9.ide.language.jsonalyzer": "#efa4426f1f", + "c9.ide.collab": "#104ec85dd1", "c9.ide.local": "#2bfd7ff051", - "c9.ide.find": "#989c06e6a7", - "c9.ide.find.infiles": "#f98dfef554", + "c9.ide.find": "#ef82bc4f0d", + "c9.ide.find.infiles": "#1b83cf12f1", "c9.ide.find.replace": "#e4daf722b8", - "c9.ide.run.debug": "#379e508be6", + "c9.ide.run.debug": "#638e6b00b3", "c9.automate": "#86bf1ee1ca", "c9.ide.ace.emmet": "#e5f1a92ac3", "c9.ide.ace.gotoline": "#4d1a93172c", @@ -77,13 +77,13 @@ "c9.ide.behaviors": "#6aad7006a0", "c9.ide.closeconfirmation": "#a28bfd8272", "c9.ide.configuration": "#b8470f4107", - "c9.ide.dialog.wizard": "#a588b64050", + "c9.ide.dialog.wizard": "#c6401bdd13", "c9.ide.fontawesome": "#781602c5d8", "c9.ide.format": "#f51451ac57", "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", - "c9.ide.immediate": "#e9ba147cc2", - "c9.ide.installer": "#24e7d6f399", + "c9.ide.immediate": "#6845a93705", + "c9.ide.installer": "#38f5840924", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#9a7464cc47", @@ -94,11 +94,11 @@ "c9.ide.pubsub": "#92ec19ed3a", "c9.ide.readonly": "#f6f07bbe42", "c9.ide.recentfiles": "#7c099abf40", - "c9.ide.remote": "#f531d62cfb", - "c9.ide.run": "#3349df7c52", + "c9.ide.remote": "#cd45e81d2f", + "c9.ide.run": "#d07c872ee1", "c9.ide.run.build": "#915e48b363", "c9.ide.run.debug.xdebug": "#b91d23f48b", - "c9.ide.save": "#a32a8f4346", + "c9.ide.save": "#b876d87d55", "c9.ide.terminal.monitor": "#b0b4d03280", "c9.ide.theme.flat": "#b1d65fa9bb", "c9.ide.threewaymerge": "#229382aa0b", diff --git a/plugins/c9.cli.publish/publish.js b/plugins/c9.cli.publish/publish.js index deb301c6..476bb923 100644 --- a/plugins/c9.cli.publish/publish.js +++ b/plugins/c9.cli.publish/publish.js @@ -36,6 +36,7 @@ define(function(require, exports, module) { var FormData = require("form-data"); var http = require(APIHOST.indexOf("localhost") > -1 ? "http" : "https"); var basename = require("path").basename; + var dirname = require("path").dirname; var verbose = false; var force = false; @@ -433,6 +434,41 @@ define(function(require, exports, module) { // @TODO add a .c9exclude file that excludes files var zipFilePath; function build(){ + var base = dirname(cwd); + var packageName = json.name; + var config = Object.keys(plugins).map(function(p) { + return packageName + "/" + p; + }); + var paths = {}; + paths[packageName] = cwd; + + var build = require("architect-build/build"); + build(config, { + paths: paths, + enableBrowser: true, + includeConfig: false, + noArchitect: true, + compress: true, + obfuscate: true, + oneLine: true, + filter: [], + ignore: [], + withRequire: false, + stripLess: false, + basepath: base, + }, function(e, result) { + var packedFiles = result.sources.map(function(m) { + return m.file + }); + fs.writeFile("__packed__.js", result.code, "utf8", function(err, result) { + if (err) console.log(err); + + zip(packedFiles); + }); + }); + } + + function zip(ignore){ zipFilePath = join(os.tmpDir(), json.name + "@" + json.version); var tarArgs = ["-zcvf", zipFilePath, "."]; var c9ignore = process.env.HOME + "/.c9/.c9ignore"; @@ -704,10 +740,29 @@ define(function(require, exports, module) { var err = code !== 0 ? new Error("Failed to unpack package") : null; + if (err) return callback(err); - // Done - callback(err, { - version: version + proc.spawn(join(process.env.HOME, ".c9/node/bin/npm"), { + args: ["install"], + cwd: packagePath + }, function(err, p){ + if (err) return callback(err); + + if (verbose) { + p.stdout.on("data", function(c){ + process.stdout.write(c.toString("utf8")); + }); + p.stderr.on("data", function(c){ + process.stderr.write(c.toString("utf8")); + }); + } + + p.on("exit", function(code){ + // Done + callback(err, { + version: version + }); + }); }); }); }); diff --git a/plugins/c9.core/c9.js b/plugins/c9.core/c9.js index 502871af..0e310961 100644 --- a/plugins/c9.core/c9.js +++ b/plugins/c9.core/c9.js @@ -24,6 +24,7 @@ define(function(require, module, exports) { var loaded = false; var loggedIn = false; + var isReady = false; var state = 0; var STORAGE = 1 << 1; @@ -109,6 +110,7 @@ define(function(require, module, exports) { } function ready(){ + isReady = true; emit.sticky("ready"); } @@ -284,6 +286,10 @@ define(function(require, module, exports) { */ get startLoadTime(){ return startLoadTime; }, set startLoadTime(v){ startLoadTime = v; }, + /** + * + */ + get isReady(){ return isReady; }, _events: [ /** diff --git a/plugins/c9.core/c9_test.js b/plugins/c9.core/c9_test.js index ecb33c61..bdd0049b 100644 --- a/plugins/c9.core/c9_test.js +++ b/plugins/c9.core/c9_test.js @@ -37,6 +37,8 @@ require(["lib/architect/architect", "lib/chai/chai"], function (architect, chai) var vfs = imports.vfs; describe('c9', function() { + this.timeout(30000); + it('should send proper events during connecting', function(done) { // var count = 0; diff --git a/plugins/c9.core/util.js b/plugins/c9.core/util.js index 538a5aa6..fd78dff6 100644 --- a/plugins/c9.core/util.js +++ b/plugins/c9.core/util.js @@ -11,7 +11,7 @@ define(function(require, exports, module) { function main(options, imports, register) { var c9 = imports.c9; var Plugin = imports.Plugin; - var _ = require("lodash"); + var normalize = require("path").normalize; var plugin = new Plugin("Ajax.org", main.consumes); @@ -270,7 +270,7 @@ define(function(require, exports, module) { var reHome = new RegExp("^" + plugin.escapeRegExp(c9.home || "/home/ubuntu")); plugin.normalizePath = function(path){ if (!path || path.charAt(0) == "~") return path; - return (path.replace(/^[\/]+/, "/")).replace(reHome, "~"); + return normalize(path.replace(reHome, "~")); }; /** diff --git a/plugins/c9.fs/fs.js b/plugins/c9.fs/fs.js index cd8c8270..e725d694 100644 --- a/plugins/c9.fs/fs.js +++ b/plugins/c9.fs/fs.js @@ -64,7 +64,7 @@ define(function(require, exports, module) { throw new Error("Missing callback for " + name); if (!/^[!~/]/.test(path)) { - var e = new Error("Invalid path passed to fs"); + var e = new Error("Invalid path passed to fs " + name); e.data = { name: name, path: path }; setTimeout(function() { throw e }); return args[args.length - 1](e); diff --git a/plugins/c9.fs/mock/test.browser.js b/plugins/c9.fs/mock/test.browser.js new file mode 100644 index 00000000..6a2a0e65 --- /dev/null +++ b/plugins/c9.fs/mock/test.browser.js @@ -0,0 +1,7 @@ +function onClick() { + say("hi"); +} + +function say(text) { + alert(text); +} \ No newline at end of file diff --git a/plugins/c9.fs/mock/test.js b/plugins/c9.fs/mock/test.js index 94948eaf..b07b88ff 100644 --- a/plugins/c9.fs/mock/test.js +++ b/plugins/c9.fs/mock/test.js @@ -28,4 +28,4 @@ var i = 0; console.log(greeter(user), Date.now(), i++); setInterval(function(){ console.log(greeter(user), Date.now(), i++); -}, 1000); \ No newline at end of file +}, 10000); diff --git a/plugins/c9.fs/net_test.js b/plugins/c9.fs/net_test.js index 359281b2..c4ef2a30 100644 --- a/plugins/c9.fs/net_test.js +++ b/plugins/c9.fs/net_test.js @@ -40,7 +40,7 @@ require(["lib/architect/architect", "lib/chai/chai"], function (architect, chai) describe('net', function() { describe('connect()', function() { - this.timeout(10000); + this.timeout(30000); it("should connect to a port", function(done) { var code = diff --git a/plugins/c9.fs/proc.js b/plugins/c9.fs/proc.js index 2ce7ece1..9aac1923 100644 --- a/plugins/c9.fs/proc.js +++ b/plugins/c9.fs/proc.js @@ -7,11 +7,14 @@ define(function(require, exports, module) { var vfs = imports.vfs; var Plugin = imports.Plugin; + var ProcessToPty = require("./proc2pty"); + /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); + var installMode; var tmuxName = options.tmuxName; /***** Register and define API *****/ @@ -171,6 +174,15 @@ define(function(require, exports, module) { * @singleton **/ plugin.freezePublicAPI({ + /** + * @ignore + */ + get installMode(){ return installMode; }, + set installMode(_vfs) { + vfs = _vfs || imports.vfs; + installMode = _vfs ? true : false; + }, + _events: [ /** * @event beforeSpawn Fires right before a file is executed @@ -334,6 +346,14 @@ define(function(require, exports, module) { * @fires afterPty */ pty: function(path, options, callback) { + if (installMode) { + plugin.spawn(path, options, function(err, process){ + if (err) return callback(err); + callback(null, new ProcessToPty(process)); + }); + return; + } + emit("beforePty", {path: path, options: options}); if (!options.encoding) diff --git a/plugins/c9.fs/proc2pty.js b/plugins/c9.fs/proc2pty.js new file mode 100644 index 00000000..249cda20 --- /dev/null +++ b/plugins/c9.fs/proc2pty.js @@ -0,0 +1,27 @@ +define(function(require, exports, module) { + var EventEmitter = require("events").EventEmitter; + + module.exports = function(process){ + var pty = new EventEmitter(); + pty.write = function(data){ + process.stdin.write(data); + }; + pty.resize = function(){}; + pty.destroy = + pty.end = function(){ + process.kill(); + }; + + process.stdout.on("data", function(chunk){ + pty.emit("data", chunk); + }); + process.stderr.on("data", function(chunk){ + pty.emit("data", chunk); + }); + process.on("exit", function(code){ + pty.emit("exit", code); + }); + + return pty; + } +}); \ No newline at end of file diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index ceff85f9..43d2ea37 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -534,7 +534,7 @@ define(function(require, exports, module) { editor = apf.activeElement; if (!isAce(editor, true)) return false; - if (!editor.ace.commands.byName[command.name]) + if (!editor.ace.commands.byName[command.name] && !command.shared) return false; return isAvailable ? isAvailable(editor.ace) : true; @@ -582,7 +582,29 @@ define(function(require, exports, module) { commands.addCommand(commands.commands.togglerecording, handle); commands.addCommand(commands.commands.replaymacro, handle); + // when event for cmd-z in textarea is not canceled + // chrome tries to find another textarea with pending undo and focus it + // we do not want this to happen when ace instance is focused commands.addCommand(fnWrap({ + name: "cancelBrowserUndoInAce", + bindKey: { + mac: "Cmd-Z|Cmd-Shift-Z|Cmd-Y", + win: "Ctrl-Z|Ctrl-Shift-Z|Ctrl-Y", + position: -10000 + }, + group: "ignore", + exec: function(e) {}, + readOnly: true, + shared: true + }), handle); + function sharedCommand(command) { + command.isAvailable = function(editor) { + return editor && editor.type == "ace"; + }; + command.group = "Code Editor"; + return command; + } + commands.addCommand(sharedCommand({ name: "syntax", exec: function(_, syntax) { if (typeof syntax == "object") @@ -597,7 +619,7 @@ define(function(require, exports, module) { commands: modes.caption }), handle); - commands.addCommand(fnWrap({ + commands.addCommand(sharedCommand({ name: "largerfont", bindKey: { mac : "Command-+|Command-=", win : "Ctrl-+|Ctrl-=" }, exec: function(e) { @@ -606,7 +628,7 @@ define(function(require, exports, module) { } }), handle); - commands.addCommand(fnWrap({ + commands.addCommand(sharedCommand({ name: "smallerfont", bindKey: { mac : "Command--", win : "Ctrl--" }, exec: function(e) { @@ -615,16 +637,13 @@ define(function(require, exports, module) { } }), handle); - commands.addCommand({ + commands.addCommand(sharedCommand({ name: "toggleWordWrap", bindKey: {win: "Ctrl-Q", mac: "Ctrl-W"}, exec: function(editor) { editor.setOption("wrap", editor.getOption("wrap") == "off"); - }, - isAvailable: function(editor) { - return editor && editor.type == "ace"; } - }, handle); + }), handle); } /***** Preferences *****/ diff --git a/plugins/c9.ide.dialog/dialog.js b/plugins/c9.ide.dialog/dialog.js index 452bf1e8..c2b5da11 100644 --- a/plugins/c9.ide.dialog/dialog.js +++ b/plugins/c9.ide.dialog/dialog.js @@ -67,26 +67,21 @@ define(function(require, module, exports) { var left = options.left; var top = options.top; var width = options.width || 512; + var height = options.height; var title = options.title; var heading = options.heading; var body = options.body; - var custom = options.custom; + var className = options.class; var modal = options.modal; var zindex = options.zindex; var allowClose = options.allowClose; var elements = options.elements || []; - var resizable = options.resizable || false; + var resizable = false; var widths = options.widths || {}; var count = 0; var dialog, buttons, titles; - var loaded; - function load(){ - if (loaded) return; - loaded = true; - } - var drawn = false; function draw(htmlNode) { if (drawn) return; @@ -101,9 +96,13 @@ define(function(require, module, exports) { modal: modal, buttons: allowClose ? "close" : "", width: width, + height: height, zindex: zindex || "", skin: "bk-window2", - class: "relative" + (options.dark ? " dark" : ""), + class: "dialog " + + (height ? "" : "relative") + + (options.dark ? " dark" : "") + + (className ? " " + className : ""), childNodes: [ new ui.vbox({ id: "titles", @@ -162,8 +161,8 @@ define(function(require, module, exports) { html: titles.$int }); - if (resizable) - plugin.resizable = resizable; + if (options.resizable) + plugin.resizable = options.resizable; } /***** Method *****/ @@ -359,6 +358,11 @@ define(function(require, module, exports) { return node; } + plugin.on("unload", function(){ + drawn = false; + resizable = false; + }); + /***** Register and define API *****/ plugin.freezePublicAPI.baseclass(); @@ -437,13 +441,20 @@ define(function(require, module, exports) { resizable = v; if (!dialog) return; - dialog.setAttribute("height", v ? dialog.getHeight() : ""); - dialog.setAttribute("width", v ? dialog.getWidth() : ""); - dialog.setAttribute("class", v ? "" : "relative"); - titles.setAttribute("anchors", v ? "0 0 46 0" : ""); - buttons.setAttribute("bottom", v ? "0" : ""); - buttons.setAttribute("left", v ? "0" : ""); - buttons.setAttribute("right", v ? "0" : ""); + if (!height) + dialog.setAttribute("height", v ? dialog.getHeight() : ""); + if (!width) + dialog.setAttribute("width", v ? dialog.getWidth() : ""); + + dialog.setAttribute("class", "dialog " + + (v ? "" : "relative") + + (options.dark ? " dark" : "") + + (className ? " " + className : "")); + + // titles.setAttribute("anchors", v ? "0 0 46 0" : ""); + // buttons.setAttribute("bottom", v ? "0" : ""); + // buttons.setAttribute("left", v ? "0" : ""); + // buttons.setAttribute("right", v ? "0" : ""); dialog.setAttribute("resizable", v); }, /** @@ -465,6 +476,11 @@ define(function(require, module, exports) { * @readonly */ get modal(){ return modal; }, + /** + * @property {Boolean} visible + * @readonly + */ + get visible(){ return dialog && dialog.visible; }, /** * @property {Boolean} allowClose */ diff --git a/plugins/c9.ide.editors/document.js b/plugins/c9.ide.editors/document.js index a7ec71ab..c305ad8f 100644 --- a/plugins/c9.ide.editors/document.js +++ b/plugins/c9.ide.editors/document.js @@ -1,5 +1,5 @@ define(function(require, module, exports) { - main.consumes = ["Plugin", "UndoManager", "util"]; + main.consumes = ["Plugin", "UndoManager", "util", "error_handler"]; main.provides = ["Document"]; return main; @@ -7,6 +7,7 @@ define(function(require, module, exports) { var Plugin = imports.Plugin; var util = imports.util; var UndoManager = imports.UndoManager; + var reportError = imports.error_handler.reportError; function Document(options) { var plugin = new Plugin("Ajax.org", main.consumes); @@ -341,8 +342,11 @@ define(function(require, module, exports) { */ get ready(){ return ready; }, set ready(v) { - if (ready) throw new Error("Permission Denied"); - ready = true; + // try to find out why is this called twice + var e = new Error("Setting ready on ready document"); + if (ready) + reportError(e, {ready: ready}); + ready = e.stack || true; emit.sticky("ready", { doc: plugin }); }, /** diff --git a/plugins/c9.ide.editors/tabmanager.js b/plugins/c9.ide.editors/tabmanager.js index e8372b07..b0004111 100644 --- a/plugins/c9.ide.editors/tabmanager.js +++ b/plugins/c9.ide.editors/tabmanager.js @@ -1004,6 +1004,11 @@ define(function(require, module, exports) { && t.document.title === options.title)); })[0]); + // prevent opening of same tab twice in non cloned mode + // TODO move cloning into ace? + if (!tab) + tab = findTab(path); + // Clone Tab if (((options.document || 0).meta || 0).cloned) { if (!tab) { diff --git a/plugins/c9.ide.editors/undomanager.js b/plugins/c9.ide.editors/undomanager.js index c88355c2..6ab1adc9 100644 --- a/plugins/c9.ide.editors/undomanager.js +++ b/plugins/c9.ide.editors/undomanager.js @@ -99,13 +99,13 @@ define(function(require, module, exports) { } function bookmark(index) { - mark = index !== undefined ? index : this.position; + mark = index !== undefined ? index : position; emit("change"); } function isAtBookmark(){ - return mark !== null && mark == this.position + return mark !== null && mark == position || mark === null && position == -1; } diff --git a/plugins/c9.ide.errorhandler/raygun_error_handler.js b/plugins/c9.ide.errorhandler/raygun_error_handler.js index 43e46239..12be8f71 100644 --- a/plugins/c9.ide.errorhandler/raygun_error_handler.js +++ b/plugins/c9.ide.errorhandler/raygun_error_handler.js @@ -75,7 +75,9 @@ define(function(require, exports, module) { /***** Register and define API *****/ plugin.freezePublicAPI({ - reportError: reportError + /** @deprecated Use log() instead. */ + reportError: reportError, + log: reportError }); register(null, { "error_handler" : plugin }); diff --git a/plugins/c9.ide.errorhandler/simple_error_handler.js b/plugins/c9.ide.errorhandler/simple_error_handler.js index d9b71362..36f530d1 100644 --- a/plugins/c9.ide.errorhandler/simple_error_handler.js +++ b/plugins/c9.ide.errorhandler/simple_error_handler.js @@ -108,7 +108,9 @@ define(function(require, exports, module) { /***** Register and define API *****/ plugin.freezePublicAPI({ - reportError: reportError + /** @deprecated Use log() instead. */ + reportError: reportError, + log: reportError }); register(null, { "error_handler" : plugin }); diff --git a/plugins/c9.ide.keys/editor.js b/plugins/c9.ide.keys/editor.js index e6c90708..970e4e08 100644 --- a/plugins/c9.ide.keys/editor.js +++ b/plugins/c9.ide.keys/editor.js @@ -2,7 +2,7 @@ define(function(require, exports, module) { main.consumes = [ "PreferencePanel", "commands", "settings", "ui", "util", "Form", "c9", "dialog.alert", "tabManager", "save", "dialog.confirm", "layout", - "preferences", "menus" + "menus" ]; main.provides = ["preferences.keybindings"]; return main; @@ -12,7 +12,6 @@ define(function(require, exports, module) { var commands = imports.commands; var settings = imports.settings; var layout = imports.layout; - var preferences = imports.preferences; var ui = imports.ui; var c9 = imports.c9; var util = imports.util; @@ -32,7 +31,7 @@ define(function(require, exports, module) { var plugin = new PreferencePanel("Ajax.org", main.consumes, { caption: "Keybindings", - className: "keybindings", + className: "keybindings flatform", form: true, noscroll: true, index: 200 @@ -211,7 +210,7 @@ define(function(require, exports, module) { node: container = new ui.bar({ anchors: "269 0 0 0", "class" : "keybindings", - style: "padding:44px 10px 10px 10px;position:relative" + style: "padding:44px 10px 10px 10px" }) } ], commands); @@ -509,16 +508,19 @@ define(function(require, exports, module) { }); plugin.on("resize", function(e) { datagrid && datagrid.resize(); - }); - plugin.on("enable", function() { - - }); - plugin.on("disable", function() { - }); plugin.on("unload", function() { loaded = false; drawn = false; + + model = null; + datagrid = null; + changed = null; + container = null; + filterbox = null; + appliedCustomSets = null; + intro = null; + reloading = null; }); /***** Register and define API *****/ diff --git a/plugins/c9.ide.layout.classic/less/bk-window.less b/plugins/c9.ide.layout.classic/less/bk-window.less index 5f48e02d..4cad75a1 100644 --- a/plugins/c9.ide.layout.classic/less/bk-window.less +++ b/plugins/c9.ide.layout.classic/less/bk-window.less @@ -129,4 +129,21 @@ right: 20px; top: 25px; } - \ No newline at end of file + +.bk-window.dialog .bk-container{ + display: flex; + flex-direction: column; +} +.bk-window.dialog .bk-container > div:first-child{ + flex: 1; + position: relative; +} + +.bk-window.wizard .bk-container > div:first-child > div{ + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + padding: 20px; +} \ No newline at end of file diff --git a/plugins/c9.ide.layout.classic/themes/default-dark-gray.less b/plugins/c9.ide.layout.classic/themes/default-dark-gray.less index 1a58adf5..d35f07fc 100644 --- a/plugins/c9.ide.layout.classic/themes/default-dark-gray.less +++ b/plugins/c9.ide.layout.classic/themes/default-dark-gray.less @@ -1599,11 +1599,11 @@ // Installer @installer-font-smoothing: true; -@installer-background: #272727; -@installer-border-top: 1px solid rgba(255, 255, 255, 0.1); -@installer-box-shadow: 0 -1px black; -@installer-color: rgb(182, 182, 182); -@installer-text-shadow: 1px 1px 0 rgb(10, 10, 10); +@installer-background: transparent; +@installer-border-top: 0; +@installer-box-shadow: none; +@installer-color: #333; +@installer-text-shadow: none; @installer-font-family: Arial; @installer-log-color: #fff; @@ -1612,7 +1612,7 @@ @installer-log-border-color: rgba(255,255,255,0.06) rgba(0,0,0,0.3) rgba(0,0,0,0.3) rgba(255,255,255,0.06); @installer-log-border-radius: 0; @installer-log-box-shadow: 1px 0 0 rgba(255,255,255,0.06), 0 1px 0 rgba(255,255,255,0.06), -1px 0 0 rgba(0,0,0,0.3), 0 -1px 0 rgba(0,0,0,0.3); -@installer-log-checkbox-color: #f1f1f1; +@installer-log-checkbox-color: #333; @installer-log-progress-color: white; // Notification diff --git a/plugins/c9.ide.layout.classic/themes/default-dark.less b/plugins/c9.ide.layout.classic/themes/default-dark.less index ec2edd6a..3ef3edf0 100644 --- a/plugins/c9.ide.layout.classic/themes/default-dark.less +++ b/plugins/c9.ide.layout.classic/themes/default-dark.less @@ -1599,11 +1599,11 @@ // Installer @installer-font-smoothing: true; -@installer-background: #272727; -@installer-border-top: 1px solid rgba(255, 255, 255, 0.1); -@installer-box-shadow: 0 -1px black; -@installer-color: rgb(182, 182, 182); -@installer-text-shadow: 1px 1px 0 rgb(10, 10, 10); +@installer-background: transparent; +@installer-border-top: 0; +@installer-box-shadow: none; +@installer-color: #333; +@installer-text-shadow: none; @installer-font-family: Arial; @installer-log-color: #fff; @@ -1612,7 +1612,7 @@ @installer-log-border-color: rgba(255,255,255,0.06) rgba(0,0,0,0.3) rgba(0,0,0,0.3) rgba(255,255,255,0.06); @installer-log-border-radius: 0; @installer-log-box-shadow: 1px 0 0 rgba(255,255,255,0.06), 0 1px 0 rgba(255,255,255,0.06), -1px 0 0 rgba(0,0,0,0.3), 0 -1px 0 rgba(0,0,0,0.3); -@installer-log-checkbox-color: #f1f1f1; +@installer-log-checkbox-color: #333; @installer-log-progress-color: white; // Notification diff --git a/plugins/c9.ide.layout.classic/themes/flat-light.less b/plugins/c9.ide.layout.classic/themes/flat-light.less index 4608a79a..81b10dda 100644 --- a/plugins/c9.ide.layout.classic/themes/flat-light.less +++ b/plugins/c9.ide.layout.classic/themes/flat-light.less @@ -390,6 +390,27 @@ padding : 5px 3px 1px 5px; } +.ace_tree .tree-row .checkbox{ + outline: 1px solid rgba(0, 0, 0, 0.16); + background: white; + width: 13px; + height: 13px; + margin: -2px 5px 0 0px; + display: inline-block; + vertical-align: middle; + pointer-events: auto; +} +.ace_tree .tree-row .checkbox.checked{ + background-position: -2px -2px; +} +.ace_tree .tree-row .checkbox.half-checked{ + background-color: #DDDDDD; + background-position: -2px -2px; +} +.installer .execute .progress{ + bottom: -44px; +} + @media print, (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 1.25dppx) { .listBP .checked .checkbox { background-position: 0px -61px !important; diff --git a/plugins/c9.ide.metrics/metrics.js b/plugins/c9.ide.metrics/metrics.js new file mode 100644 index 00000000..ff19b8f3 --- /dev/null +++ b/plugins/c9.ide.metrics/metrics.js @@ -0,0 +1,121 @@ +/** + * Stub for collecting workspace/service metrics. + * + * For logging metrics to a service like datadog. + * + * Actual metrics are implemented by an external plugin that has + * many dependencies, including dependencies on VFS. + * VFS and other plugins have dependencies on "metrics". + * This stub plugin breaks the dependency cycle. + */ +define(function(require, exports, module) { + "use strict"; + + main.consumes = ["Plugin"]; + main.provides = ["metrics"]; + return main; + + /** + * Collects workspace/service metrics. + */ + function main(options, imports, register) { + var Plugin = imports.Plugin; + var plugin = new Plugin("Ajax.org", main.consumes); + + var api; + var delayedCalls = []; + + var apiFunction = options.hosted + ? function(apiName, varArgs) { + var args = [].slice.apply(arguments); + + if (!api) + return arguments[1] && delayedCalls.push(args); + + var name = args.shift(); + return api[name].apply(api, args); + } + : function() {}; + + function publishApi(newApi) { + api = newApi; + delayedCalls.forEach(function(args) { + var name = args.shift(); + api[name].apply(api, args); + }); + delayedCalls = []; + } + + plugin.freezePublicAPI({ + /** + * @internal @ignore + */ + $publishApi: publishApi, + + $setRequestApi: apiFunction.bind(null, "$setRequestApi"), + + /** + * Increment a metric value for this user/workspace, + * e.g. increment the number of times the user was disconnected by 1. + * + * These metrics get logged to a service like datadog, + * namespaced in c9.ide.metrics. + * + * Example: + * + * metrics.increment("disconnected", 1, true); + * + * @param {String} name + * @param {Number} [value] + * @param {Boolean} [now] + * Whether to send it within 50ms (or + * when we have a connection) or some time between now + * and up to 5 min or so. + */ + increment: apiFunction.bind(null, "increment"), + + /** + * Set a metric value for this user/workspace, + * e.g. the time it takes to load this workspace. + * + * These metrics get logged to a service like datadog, + * namespaced in c9.ide.metrics. + * + * Example: + * + * metrics.log("load_workspace", Date.now() - start, [500, 1000, 5000]); + * + * @param {String} name + * @param {Number} value + * @param {Number[]} [levels] + * If specified, splits up the metric into different levels, e.g. + * for [500, 1000, 5000], a 'value' of 600 would + * log a load_workspace_500 metric. A 'value' of 6000 + * would log a load_workspace_over_5000 metric. + * @param {Boolean} [now] + * Whether to send within 50ms (or + * when we have a connection) or some time between now + * and up to 5 min or so. + */ + log: apiFunction.bind(null, "log"), + + /** + * Get the latest metrics collected, or null if not available yet. + * + * @internal + * @ignore + * @return {Number} + */ + getLatest: apiFunction.bind(null, "getLatest"), + + /** + * Get the last vfs ping time. + * + * @return {Object} + */ + getLastPing: apiFunction.bind(null, "getLastPing"), + }); + + register(null, { metrics: plugin }); + } +}); \ No newline at end of file diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index ec27acfd..9058bfbc 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -1,12 +1,13 @@ +/*global requirejs*/ define(function(require, exports, module) { main.consumes = [ "PreferencePanel", "settings", "ui", "util", "Form", "ext", "c9", "dialog.alert", "dialog.confirm", "layout", "proc", "menus", "commands", - "dialog.error", "tree.favorites", "fs", "tree" + "dialog.error", "tree.favorites", "fs", "tree", "plugin.debug" ]; main.provides = ["plugin.manager"]; return main; - + /* - Show Packages to be updated - Open Plugin Store @@ -23,16 +24,16 @@ define(function(require, exports, module) { - * - 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) */ @@ -54,17 +55,18 @@ define(function(require, exports, module) { var confirm = imports["dialog.confirm"].show; var showError = imports["dialog.error"].show; var favs = imports["tree.favorites"]; - + var pluginDebug = imports["plugin.debug"]; + 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 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, @@ -85,11 +87,11 @@ define(function(require, exports, module) { "plugin.simple": "Empty Plugin", "plugin.default": "Full Plugin" }; - + // @TODO add sorting - + /***** Initialization *****/ - + var plugin = new PreferencePanel("Ajax.org", main.consumes, { caption: "Plugin Manager", className: "plugins", @@ -98,31 +100,31 @@ define(function(require, exports, module) { index: 200 }); // var emit = plugin.getEmitter(); - + var HASSDK = c9.location.indexOf("sdk=0") === -1; var ENABLED = c9.location.indexOf("sdk=1") > -1; - + var model, datagrid, filterbox; - var btnUninstall, btnReport, btnReadme, btnCloud9; - + var btnUninstall, btnReport, btnReadme, btnCloud9, btnReload; + var loaded = false; function load() { if (loaded) return false; loaded = true; - + if (!HASSDK) return; - + // @TODO enable/disable plugins -> move to ext - + // settings.on("read", function(e) { // updateCommandsFromSettings(); // }, plugin); - + // commands.on("update", function(){ // changed = true; // updateCommandsFromSettings(); // }, plugin); - + if (ENABLED) { menus.addItemByPath("File/New Plugin", null, 210, plugin); Object.keys(TEMPLATES).forEach(function(name){ @@ -134,15 +136,15 @@ define(function(require, exports, module) { }); } } - + var drawn; function draw(e) { if (drawn) return; drawn = true; - + model = new TreeData(); model.emptyMessage = "No plugins found"; - + model.columns = [{ caption: "Name", value: "name", @@ -154,8 +156,8 @@ define(function(require, exports, module) { }, { caption: "Version", // value: "version", - getText: function(p){ - return p.version || + getText: function(p){ + return p.version || (p.isPackage ? p.items.length && p.items[0].version || "" : ""); @@ -165,16 +167,16 @@ define(function(require, exports, module) { caption: "Startup Time", // value: "time", width: "100", - getText: function(p){ + getText: function(p){ if (p.time !== undefined) - return (p.time || 0) + "ms"; - + 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.forEach(function(p){ p.items && p.items.forEach(function(item){ total += item.time || 0 }); }); } @@ -192,24 +194,24 @@ define(function(require, exports, module) { caption: "Developer", // value: "developer", getText: function(p){ - return p.developer || + 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, @@ -218,16 +220,16 @@ define(function(require, exports, module) { // "class" : "intro", // style: "padding:12px;position:relative;" // }) - - // intro.$int.innerHTML = + + // 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, @@ -270,30 +272,39 @@ define(function(require, exports, module) { 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", + 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", @@ -303,25 +314,26 @@ define(function(require, exports, module) { 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) { @@ -332,31 +344,38 @@ define(function(require, exports, module) { 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") + + if (CORE[item.name] || item.parent.parent && item.parent.parent.isType == "core") { btnUninstall.disable(); - else + } 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(); } }); } - + /***** Methods *****/ - + function reloadModel() { if (!model) return; - + var groups = {}; var packages = {}; var root = []; - + ["custom", "pre", "core", "runtime"].forEach(function(name){ - root.push(groups[name] = { - items: [], + root.push(groups[name] = { + items: [], isOpen: name != "runtime", className: "group", isGroup: true, @@ -365,18 +384,18 @@ define(function(require, exports, module) { name: GROUPS[name] }); }); - + 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; @@ -384,15 +403,15 @@ define(function(require, exports, module) { else { package = packages[packageName]; if (!package) - groups[groupName].items.push(package = packages[packageName] = { - items: [], + groups[groupName].items.push(package = packages[packageName] = { + items: [], isPackage: true, className: "package", parent: groups[groupName], - name: packageName + name: packageName }); } - + package.items.push({ name: plugin.name, enabled: lut[plugin.name].loaded ? "true" : "false", @@ -405,42 +424,42 @@ define(function(require, exports, module) { : 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("(" + 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) { @@ -448,13 +467,13 @@ define(function(require, exports, module) { "Could not uninstall plugin", "Could not uninstall plugin"); } - + btnUninstall.setAttribute("caption", "Uninstall"); btnUninstall.enable(); }); }); } - + function enable(name){ try{ ext.enablePlugin(name); } catch(e){ @@ -463,21 +482,21 @@ define(function(require, exports, module) { 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 + "'?", @@ -502,24 +521,24 @@ define(function(require, exports, module) { 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) { + + function disable(name) { if (!plugins[name].loaded) return; - + try{ ext.disablePlugin(name); } - catch(e){ + catch(e){ alert("Could not disable plugin", "Got an error when disabling plugin: " + name, e.message); @@ -527,15 +546,15 @@ define(function(require, exports, module) { } } } - + function createNewPlugin(template){ if (!template) template = "c9.ide.default"; - + var url = staticPrefix + "/" + join("templates", template + ".tar.gz"); if (!url.match(/^http/)) url = location.origin + url; - + function getPath(callback, i){ i = i || 0; var path = join("~", ".c9/plugins/", template + (i ? "." + i : "")); @@ -544,47 +563,47 @@ define(function(require, exports, module) { callback(null, path); }); } - + function handleError(err){ showError("Could not create plugin."); console.error(err); } - + getPath(function(err, path){ if (err) return handleError(err); - + var pluginsDir = join("~", ".c9/plugins/_/"); var pluginsDirAbsolute = pluginsDir.replace(/^~/, c9.home); var tarPath = join(pluginsDir, template + ".tar.gz"); var tarPathAbsolute = tarPath.replace(/^~/, c9.home); - + // Download tar file with template for plugin proc.execFile("bash", { args: ["-c", ["curl", "-L", url, "--create-dirs", "-o", tarPathAbsolute].join(" ")] }, function(err, stderr, stdout){ if (err) return handleError(err); - + // Untar tar file proc.execFile("bash", { args: ["-c", ["tar", "-zxvf", tarPath, "-C", pluginsDirAbsolute].join(" ")] }, function(err, stderr, stdout){ - if (err) + if (err) return handleError(err); - + // Move template to the right folder var dirPath = join(dirname(tarPath), template); fs.rename(dirPath, path, function(err){ - if (err) + if (err) return handleError(err); - + // Remove .tar.gz fs.unlink(tarPath, function(){ - + // Add plugin to favorites favs.addFavorite(dirname(pluginsDir), "plugins"); - + // Select and expand the folder of the plugin tree.expandAndSelect(path); }); @@ -593,9 +612,19 @@ define(function(require, exports, module) { }); }); } - + + function reload(name) { + for (var plugin in architect.lut) { + if (architect.lut[plugin].provides.indexOf(name) < 0) + continue; + + pluginDebug.reloadPackage(plugin); + return; + } + } + /***** Lifecycle *****/ - + plugin.on("load", function() { load(); }); @@ -609,15 +638,15 @@ define(function(require, exports, module) { 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; @@ -626,48 +655,54 @@ define(function(require, exports, module) { btnReport = null; btnReadme = null; btnCloud9 = null; + btnReload = null; }); - + /***** Register and define API *****/ - + /** - * + * **/ plugin.freezePublicAPI({ /** - * + * */ get architect(){ throw new Error(); }, - set architect(v){ - architect = v; + set architect(v){ + architect = v; architect.on("ready-additional", function(){ reloadModel(); }); }, - + /** - * + * */ createNewPlugin: createNewPlugin, - + /** - * + * */ uninstall: uninstall, - + /** - * + * */ enable: enable, - + /** - * + * */ - disable: disable + disable: disable, + + /** + * + */ + reload: reload }); - - register(null, { - "plugin.manager" : plugin + + register(null, { + "plugin.manager" : plugin }); } -}); \ No newline at end of file +}); diff --git a/plugins/c9.ide.plugins/mock/c9.ide.example b/plugins/c9.ide.plugins/mock/c9.ide.example deleted file mode 160000 index 35a5deda..00000000 --- a/plugins/c9.ide.plugins/mock/c9.ide.example +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 35a5dedaeb20cf6f8a9f1021de23d8dd96860f11 diff --git a/plugins/c9.ide.preferences/general.js b/plugins/c9.ide.preferences/general.js index 67a811a0..1c656659 100644 --- a/plugins/c9.ide.preferences/general.js +++ b/plugins/c9.ide.preferences/general.js @@ -23,7 +23,7 @@ define(function(require, exports, module) { var emit = plugin.getEmitter(); emit.setMaxListeners(1000); - var navHtml, intro; + var intro; var loaded = false; function load() { @@ -44,7 +44,7 @@ define(function(require, exports, module) { prefs.on("add", function(e) { if (!("Project" in e.state)) plugin.add(e.state, e.plugin); - }); + }, plugin); } var drawn = false; @@ -52,8 +52,6 @@ define(function(require, exports, module) { if (drawn) return; drawn = true; - navHtml = e.navHtml; - plugin.add({ "General" : { position: 10, @@ -71,7 +69,7 @@ define(function(require, exports, module) { + "Cloud9 will return to it's original configuration", function(){ settings.reset(); - }, function(){}) + }, function(){}); } } }, @@ -109,8 +107,8 @@ define(function(require, exports, module) { + '

These settings are synced across all your workspaces.

'; intro.$int.querySelector("a").onclick = function(){ - emit("edit") - } + emit("edit"); + }; } /***** Methods *****/ @@ -123,16 +121,11 @@ define(function(require, exports, module) { }); plugin.on("draw", function(e) { draw(e); - }); - plugin.on("enable", function() { - - }); - plugin.on("disable", function() { - }); plugin.on("unload", function() { loaded = false; drawn = false; + intro = null; }); /***** Register and define API *****/ diff --git a/plugins/c9.ide.preferences/preferencepanel.js b/plugins/c9.ide.preferences/preferencepanel.js index 35088827..66acbaa4 100644 --- a/plugins/c9.ide.preferences/preferencepanel.js +++ b/plugins/c9.ide.preferences/preferencepanel.js @@ -261,7 +261,7 @@ define(function(require, module, exports) { "textarea" : 400, "checked-spinner" : 50 } - }); + }, plugin); } }); diff --git a/plugins/c9.ide.preferences/preferences.css b/plugins/c9.ide.preferences/preferences.css index c5450847..74ccdc48 100644 --- a/plugins/c9.ide.preferences/preferences.css +++ b/plugins/c9.ide.preferences/preferences.css @@ -148,14 +148,15 @@ .bar-preferences .keybindings{ min-height: 250px; } -.bar-preferences .keybindings .container > .hsplitbox, .bar-preferences .keybindings .container > .basic{ +.bar-preferences .flatform .container > .hsplitbox, +.bar-preferences .flatform .container > .basic{ border-top : 1px solid @preferences-border-top-color-light; box-sizing: border-box; } .bar-preferences .keybindings .intro when(@preferences-flat){ margin-bottom: 8px !important; } -.bar-preferences .keybindings .container > .hsplitbox:last-child when(@preferences-flat){ +.bar-preferences .flatform .container > .hsplitbox:last-child when(@preferences-flat){ height: 49px !important; } .bar-preferences .container>.hsplitbox:last-child{ diff --git a/plugins/c9.ide.preferences/project.js b/plugins/c9.ide.preferences/project.js index 1b9aafee..bc28a828 100644 --- a/plugins/c9.ide.preferences/project.js +++ b/plugins/c9.ide.preferences/project.js @@ -21,7 +21,7 @@ define(function(require, exports, module) { var emit = plugin.getEmitter(); emit.setMaxListeners(1000); - var navHtml, intro; + var intro; var loaded = false; function load() { @@ -42,12 +42,12 @@ define(function(require, exports, module) { prefs.on("add", function(e) { if ("Project" in e.state) plugin.add(e.state, e.plugin); - }); + }, plugin); prefs.on("draw", function(e) { if (!prefs.activePanel) prefs.activate(plugin); - }); + }, plugin); } var drawn = false; @@ -65,8 +65,6 @@ define(function(require, exports, module) { intro.$int.querySelector("a").onclick = function(){ emit("edit"); } - - navHtml = e.navHtml; } /***** Methods *****/ @@ -78,16 +76,11 @@ define(function(require, exports, module) { }); plugin.on("draw", function(e) { draw(e); - }); - plugin.on("enable", function() { - - }); - plugin.on("disable", function() { - }); plugin.on("unload", function() { loaded = false; drawn = false; + intro = null; }); /***** Register and define API *****/ diff --git a/plugins/c9.ide.terminal/terminal.js b/plugins/c9.ide.terminal/terminal.js index 606e211f..dcc8e9a0 100644 --- a/plugins/c9.ide.terminal/terminal.js +++ b/plugins/c9.ide.terminal/terminal.js @@ -778,7 +778,7 @@ define(function(require, exports, module) { + "installed. Would you like to open the installer " + "to update to the latest version?", function(){ // Yes - installer.show(); + installer.reinstall("Cloud9 IDE"); }, function(){ // No // Do nothing diff --git a/plugins/c9.ide.ui/forms.js b/plugins/c9.ide.ui/forms.js index a8b39694..354a32cf 100644 --- a/plugins/c9.ide.ui/forms.js +++ b/plugins/c9.ide.ui/forms.js @@ -10,7 +10,7 @@ define(function(require, exports, module) { /***** Initialization *****/ - function Form(options) { + function Form(options, forPlugin) { var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); @@ -41,6 +41,9 @@ define(function(require, exports, module) { if (options.form) add(options.form); + + if (forPlugin) + forPlugin.addOther(function(){ plugin.unload(); }); } var drawn = false; @@ -398,7 +401,7 @@ define(function(require, exports, module) { function update(items) { items.forEach(function(item) { var el = plugin.getElement(item.id); - switch(el.type) { + switch (el.type) { case "dropdown": var dropdown = el.lastChild; @@ -412,7 +415,14 @@ define(function(require, exports, module) { dropdown.setAttribute("value", item.value); break; default: - el.lastChild.setAttribute('value', item.value); + if ("value" in item) + el.lastChild.setAttribute('value', item.value); + if ("onclick" in item) + el.lastChild.onclick = item.onclick; + if ("visible" in item) + el.lastChild.setAttribute("visible", item.visible) + if ("zindex" in item) + el.lastChild.setAttribute("zindex", item.zindex) break; } }) diff --git a/plugins/c9.ide.ui/lib_apf.js b/plugins/c9.ide.ui/lib_apf.js index c7893793..4d12f382 100644 --- a/plugins/c9.ide.ui/lib_apf.js +++ b/plugins/c9.ide.ui/lib_apf.js @@ -9016,6 +9016,9 @@ var modules = { boxFlex: function(oHtml, value, center) { oHtml.style[apf.CSS_FLEX_PROP] = value; }, + boxFlexGrow: function(oHtml, value, center) { + oHtml.style[apf.CSS_FLEX_PROP + "-grow"] = value; + }, "height-rsz": function(oHtml, value, center) { oHtml.style.height = value + PX; if (apf.hasSingleResizeEvent && apf.layout.$onresize) diff --git a/plugins/c9.ide.ui/style.css b/plugins/c9.ide.ui/style.less similarity index 100% rename from plugins/c9.ide.ui/style.css rename to plugins/c9.ide.ui/style.less diff --git a/plugins/c9.ide.ui/ui.js b/plugins/c9.ide.ui/ui.js index f44aec7c..ab26b6f4 100644 --- a/plugins/c9.ide.ui/ui.js +++ b/plugins/c9.ide.ui/ui.js @@ -8,7 +8,7 @@ define(function(require, module, exports) { require("plugins/c9.ide.ui/codebox")(imports.apf); var settings; - var packed = require("text!./style.css").length === 0; + var packed = require("text!./style.less").length === 0; var less = require("./lib_less1.5"); if (less) less.async = true; var packedThemes = packed || options.packedThemes !== false; @@ -40,6 +40,12 @@ define(function(require, module, exports) { apf.Class.prototype.emit = apf.Class.prototype.dispatchEvent; apf.Class.prototype.off = apf.Class.prototype.removeEventListener; + Object.defineProperty(apf.Class.prototype, '$html', { + get: function() { return this.$int || this.$container || this.$ext; }, + enumerable: false, + configurable: false + }); + apf.preProcessCSS = insertLess; // Load a basic document into APF diff --git a/plugins/c9.ide.ui/widgets.datagrid.js b/plugins/c9.ide.ui/widgets.datagrid.js index fbf087ee..329c3764 100644 --- a/plugins/c9.ide.ui/widgets.datagrid.js +++ b/plugins/c9.ide.ui/widgets.datagrid.js @@ -16,9 +16,10 @@ define(function(require, exports, module) { if (!options) throw new Error("options are required"); if (!options.baseName) - options.baseName = "tree"; + options.baseName = "datagrid"; + if (!options.theme) - options.theme = "blackdg"; + options.theme = "blackdg ace-tree-" + (options.baseName || "datagrid"); var model = new TreeModel(); model.columns = options.columns; diff --git a/plugins/c9.ide.ui/widgets.less b/plugins/c9.ide.ui/widgets.less index f70ce2f8..8fcfbef4 100644 --- a/plugins/c9.ide.ui/widgets.less +++ b/plugins/c9.ide.ui/widgets.less @@ -1,7 +1,7 @@ .custom-tree.ace_tree { background: transparent; border: none; - font: 11px Tahoma, Arial; + // font: @general-font-size @general-font-family; .font-smoothing(@tree-font-smoothing) } @@ -43,6 +43,25 @@ line-height: 1.5em; } +.ace_tree .tree-row .checkbox{ + outline: 1px solid rgba(255, 255, 255, 0.35); + background: #2D2D2D; + width: 13px; + height: 13px; + margin: 1px 5px 0 0px; + display: inline-block; + vertical-align: middle; + pointer-events: auto; +} +.ace_tree .tree-row .checkbox.checked{ + background-image: url("@{image-path}/@{menu-check}"); + background-position: -2px -18px; +} +.ace_tree .tree-row .checkbox.half-checked{ + background-color: #565656; + background-position: -2px -18px; +} + .custom-tree.ace_tree .tree-row .toggler{ .image-2x("@{tree-arrow}", 20px, 10px); margin: 0 1px 0 0; diff --git a/plugins/c9.ide.ui/widgets.list.js b/plugins/c9.ide.ui/widgets.list.js index 1306b2a9..8c62a39c 100644 --- a/plugins/c9.ide.ui/widgets.list.js +++ b/plugins/c9.ide.ui/widgets.list.js @@ -19,7 +19,7 @@ define(function(require, exports, module) { }; ui.on("load", function(){ - ui.insertCss(require("text!./widgets.less"), ui); + ui.insertCss(require("text!./widgets.less"), options.staticPrefix, ui); }); /***** Constructors *****/ @@ -56,6 +56,7 @@ define(function(require, exports, module) { ? new TreeModel() : new ListModel()); model.filterCaseInsensitive = true; + model.$sortNodes = false; if (!options.rowHeight) options.rowHeight = 23; @@ -80,12 +81,14 @@ define(function(require, exports, module) { scrollbarVisibilityChanged: acetree.renderer, resize: acetree.renderer, expand: model, - collapse: model + collapse: model, + check: model, + uncheck: model }; emit.sticky("draw"); } - + plugin.on("load", function(){ if (options.container) plugin.attachTo(options.container); @@ -254,6 +257,35 @@ define(function(require, exports, module) { ? new AceTreeEditor(acetree) : null; }, + /** + * + */ + get enableCheckboxes(){ return model.getCheckboxHTML ? true : false; }, + set enableCheckboxes(value){ + model.getCheckboxHTML = value + ? function(node){ + return ""; + } + : null; + + if (value) { + acetree.commands.bindKey("Space", function(e) { + var nodes = acetree.selection.getSelectedNodes(); + var node = acetree.selection.getCursor(); + node.isChecked = !node.isChecked; + nodes.forEach(function(n){ n.isChecked = node.isChecked }); + model._signal(node.isChecked ? "check" : "uncheck", nodes); + model._signal("change"); + }); + } + else { + acetree.commands.bindKey("Space", null); + } + }, /** * */ @@ -339,11 +371,16 @@ define(function(require, exports, module) { */ get getIconHTML(){ return model.getIconHTML; }, set getIconHTML(fn){ model.getIconHTML = fn; }, + /** + * + */ + get getCheckboxHTML(){ return model.getCheckboxHTML; }, + set getCheckboxHTML(fn){ model.getCheckboxHTML = fn; }, /** * */ get sort(){ return model.sort; }, - set sort(fn){ + set sort(fn){ debugger; model.$sortNodes = fn ? true : false; model.$sorted = fn ? true : false; model.sort = fn; @@ -417,6 +454,14 @@ define(function(require, exports, module) { * @event afterRename Fires */ "afterRename", + /** + * @event check Fires + */ + "check", + /** + * @event uncheck Fires + */ + "uncheck", /** * @event select Fires */ @@ -494,6 +539,22 @@ define(function(require, exports, module) { disable: function(){ return acetree.enable(); }, + /** + * + */ + check: function(node, half){ + node.isChecked = half ? -1 : true; + model._signal("check", node); + model._signal("change"); + }, + /** + * + */ + uncheck: function(node){ + node.isChecked = false; + model._signal("uncheck", node); + model._signal("change"); + }, /** * */ diff --git a/plugins/c9.nodeapi/util.js b/plugins/c9.nodeapi/util.js index 9fbff737..9628e42c 100644 --- a/plugins/c9.nodeapi/util.js +++ b/plugins/c9.nodeapi/util.js @@ -463,7 +463,9 @@ function isPrimitive(arg) { exports.isPrimitive = isPrimitive; function isBuffer(arg) { - return arg instanceof Buffer; + // Not relevant outside of node + // return arg instanceof Buffer; + return false; } exports.isBuffer = isBuffer; diff --git a/plugins/c9.vfs.client/endpoint.js b/plugins/c9.vfs.client/endpoint.js index 102b5723..3bd55ca3 100644 --- a/plugins/c9.vfs.client/endpoint.js +++ b/plugins/c9.vfs.client/endpoint.js @@ -1,7 +1,7 @@ define(function(require, exports, module) { "use strict"; - main.consumes = ["Plugin", "auth", "http", "api", "error_handler"]; + main.consumes = ["Plugin", "auth", "http", "api", "error_handler", "metrics"]; main.provides = ["vfs.endpoint"]; return main; @@ -11,6 +11,7 @@ define(function(require, exports, module) { var http = imports.http; var api = imports.api; var errorHandler = imports.error_handler; + var metrics = imports.metrics; /***** Initialization *****/ @@ -168,8 +169,10 @@ define(function(require, exports, module) { // just take the first server that doesn't return an error (function tryNext(i) { - if (i >= servers.length) + if (i >= servers.length) { + metrics.increment("vfs.failed.connect_all", 1, true); return callback(new Error("Disconnected: Could not reach your workspace. Please try again later.")); + } var server = servers[i]; auth.request(server.url + "/" + options.pid, { diff --git a/plugins/c9.vfs.client/vfs.ping.js b/plugins/c9.vfs.client/vfs.ping.js index 13ae4c44..fcebcf6e 100644 --- a/plugins/c9.vfs.client/vfs.ping.js +++ b/plugins/c9.vfs.client/vfs.ping.js @@ -1,7 +1,7 @@ define(function(require, exports, module) { "use strict"; - main.consumes = ["Plugin", "ext", "c9", "vfs"]; + main.consumes = ["Plugin", "ext", "c9", "vfs", "metrics"]; main.provides = ["vfs.ping"]; return main; @@ -9,6 +9,7 @@ define(function(require, exports, module) { var Plugin = imports.Plugin; var c9 = imports.c9; var ext = imports.ext; + var metrics = imports.metrics; /***** Initialization *****/ diff --git a/plugins/c9.vfs.client/vfs_client.js b/plugins/c9.vfs.client/vfs_client.js index b44e2c30..31ec4845 100644 --- a/plugins/c9.vfs.client/vfs_client.js +++ b/plugins/c9.vfs.client/vfs_client.js @@ -1,13 +1,10 @@ -/** - * Smith.io client - * - * @copyright 2013, Ajax.org B.V. - */ - define(function(require, exports, module) { "use strict"; - main.consumes = ["Plugin", "auth", "vfs.endpoint", "dialog.error", "dialog.alert", "error_handler"]; + main.consumes = [ + "Plugin", "auth", "vfs.endpoint", "dialog.error", + "dialog.alert", "error_handler", "metrics" + ]; main.provides = ["vfs"]; return main; @@ -34,6 +31,7 @@ define(function(require, exports, module) { var hideError = errorDialog.hide; var showAlert = imports["dialog.alert"].show; var errorHandler = imports.error_handler; + var metrics = imports.metrics; var eio = require("engine.io"); var Consumer = require("vfs-socket/consumer").Consumer; @@ -50,11 +48,10 @@ define(function(require, exports, module) { var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); - // Give reference to vfs to plugin + // Give reference to vfs to plugins errorDialog.vfs = plugin; var buffer = []; - var installChecked = false; var withInstall = options.withInstall; var dashboardUrl = options.dashboardUrl; var region, vfsBaseUrl, homeUrl, projectUrl, pingUrl, serviceUrl; @@ -209,6 +206,7 @@ define(function(require, exports, module) { vfsEndpoint.get(protocolVersion, function(err, urls) { if (err) { + metrics.increment("vfs.failed.connect", 1, true); if (!showErrorTimer) { showErrorTimer = setTimeout(function() { showVfsError(showErrorTimerMessage); @@ -282,13 +280,8 @@ define(function(require, exports, module) { return; } - if (!installChecked) { - checkInstall(_vfs, callback); - installChecked = true; - } - else { + if (emit("beforeConnect", { done: callback, vfs: _vfs }) !== false) callback(); - } function callback(shouldReconnect) { if (shouldReconnect) { @@ -315,19 +308,6 @@ define(function(require, exports, module) { }); } - function checkInstall(vfs, callback) { - if (!withInstall) - return callback(false); - - vfs.stat(options.installPath + "/installed", {}, function(err, stat) { - if (err && err.code == "ENOENT") { - emit.sticky("install", { callback: callback, vfs: vfs }); - } - else - callback(); - }); - } - var bufferedVfsCalls = []; function vfsCall(method, path, options, callback) { if (Array.isArray(method)) @@ -349,7 +329,6 @@ define(function(require, exports, module) { id = null; buffer = []; - installChecked = false; region = null; vfsBaseUrl = null; homeUrl = null; diff --git a/plugins/c9.vfs.standalone/standalone.js b/plugins/c9.vfs.standalone/standalone.js index 55d2c8a4..44dbb35c 100644 --- a/plugins/c9.vfs.standalone/standalone.js +++ b/plugins/c9.vfs.standalone/standalone.js @@ -48,7 +48,7 @@ function plugin(options, imports, register) { imports.connect.use(api); api.get("/", function(req, res, next) { - res.writeHead(302, { "Location": "/ide.html" }); + res.writeHead(302, { "Location": options.sdk ? "/ide.html" : "/static/places.html" }); res.end(); }); @@ -226,7 +226,7 @@ function plugin(options, imports, register) { options._projects = [options.workspaceDir]; } var wd = options._projects[pid] || options._projects[0]; - console.log(user) + return { workspaceDir: wd, extendOptions: { @@ -316,7 +316,6 @@ function getConfig(requested, options) { try { data = fs.readFileSync(settings[type], "utf8"); } catch (e) { - console.log(e); } settings[type] = data; } diff --git a/plugins/c9.vfs.standalone/www/install.html b/plugins/c9.vfs.standalone/www/install.html index d7e3dce9..0cee36a6 100644 --- a/plugins/c9.vfs.standalone/www/install.html +++ b/plugins/c9.vfs.standalone/www/install.html @@ -35,6 +35,7 @@ app.on("ready", function(){ // if (app.services.c9.debug) window.app = app.services; + app.services.c9.ready(); }); }); }); diff --git a/plugins/c9.vfs.standalone/www/test.html b/plugins/c9.vfs.standalone/www/test.html index 49578f7c..11d99d24 100644 --- a/plugins/c9.vfs.standalone/www/test.html +++ b/plugins/c9.vfs.standalone/www/test.html @@ -186,10 +186,10 @@ tests = JSON.parse(tests); allFiles = tests.list.slice(); - if (!files.length && location.href.slice(-1) == "?") { + if (!files.length && (location.search === "?" || location.search === "")) { var disabled = tests.blacklist.slice().sort(); var enabled = tests.list.slice().sort(); - var all = "run all"; + var all = "run all"; var initErr = document.getElementById('jserror').innerHTML; setupDom("" + "

Enabled tests" + all + "

" @@ -376,8 +376,11 @@ c9Test.run = run; c9Test.runAll = runAll; c9Test.allFiles = allFiles; + if (location.href.match(/\?runAllTests/)) { + files = allFiles; + } c9Test.find = function(r) { - return c9Test.allFiles.filter(function(x) { return x.match(r) }); + return c9Test.allFiles.filter(function(x) { return x.match(r); }); }; c9Test.reRun = c9Test.run; // wait for test.js to initialise chai diff --git a/plugins/c9.vfs.standalone/www/test.js b/plugins/c9.vfs.standalone/www/test.js index a4ac2afb..276a6ce0 100644 --- a/plugins/c9.vfs.standalone/www/test.js +++ b/plugins/c9.vfs.standalone/www/test.js @@ -400,6 +400,7 @@ require([ console.warn(msg); } }, + "installer": { createSession : function(){}, reinstall: function(){} }, "run.gui": { getElement : function(){} }, "debugger": {debug: function() {}, stop: function(){}}, "focusManager": { @@ -407,10 +408,11 @@ require([ } }, - error_handler: {reportError: function(){}}, - installer: { - show: function(){} + "metrics": { + getLastPing: function() { throw Error("Not implemented"); }, + getLastest: function() { throw Error("Not implemented"); }, }, + error_handler: {reportError: function(){}}, proc: { execFile: function() {}, spawn: function() {} diff --git a/scripts/install-sdk.sh b/scripts/install-sdk.sh index 88794bf0..ebfd1b48 100755 --- a/scripts/install-sdk.sh +++ b/scripts/install-sdk.sh @@ -93,7 +93,7 @@ updateCore() { } installGlobalDeps() { - if ! [[ -f ~/.c9/installed ]]; then + if ! [[ -f ~/.c9/installed ]] && ! [[ $os == "windows" ]]; then curl https://raw.githubusercontent.com/c9/install/master/install.sh | bash fi } diff --git a/settings/standalone.js b/settings/standalone.js index 92a162c9..c65482db 100644 --- a/settings/standalone.js +++ b/settings/standalone.js @@ -11,6 +11,7 @@ module.exports = function(manifest, installPath) { var builders = require("../plugins/c9.ide.run.build/builders_list"); var workspaceDir = path.resolve(__dirname + "/../"); + var sdk = !manifest.sdk; if (process.platform == "win32" && process.env.HOME === undefined) { process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH; @@ -37,11 +38,13 @@ module.exports = function(manifest, installPath) { home: home, uid: "-1", dev: true, + sdk: sdk, pid: process.pid, port: process.env.PORT || 8181, host: process.env.IP || "0.0.0.0", testing: false, platform: process.platform, + arch: process.arch, tmux: path.join(installPath, "bin/tmux"), nakBin: path.join(__dirname, "../node_modules/nak/bin/nak"), bashBin: "bash",