From 162e240d431e01865c1db3b87e27265575c2e91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 6 Apr 2015 00:20:43 +0200 Subject: [PATCH 001/101] Add base gulpfile and hr dependencies --- editor/index.html | 4 ++-- gulpfile.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 28 +++++++++++++++++++--------- 3 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 gulpfile.js diff --git a/editor/index.html b/editor/index.html index 2043b60d..4e39aa6d 100644 --- a/editor/index.html +++ b/editor/index.html @@ -4,11 +4,11 @@ Codebox - + - + \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..e6aba933 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,44 @@ +var gulp = require('gulp'); +var del = require('del'); +var less = require('gulp-less'); +var browserify = require('gulp-browserify'); +var runSequence = require('gulp-run-sequence'); +var rename = require('gulp-rename'); +var uglify = require('gulp-uglify'); +var stringify = require('stringify'); + +// Compile Javascript +gulp.task('scripts', function() { + return gulp.src('editor/main.js') + .pipe(browserify({ + debug: false, + transform: ['stringify'] + })) + //.pipe(uglify()) + .pipe(rename('application.js')) + .pipe(gulp.dest('./build/static/js')); +}); + +// Copy html +gulp.task('html', function() { + return gulp.src('editor/index.html') + .pipe(rename('index.html')) + .pipe(gulp.dest('./build/static/')); +}); + +// Less to css +gulp.task('styles', function() { + return gulp.src('./editor/resources/stylesheets/main.less') + .pipe(less()) + .pipe(rename('application.css')) + .pipe(gulp.dest('./build/static/css')); +}); + +// Clean output +gulp.task('clean', function(cb) { + del(['dist/**'], cb); +}); + +gulp.task('default', function(cb) { + runSequence('clean', ['scripts', 'styles', 'html'], cb); +}); \ No newline at end of file diff --git a/package.json b/package.json index 5df78ca9..094432e4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "codebox", "description": "Extensible hybrid IDE", "version": "1.0.0", - "author": "FriendCode Inc. ", + "author": "FriendCode Inc. ", "license": "Apache 2", "preferGlobal": true, "main": "lib/index.js", @@ -46,15 +46,25 @@ "connect-multiparty": "1.1.0" }, "devDependencies": { + "gulp": "^3.8.11", + "gulp-browserify": "^0.5.1", + "gulp-rename": "^1.2.2", + "gulp-uglify": "1.1.0", + "gulp-less": "3.0.2", + "gulp-run-sequence": "0.3.2", + "del": "1.1.1", + "stringify": "3.1.0", "mocha": "1.18.2", - "grunt": "~0.4.2", - "grunt-cli": "0.1.11", - "grunt-hr-builder": "1.1.0", - "grunt-contrib-clean": "0.5.0", - "grunt-contrib-copy": "0.5.0", - "grunt-bower-install-simple": "0.9.2", - "grunt-exec": "0.4.6", - "happyrhino": "1.0.5" + "q": "~1.2.0", + "jquery": "~2.1.3", + "hr.utils": "*", + "hr.app": "*", + "hr.list": "*", + "hr.storage": "*", + "hr.model": "*", + "hr.view": "*", + "hr.collection": "*", + "hr.class": "*" }, "packageDependencies": { "about": "CodeboxIDE/package-about", From 08b0761aedd9963bd537fd4d6ae1401e64b81d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 6 Apr 2015 11:22:34 +0200 Subject: [PATCH 002/101] Move bower dependencies to npm dependencies --- bower.json | 9 --------- package.json | 6 +++++- 2 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 bower.json diff --git a/bower.json b/bower.json deleted file mode 100644 index 16923081..00000000 --- a/bower.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Codebox", - "dependencies": { - "mousetrap": "1.4.6", - "octicons": "2.0.2", - "bower-sockjs-client": "0.3.4", - "moment": "2.7.0" - } -} \ No newline at end of file diff --git a/package.json b/package.json index 094432e4..94d2a461 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,11 @@ "hr.model": "*", "hr.view": "*", "hr.collection": "*", - "hr.class": "*" + "hr.class": "*", + "octicons": "2.2.0", + "mousetrap": "1.4.6", + "moment": "2.9.0", + "sockjs-client": "0.1.3" }, "packageDependencies": { "about": "CodeboxIDE/package-about", From 7055869c3127e1e4e3e40618a2bf24b0b0325eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 6 Apr 2015 11:33:10 +0200 Subject: [PATCH 003/101] Fix styles generation and mimify css --- editor/resources/stylesheets/main.less | 2 +- editor/resources/stylesheets/variables.less | 3 ++- gulpfile.js | 7 ++++++- package.json | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/editor/resources/stylesheets/main.less b/editor/resources/stylesheets/main.less index 853e227c..919173b3 100644 --- a/editor/resources/stylesheets/main.less +++ b/editor/resources/stylesheets/main.less @@ -1,4 +1,4 @@ -@import (less) "../../vendors/octicons/octicons/octicons.css"; +@import "./node_modules/octicons/octicons/octicons.less"; @import "normalize.less"; @import "variables.less"; diff --git a/editor/resources/stylesheets/variables.less b/editor/resources/stylesheets/variables.less index 2c75bbc4..8b1cae6c 100644 --- a/editor/resources/stylesheets/variables.less +++ b/editor/resources/stylesheets/variables.less @@ -1,7 +1,8 @@ @import "./utilities.less"; // Fonts -@FontPath: "fonts/"; +@FontPath: "fonts"; +@octicons-font-path: "@{FontPath}/octions"; // Colors // Color palette is based on base16 ocean diff --git a/gulpfile.js b/gulpfile.js index e6aba933..485af322 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,7 +1,9 @@ +var path = require('path'); var gulp = require('gulp'); var del = require('del'); var less = require('gulp-less'); var browserify = require('gulp-browserify'); +var minifyCSS = require('gulp-minify-css'); var runSequence = require('gulp-run-sequence'); var rename = require('gulp-rename'); var uglify = require('gulp-uglify'); @@ -29,7 +31,10 @@ gulp.task('html', function() { // Less to css gulp.task('styles', function() { return gulp.src('./editor/resources/stylesheets/main.less') - .pipe(less()) + .pipe(less({ + paths: [ path.join(__dirname) ] + })) + .pipe(minifyCSS()) .pipe(rename('application.css')) .pipe(gulp.dest('./build/static/css')); }); diff --git a/package.json b/package.json index 94d2a461..70128dfa 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "gulp-rename": "^1.2.2", "gulp-uglify": "1.1.0", "gulp-less": "3.0.2", + "gulp-minify-css": "1.0.0", "gulp-run-sequence": "0.3.2", "del": "1.1.1", "stringify": "3.1.0", From ddfd7a9b9195e01210c34d058367bc059ff9689c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 6 Apr 2015 23:53:32 +0200 Subject: [PATCH 004/101] Start switching to browserify --- editor/collections/commands.js | 98 ++++--- editor/collections/packages.js | 103 ++++---- editor/collections/users.js | 32 ++- editor/core/application.js | 66 ++--- editor/core/commands.js | 8 +- editor/core/events.js | 24 +- editor/core/packages.js | 34 +-- editor/core/rpc.js | 56 ++-- editor/core/settings.js | 238 ++++++++--------- editor/core/socket.js | 87 +++--- editor/core/statusbar.js | 12 - editor/core/user.js | 8 +- editor/core/users.js | 19 +- editor/main.js | 118 ++++----- editor/models/command.js | 188 ++++++------- editor/models/file.js | 466 ++++++++++++++++----------------- editor/models/package.js | 123 +++++---- editor/models/schema.js | 78 +++--- editor/models/user.js | 49 ++-- editor/resources/init.js | 12 - editor/utils/dragdrop.js | 258 ------------------ editor/utils/string.js | 22 +- editor/views/grid.js | 299 --------------------- package.json | 7 +- 24 files changed, 895 insertions(+), 1510 deletions(-) delete mode 100644 editor/core/statusbar.js delete mode 100644 editor/resources/init.js delete mode 100644 editor/utils/dragdrop.js delete mode 100644 editor/views/grid.js diff --git a/editor/collections/commands.js b/editor/collections/commands.js index 60eebd28..d2ab2062 100644 --- a/editor/collections/commands.js +++ b/editor/collections/commands.js @@ -1,50 +1,48 @@ -define([ - "hr/hr", - "hr/utils", - "hr/promise", - "models/command" -], function(hr, _, Q, Command) { - var logging = hr.Logger.addNamespace("commands"); - - var Commands = hr.Collection.extend({ - model: Command, - - // Initialize - initialize: function() { - Commands.__super__.initialize.apply(this, arguments); - - this.context = {}; - }, - - // Register a new command - register: function(cmd) { - if (_.isArray(cmd)) return _.map(cmd, this.register, this); - - var c = this.get(cmd.id); - if (c) this.remove(c); - - return this.add(cmd); - }, - - // Run a command - run: function(cmd, args) { - - cmd = this.get(cmd); - if (!cmd) return Q.reject(new Error("Command not found: '"+cmd+"'")); - - return cmd.run(args); - }, - - // Set context - setContext: function(id, data) { - logging.log("update context", id); - this.context = { - 'type': id, - 'data': data - }; - this.trigger("context", this.context); - } - }); - - return Commands; -}); \ No newline at end of file +var Q = require("q"); +var _ = require("hr.utils"); +var Collection = require("hr.collection"); +var logger = require("hr.logger")("commands"); + +var Command = require("../models/command"); + +var Commands = hr.Collection.extend({ + model: Command, + + // Initialize + initialize: function() { + Commands.__super__.initialize.apply(this, arguments); + + this.context = {}; + }, + + // Register a new command + register: function(cmd) { + if (_.isArray(cmd)) return _.map(cmd, this.register, this); + + var c = this.get(cmd.id); + if (c) this.remove(c); + + return this.add(cmd); + }, + + // Run a command + run: function(cmd, args) { + + cmd = this.get(cmd); + if (!cmd) return Q.reject(new Error("Command not found: '"+cmd+"'")); + + return cmd.run(args); + }, + + // Set context + setContext: function(id, data) { + logging.log("update context", id); + this.context = { + 'type': id, + 'data': data + }; + this.trigger("context", this.context); + } +}); + +module.exports = Commands; diff --git a/editor/collections/packages.js b/editor/collections/packages.js index c1bc6c7a..215f4371 100644 --- a/editor/collections/packages.js +++ b/editor/collections/packages.js @@ -1,51 +1,54 @@ -define([ - "hr/hr", - "hr/utils", - "hr/promise", - "models/package", - "core/rpc" -], function(hr, _, Q, Package, rpc) { - var Packages = hr.Collection.extend({ - model: Package, - - listAll: function() { - return rpc.execute("packages/list") - .then(this.reset.bind(this)); - }, - - loadAll: function() { - var that = this; - var errors = []; - - return this.listAll() - .then(function() { - return that.reduce(function(prev, pkg) { - errors = errors.concat(_.map(pkg.get("errors"), function(e) { - return { - 'name': pkg.get("name"), - 'error': e - }; - })); - - return prev.then(pkg.load.bind(pkg)) - .fail(function(err) { - errors.push({ - 'name': pkg.get("name"), - 'error': err - }); - return Q(); +var Q = require("q"); +var _ = require("hr.utils"); +var Collection = require("hr.collection"); +var logger = require("hr.logger")("packages"); + +var Package = require("../models/package"); +var rpc = require("../core/rpc"); + + +var Packages = hr.Collection.extend({ + model: Package, + + // Get packages list from backend + listAll: function() { + return rpc.execute("packages/list") + .then(this.reset.bind(this)); + }, + + // Load all plugins from backend + loadAll: function() { + var that = this; + var errors = []; + + return this.listAll() + .then(function() { + return that.reduce(function(prev, pkg) { + errors = errors.concat(_.map(pkg.get("errors"), function(e) { + return { + 'name': pkg.get("name"), + 'error': e + }; + })); + + return prev.then(pkg.load.bind(pkg)) + .fail(function(err) { + errors.push({ + 'name': pkg.get("name"), + 'error': err }); - }, Q()); - }) - .then(function() { - if (errors.length > 0) { - var e = new Error("Error loading packages"); - e.errors = errors; - return Q.reject(e); - } - }); - } - }); - - return Packages; -}); \ No newline at end of file + return Q(); + }); + }, Q()); + }) + .then(function() { + if (errors.length > 0) { + var e = new Error("Error loading packages"); + e.errors = errors; + return Q.reject(e); + } + }); + } +}); + +module.exports = Packages; diff --git a/editor/collections/users.js b/editor/collections/users.js index 573eea76..62ba62ea 100644 --- a/editor/collections/users.js +++ b/editor/collections/users.js @@ -1,20 +1,18 @@ -define([ - "hr/hr", - "hr/utils", - "hr/promise", - "models/user", - "core/rpc" -], function(hr, _, Q, User, rpc) { - var logging = hr.Logger.addNamespace("users"); +var Q = require("q"); +var _ = require("hr.utils"); +var Collection = require("hr.collection"); +var logger = require("hr.logger")("users"); - var Users = hr.Collection.extend({ - model: User, +var User = require("../models/user"); +var rpc = require("../core/rpc"); - listAll: function() { - return rpc.execute("users/list") - .then(this.reset.bind(this)); - }, - }); +var Users = hr.Collection.extend({ + model: User, - return Users; -}); \ No newline at end of file + listAll: function() { + return rpc.execute("users/list") + .then(this.reset.bind(this)); + }, +}); + +module.exports = Users; diff --git a/editor/core/application.js b/editor/core/application.js index d3197311..08eb893f 100644 --- a/editor/core/application.js +++ b/editor/core/application.js @@ -1,44 +1,36 @@ -define([ - "hr/utils", - "hr/dom", - "hr/promise", - "hr/hr", - "views/grid", - "core/commands", - "core/packages" -], function(_, $, Q, hr, GridView, commands, packages) { - // Define base application - var Application = hr.Application.extend({ - el: null, - className: "main-application", - name: "Codebox", - events: { +var _ = require("hr.utils"); +var $ = require("jquery"); +var Q = require("q"); - }, - routes: {}, +var Application = require("hr.app"); +var GridView = require("hr.gridview"); - initialize: function() { - Application.__super__.initialize.apply(this, arguments); +// Define base application +var CodeboxApplication = Application.extend({ + el: null, + className: "main-application", + name: "Codebox", + events: {}, + routes: {}, - this.grid = new GridView({ - columns: 10 - }, this); - this.grid.$el.addClass("main-grid"); - this.grid.appendTo(this); - }, + initialize: function() { + Application.__super__.initialize.apply(this, arguments); - render: function() { - return this.ready(); - }, + this.grid = new GridView({ + columns: 10 + }, this); + this.grid.$el.addClass("main-grid"); + this.grid.appendTo(this); + }, - run: function() { - $(".main-authentication").remove(); - this.$el.appendTo($("body")); + render: function() { + return this.ready(); + }, - return Application.__super__.run.apply(this, arguments); - }, - }); - - var app = new Application(); - return app; + start: function() { + this.$el.appendTo($("body")); + return this.update(); + }, }); + +module.exports = new CodeboxApplication(); diff --git a/editor/core/commands.js b/editor/core/commands.js index 336c1595..9bca2cd6 100644 --- a/editor/core/commands.js +++ b/editor/core/commands.js @@ -1,7 +1,3 @@ -define([ - "collections/commands" -], function(Commands) { - var commands = new Commands(); +var Commands = require("../collections/commands"); - return commands; -}); \ No newline at end of file +module.exports = new Commands(); diff --git a/editor/core/events.js b/editor/core/events.js index 815bcc62..2b7bb28d 100644 --- a/editor/core/events.js +++ b/editor/core/events.js @@ -1,16 +1,14 @@ -define([ - "hr/hr", - "core/socket" -], function(hr, Socket) { - var logging = hr.Logger.addNamespace("events"); +var logger = require("hr.logger")("events"); - var events = new Socket({ - service: "events" - }); +var Socket = require("./socket"); - events.on("do:report", function(e) { - events.trigger("e:"+e.event, e.data); - }); +// Create a socket connected to the events namespace from backend +var events = new Socket({ + service: "events" +}); - return events; -}); \ No newline at end of file +events.on("do:report", function(e) { + events.trigger("e:"+e.event, e.data); +}); + +module.exports = events; diff --git a/editor/core/packages.js b/editor/core/packages.js index fe3afe58..a52a9d33 100644 --- a/editor/core/packages.js +++ b/editor/core/packages.js @@ -1,20 +1,20 @@ -define([ - "collections/packages", - "core/events", - "utils/dialogs" -], function(Packages, events, dialogs) { - var packages = new Packages(); +var Packages = require("../collections/packages"); +var dialogs = require("../utils/dialogs"); +var events = require("./events"); - events.on("e:packages:add", function(pkg) { - pkg = packages.add(pkg); - pkg.load() - .fail(dialogs.error); - }); +var packages = new Packages(); - events.on("e:packages:remove", function(pkg) { - pkg = packages.get(pkg.name); - if (pkg) pkg.destroy(); - }); +// Load new installed packages +events.on("e:packages:add", function(pkg) { + pkg = packages.add(pkg); + pkg.load() + .fail(dialogs.error); +}); - return packages; -}); \ No newline at end of file +// Unload removed packages +events.on("e:packages:remove", function(pkg) { + pkg = packages.get(pkg.name); + if (pkg) pkg.destroy(); +}); + +module.exports = packages; diff --git a/editor/core/rpc.js b/editor/core/rpc.js index 0f0e37c4..9928e63f 100644 --- a/editor/core/rpc.js +++ b/editor/core/rpc.js @@ -1,35 +1,27 @@ -define([ - "hr/hr" -], function(hr) { - var rpc = new hr.Backend({ - prefix: "rpc" - }); +var Q = require("q"); +var axios = require("axios"); +var Backend = require("hr.backend"); - rpc.defaultMethod({ - execute: function(args, options, method) { - options = _.defaults({}, options || {}, { - dataType: "json", - options: { - 'headers': { - 'Content-type': 'application/json' - } - } - }); +var rpc = new Backend({ + prefix: "rpc" +}); - return hr.Requests.post("rpc/"+method, JSON.stringify(args), options).then(function(data) { - return data.result; - }, function(err) { - try { - var errContent = JSON.parse(err.httpRes); - var e = new Error(errContent.error || err.message); - e.code = errContent.code || err.status || 500; - return Q.reject(e); - } catch(e) { - return Q.reject(err); - } - }); - } - }); +rpc.defaultMethod({ + execute: function(args, options, method) { + return Q(axios.post("rpc/"+method, args)) + .then(function(data) { + return data.result; + }, function(err) { + try { + var errContent = JSON.parse(err.httpRes); + var e = new Error(errContent.error || err.message); + e.code = errContent.code || err.status || 500; + return Q.reject(e); + } catch(e) { + return Q.reject(err); + } + }); + } +}); - return rpc; -}); \ No newline at end of file +module.exports = rpc; diff --git a/editor/core/settings.js b/editor/core/settings.js index e5eee997..93b4c4ee 100644 --- a/editor/core/settings.js +++ b/editor/core/settings.js @@ -1,123 +1,123 @@ -define([ - "hr/utils", - "hr/promise", - "hr/hr", - "core/rpc", - "models/file", - "models/schema" -], function(_, Q, hr, rpc, File, Schema) { - var Manager = hr.Class.extend({ - initialize: function() { - Manager.__super__.initialize.apply(this, arguments); - - this.settings = {}; - this.schemas = new (hr.Collection.extend({ model: Schema })); - }, - - // Add/Get a schema - schema: function(id, infos) { - var that = this; - - if (!infos) return this.schemas.get(id); - - this.schemas.add({ - 'id': id, - 'schema': infos - }); - this.importJSON(this.settings, { - save: false, - silent: false - }); - var sch = this.schemas.get(id); - - this.listenTo(sch.data, "change", function() { - this.trigger("change"); - }); - - return sch; - }, - - // Export - exportJson: function() { - var that = this; - return _.object( - this.schemas.map(function(schema) { - return [ - schema.id, - schema.getData() - ]; - }) - ); - }, - - // Import - importJSON: function(data, options) { - options = _.defaults(options || {}, { - save: true, - silent: false - }); - - this.settings = data; - - this.schemas.each(function(schema) { - schema.data.clear({ silent: true }); - schema.data.set(_.defaults(this.settings[schema.id] || {}, schema.getDefaults()), { - silent: options.silent - }); - }, this); - - if (options.save) { - return this.save(); - } - - return Q(); - }, - - // Get file - getFile: function() { - // Generate the string content - var code = JSON.stringify(this.exportJson(), null, 4); - - // Build the file buffer - var f = File.buffer("Codebox Settings.json", code, "codebox-settings.json", { - saveAsFile: false - }); - - // Handle write operations - this.listenTo(f, "write", function(data) { - this.importJSON(JSON.parse(data)); +var Q = require("q"); +var _ = require("hr.utils"); +var Class = require("hr.class"); +var Collection = require("hr.collection"); + +var File = require("../models/file"); +var Schema = require("../models/schema"); + +var rpc = require("./rpc"); + +var Manager = Class.extend({ + initialize: function() { + Manager.__super__.initialize.apply(this, arguments); + + this.settings = {}; + this.schemas = new (Collection.extend({ model: Schema })); + }, + + // Add/Get a schema + schema: function(id, infos) { + var that = this; + + if (!infos) return this.schemas.get(id); + + this.schemas.add({ + 'id': id, + 'schema': infos + }); + this.importJSON(this.settings, { + save: false, + silent: false + }); + var sch = this.schemas.get(id); + + this.listenTo(sch.data, "change", function() { + this.trigger("change"); + }); + + return sch; + }, + + // Export + exportJson: function() { + var that = this; + return _.object( + this.schemas.map(function(schema) { + return [ + schema.id, + schema.getData() + ]; + }) + ); + }, + + // Import + importJSON: function(data, options) { + options = _.defaults(options || {}, { + save: true, + silent: false + }); + + this.settings = data; + + this.schemas.each(function(schema) { + schema.data.clear({ silent: true }); + schema.data.set(_.defaults(this.settings[schema.id] || {}, schema.getDefaults()), { + silent: options.silent }); + }, this); - return f; - }, - - // Save settings on the server - save: function() { - return rpc.execute("settings/set", this.exportJson()); - }, - - // Load settings from the server - load: function() { - return rpc.execute("settings/get") - .then(_.partialRight(this.importJSON, { save: false }).bind(this)); - }, - - // Return as a schema - toSchema: function() { - return { - title: "Settings", - type: "object", - properties: _.chain(this.schemas.models) - .map(function(sch) { - sch = sch.toJSON(); - return [sch.id, sch.schema]; - }) - .object() - .value() - }; + if (options.save) { + return this.save(); } - }); - var settings = new Manager(); - return settings; -}); \ No newline at end of file + return Q(); + }, + + // Get file + getFile: function() { + // Generate the string content + var code = JSON.stringify(this.exportJson(), null, 4); + + // Build the file buffer + var f = File.buffer("Codebox Settings.json", code, "codebox-settings.json", { + saveAsFile: false + }); + + // Handle write operations + this.listenTo(f, "write", function(data) { + this.importJSON(JSON.parse(data)); + }); + + return f; + }, + + // Save settings on the server + save: function() { + return rpc.execute("settings/set", this.exportJson()); + }, + + // Load settings from the server + load: function() { + return rpc.execute("settings/get") + .then(_.partialRight(this.importJSON, { save: false }).bind(this)); + }, + + // Return as a schema + toSchema: function() { + return { + title: "Settings", + type: "object", + properties: _.chain(this.schemas.models) + .map(function(sch) { + sch = sch.toJSON(); + return [sch.id, sch.schema]; + }) + .object() + .value() + }; + } +}); + +module.exports = new Manager(); diff --git a/editor/core/socket.js b/editor/core/socket.js index 703100de..00a5c3e4 100644 --- a/editor/core/socket.js +++ b/editor/core/socket.js @@ -1,52 +1,49 @@ -define([ - "hr/hr", - "sockjs" -], function(hr, sockjs) { - var logging = hr.Logger.addNamespace("socket"); +var Class = require("hr.class"); +var sockjs = require("sockjs-client"); +var logger = require("hr.logger")("socket"); - var Socket = hr.Class.extend({ - initialize: function() { - var that = this; - Socket.__super__.initialize.apply(this, arguments); +var Socket = Class.extend({ + initialize: function() { + var that = this; + Socket.__super__.initialize.apply(this, arguments); - logging.log("connecting to service", this.options.service); - this.sock = new SockJS(window.location.origin+window.location.pathname+"socket/"+this.options.service); - this.sock.onopen = function() { - that.trigger("open"); - }; - this.sock.onmessage = function(e) { - var data = JSON.parse(e.data); + logger.log("connecting to service", this.options.service); + this.sock = new SockJS(window.location.origin+window.location.pathname+"socket/"+this.options.service); + this.sock.onopen = function() { + that.trigger("open"); + }; + this.sock.onmessage = function(e) { + var data = JSON.parse(e.data); - that.trigger('data', data); - if (data.method) { - that.trigger('do:'+data.method, data.data || {}); - } - }; - this.sock.onclose = function() { - that.trigger("close"); - }; - }, + that.trigger('data', data); + if (data.method) { + that.trigger('do:'+data.method, data.data || {}); + } + }; + this.sock.onclose = function() { + that.trigger("close"); + }; + }, - // Send a message - send: function(message) { - this.sock.send(JSON.stringify(message)); - return this; - }, + // Send a message + send: function(message) { + this.sock.send(JSON.stringify(message)); + return this; + }, - // Call a method - do: function(method, data) { - return this.send({ - 'method': method, - 'data': data - }); - }, + // Call a method + do: function(method, data) { + return this.send({ + 'method': method, + 'data': data + }); + }, - // Close the connection - close: function() { - this.sock.close(); - return this; - } - }); + // Close the connection + close: function() { + this.sock.close(); + return this; + } +}); - return Socket; -}); \ No newline at end of file +module.exports = Socket; diff --git a/editor/core/statusbar.js b/editor/core/statusbar.js deleted file mode 100644 index 3df3f6fb..00000000 --- a/editor/core/statusbar.js +++ /dev/null @@ -1,12 +0,0 @@ -define([ - "hr/hr" -], function(hr) { - var StatusBar = hr.View.extend({ - - }); - - var status = new hr.Model(); - status.view = new StatusBar(); - - return status; -}); \ No newline at end of file diff --git a/editor/core/user.js b/editor/core/user.js index ac8e5520..49e13c8c 100644 --- a/editor/core/user.js +++ b/editor/core/user.js @@ -1,7 +1,3 @@ -define([ - "models/user" -], function(User) { - var user = new User(); +var User = require("../models/user"); - return user; -}); \ No newline at end of file +module.exports = new User(); diff --git a/editor/core/users.js b/editor/core/users.js index ce72da4f..76f4a6d2 100644 --- a/editor/core/users.js +++ b/editor/core/users.js @@ -1,13 +1,10 @@ -define([ - "collections/users", - "core/events" -], function(Users, events) { - var users = new Users(); +var Users = require("../collections/users"); - // Listen to update - events.on("e:users", function() { - users.listAll(); - }); +var users = new Users(); - return users; -}); \ No newline at end of file +// Listen to update +events.on("e:users", function() { + users.listAll(); +}); + +module.exports = users; diff --git a/editor/main.js b/editor/main.js index ca356c45..f1007ad6 100644 --- a/editor/main.js +++ b/editor/main.js @@ -1,65 +1,63 @@ -require([ - "hr/utils", - "hr/dom", - "hr/promise", - "hr/hr", - "hr/args", - "resources/init", - "core/application", - "core/commands", - "core/packages", - "core/user", - "core/users", - "core/settings", - "utils/dialogs", - "utils/menu", - "models/file", - "utils/date", - "settings/keybindings", - "utils/upload" -], function(_, $, Q, hr, args, resources, app, commands, packages, user, users, settings, dialogs, menu, File) { - // Create the global object for packages - window.codebox = { - require: require, - app: app, - user: user, - root: new File(), - settings: settings - }; +var _ = require("hr.utils"); +var $ = require("jquery"); +var Q = require("q"); - commands.register({ - id: "settings.open", - title: "Settings: Open", - icon: "gear", - shortcuts: [ - "mod+," - ], - run: function(args, context) { - return commands.run("file.open", { - file: settings.getFile() - }); - } - }); +var app = require("./core/application"); +var commands = require("./core/commands"); +var packages = require("./core/packages"); +var user = require("./core/user"); +var users = require("./core/users"); +var settings = require("./core/settings"); +var dialogs = require("./utils/dialogs"); +var menu = require("./utils/menu"); +var File = require("./models/file"); - // Start running the applications - resources() - .then(codebox.user.whoami.bind(codebox.user)) - .then(codebox.root.stat.bind(codebox.root, "./")) - .then(settings.load.bind(settings)) - .then(users.listAll.bind(users)) - .then(function() { - return packages.loadAll() - .fail(function(err) { - var message = "

"+err.message+"

"; - if (err.errors) { - message += "
    "+ _.map(err.errors, function(e) { - return "
  • "+_.escape(e.name)+": "+_.escape(e.error)+"
  • "; - }).join("\n")+ "
"; - } +var date = require("./utils/date"); +var keybindings = require("./settings/keybindings"); +var upload = require("./utils/upload"); - return dialogs.alert(message, { html: true }) - }); - }) - .then(app.run.bind(app)) +// Create the global object for packages +window.codebox = { + require: require, + app: app, + user: user, + root: new File(), + settings: settings +}; + +commands.register({ + id: "settings.open", + title: "Settings: Open", + icon: "gear", + shortcuts: [ + "mod+," + ], + run: function(args, context) { + return commands.run("file.open", { + file: settings.getFile() + }); + } }); + +// Start running the applications +Q() +.then(codebox.user.whoami.bind(codebox.user)) +.then(codebox.root.stat.bind(codebox.root, "./")) +.then(settings.load.bind(settings)) +.then(users.listAll.bind(users)) +.then(function() { + return packages.loadAll() + .fail(function(err) { + var message = "

"+err.message+"

"; + if (err.errors) { + message += "
    "+ _.map(err.errors, function(e) { + return "
  • "+_.escape(e.name)+": "+_.escape(e.error)+"
  • "; + }).join("\n")+ "
"; + } + + return dialogs.alert(message, { html: true }) + }); +}) +.then(app.start.bind(app)); + diff --git a/editor/models/command.js b/editor/models/command.js index f140e78d..ad4ecabd 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -1,94 +1,94 @@ -define([ - "hr/hr", - "hr/utils", - "utils/keyboard" -], function(hr, _, keyboard) { - var logging = hr.Logger.addNamespace("command"); - var ARGS = { - 'number': parseInt - }; - - var Command = hr.Model.extend({ - defaults: { - // Unique id for the command - id: null, - - // Title for the command - title: null, - - // Run command - run: function(context) {}, - - // Context needed for the command - context: [], - - // Arguments - arguments: [], - - // Keyboard shortcuts - shortcuts: [] - }, - - // Constructor - initialize: function() { - Command.__super__.initialize.apply(this, arguments); - - this.justRun = function(e) { - if (e) e.preventDefault(); - this.run({}, e); - }.bind(this); - - this.listenTo(this, "change:shortcuts", this.bindKeyboard); - this.listenTo(this, "destroy", this.unbindKeyboard); - this.bindKeyboard(); - }, - - // Unbind keyboard shortcuts - unbindKeyboard: function() { - if (!this._shortcuts) return; - - keyboard.unbind(this._shortcuts, this, this.justRun); - }, - - // Bind keyboard shortcuts - bindKeyboard: function() { - this.unbindKeyboard(); - this._shortcuts = _.clone(this.get("shortcuts", [])); - keyboard.bind(this._shortcuts, this.justRun, this); - }, - - // Run a command - run: function(args, origin) { - var that = this; - - // Check context - if (!this.isValidContext()) return Q(); - - logging.log("Run", this.get("id")); - - return Q() - .then(function() { - return that.get("run").apply(that, [ args || {}, that.collection.context.data, origin ]); - }) - .fail(function(err) { - logging.error("Command failed", err); - }); - }, - - // Shortcut text - shortcutText: function() { - return keyboard.toText(this.get("shortcuts")); - }, - - // Valid context - isValidContext: function() { - var context = this.get("context") || []; - return (context.length == 0 - || !this.collection - || !this.collection.context - || _.contains(context, this.collection.context.type)); - } - }); - - return Command; -}); \ No newline at end of file +var Q = require("q"); +var _ = require("hr.utils"); +var Model = require("hr.model"); +var logger = require("hr.logger")("command"); + +var keyboard = require("../utils/keyboard"); + +var ARGS = { + 'number': parseInt +}; + +var Command = Model.extend({ + defaults: { + // Unique id for the command + id: null, + + // Title for the command + title: null, + + // Run command + run: function(context) {}, + + // Context needed for the command + context: [], + + // Arguments + arguments: [], + + // Keyboard shortcuts + shortcuts: [] + }, + + // Constructor + initialize: function() { + Command.__super__.initialize.apply(this, arguments); + + this.justRun = function(e) { + if (e) e.preventDefault(); + this.run({}, e); + }.bind(this); + + this.listenTo(this, "change:shortcuts", this.bindKeyboard); + this.listenTo(this, "destroy", this.unbindKeyboard); + this.bindKeyboard(); + }, + + // Unbind keyboard shortcuts + unbindKeyboard: function() { + if (!this._shortcuts) return; + + keyboard.unbind(this._shortcuts, this, this.justRun); + }, + + // Bind keyboard shortcuts + bindKeyboard: function() { + this.unbindKeyboard(); + this._shortcuts = _.clone(this.get("shortcuts", [])); + keyboard.bind(this._shortcuts, this.justRun, this); + }, + + // Run a command + run: function(args, origin) { + var that = this; + + // Check context + if (!this.isValidContext()) return Q(); + + logger.log("Run", this.get("id")); + + return Q() + .then(function() { + return that.get("run").apply(that, [ args || {}, that.collection.context.data, origin ]); + }) + .fail(function(err) { + logger.error("Command failed", err); + }); + }, + + // Shortcut text + shortcutText: function() { + return keyboard.toText(this.get("shortcuts")); + }, + + // Valid context + isValidContext: function() { + var context = this.get("context") || []; + return (context.length == 0 + || !this.collection + || !this.collection.context + || _.contains(context, this.collection.context.type)); + } +}); + +module.exports = Command; diff --git a/editor/models/file.js b/editor/models/file.js index 6eb7c164..6d31b9f1 100644 --- a/editor/models/file.js +++ b/editor/models/file.js @@ -1,238 +1,238 @@ -define([ - "hr/hr", - "hr/utils", - "hr/promise", - "core/rpc", - "core/commands", - "core/events", - "utils/hash", - "utils/dialogs" -], function(hr, _, Q, rpc, commands, events, hash, dialogs) { - var File = hr.Model.extend({ - defaults: { - path: null, - name: null, - directory: false, - size: 0, - mtime: 0, - atime: 0, - buffer: null - }, - idAttribute: "name", - - // Initialize - initialize: function() { - File.__super__.initialize.apply(this, arguments); - - this.options = _.defaults(this.options, { - saveAsFile: true - }); - - this.listenTo(events, "e:fs:modified", _.partial(this._dispatchFsEvent, "modified")); - this.listenTo(events, "e:fs:deleted", _.partial(this._dispatchFsEvent, "deleted")); - this.listenTo(events, "e:fs:created", _.partial(this._dispatchFsEvent, "created")); - }, - - // Dispatch fs event - _dispatchFsEvent: function(type, paths) { - var path, childs; - - if (this.isBuffer()) return; - - path = this.get("path"); - if (_.contains(paths, path)) { - this.trigger("fs:"+type); - } - if (this.isDirectory()) { - childs = _.filter(paths, this.isChild, this); - if (childs.length > 0) { - this.trigger("fs:files:"+type); - } +var Q = require("q"); +var _ = require("hr.utils"); +var Model = require("hr.model"); +var logger = require("hr.logger")("files"); + +var rpc = require("../core/rpc"); +var commands = require("../core/commands"); +var events = require("../core/events"); +var hash = require("../utils/hash"); +var dialogs = require("../utils/dialogs"); + +var File = Model.extend({ + defaults: { + path: null, + name: null, + directory: false, + size: 0, + mtime: 0, + atime: 0, + buffer: null + }, + idAttribute: "name", + + // Initialize + initialize: function() { + File.__super__.initialize.apply(this, arguments); + + this.options = _.defaults(this.options, { + saveAsFile: true + }); + + this.listenTo(events, "e:fs:modified", _.partial(this._dispatchFsEvent, "modified")); + this.listenTo(events, "e:fs:deleted", _.partial(this._dispatchFsEvent, "deleted")); + this.listenTo(events, "e:fs:created", _.partial(this._dispatchFsEvent, "created")); + }, + + // Dispatch fs event + _dispatchFsEvent: function(type, paths) { + var path, childs; + + if (this.isBuffer()) return; + + path = this.get("path"); + if (_.contains(paths, path)) { + this.trigger("fs:"+type); + } + if (this.isDirectory()) { + childs = _.filter(paths, this.isChild, this); + if (childs.length > 0) { + this.trigger("fs:files:"+type); } - }, - - // Open this file - open: function() { - return commands.run("file.open", { - path: this.get("path") - }); - }, - - // Check if is a directory - isDirectory: function() { - return this.get("directory"); - }, - - // Check if a file is a buffer or exists - isBuffer: function() { - return this.get("buffer") != null; - }, - - // Test if a path is child - isChild: function(path) { - var parts1 = _.filter(path.split("/"), function(p) { return p.length > 0; }); - var parts2 = _.filter(this.get("path").split("/"), function(p) { return p.length > 0; }); - return (parts1.length == (parts2.length+1)); - }, - - // List files in this directory - list: function() { - return rpc.execute("fs/list", { - 'path': this.get("path") - }) - .then(function(files) { - return _.map(files, function(file) { - return new File({}, file); - }); - }); - }, - - // Get a specific file - stat: function(path) { - var that = this; - - return rpc.execute("fs/stat", { - 'path': path - }) - .then(function(file) { - that.del("buffer", { silent: true }); - return that.set(file); - }) - .thenResolve(that); - }, - - // Read file content - read: function() { - if (this.isBuffer()) return Q(this.get("buffer")); - - return rpc.execute("fs/read", { - 'path': this.get("path") - }) - .get("content") - .then(hash.atob); - }, - - // Write file content - write: function(content) { - var that = this; - - return Q() - .then(function() { - if (that.isBuffer()) return Q(that.set("buffer", content)); - - return rpc.execute("fs/write", { - 'path': that.get("path"), - 'content': hash.btoa(content) - }); - }) - .then(function() { - that.trigger("write", content); - }); - }, - - // Rename - rename: function(name) { - var that = this; - if (this.isBuffer()) return Q(); - - return rpc.execute("fs/rename", { - 'from': this.get("path"), - 'name': name - }) - .then(function(f) { - that.set(f); - }); - }, - - // Remove this file - remove: function() { - if (this.isBuffer()) return Q(this.destroy()); - - return rpc.execute("fs/remove", { - 'path': this.get("path") - }) - .then(this.destroy.bind(this)); - }, - - // Create a file in this folder - create: function(name) { - return File.create(this.get("path"), name); - }, - - // Create a folder in this folder - mkdir: function(name) { - return File.mkdir(this.get("path"), name); - }, - - // Get by extension - getExtension: function() { - return "."+this.get("name").split('.').pop(); - }, - - // Save file - save: function(content) { - var that = this; - - return Q() - .then(function() { - if (!that.isBuffer() || !that.options.saveAsFile) return that.write(content); - - return dialogs.prompt("Save as:", that.get("name")) - .then(function(_path) { - return rpc.execute("fs/write", { - 'path': _path, - 'content': hash.btoa(content), - 'override': false - }) - .then(function() { - return that.stat(_path); - }) - .fail(dialogs.error); - }); - }); } - }, { - // Get a specific file - get: function(path) { - var f = new File(); - - return f.stat(path); - }, - - // Create a file buffer - buffer: function(name, content, id, options) { - var f = new File(options || {}, { - 'name': name, - 'buffer': content, - 'path': "buffer://"+(id || _.uniqueId("tmp")), - 'directory': false + }, + + // Open this file + open: function() { + return commands.run("file.open", { + path: this.get("path") + }); + }, + + // Check if is a directory + isDirectory: function() { + return this.get("directory"); + }, + + // Check if a file is a buffer or exists + isBuffer: function() { + return this.get("buffer") != null; + }, + + // Test if a path is child + isChild: function(path) { + var parts1 = _.filter(path.split("/"), function(p) { return p.length > 0; }); + var parts2 = _.filter(this.get("path").split("/"), function(p) { return p.length > 0; }); + return (parts1.length == (parts2.length+1)); + }, + + // List files in this directory + list: function() { + return rpc.execute("fs/list", { + 'path': this.get("path") + }) + .then(function(files) { + return _.map(files, function(file) { + return new File({}, file); }); - - return f; - }, - - // Create a new file - create: function(path, name) { - return rpc.execute("fs/create", { - 'path': path, - 'name': name - }) - .then(function(f) { - return new File({}, f); + }); + }, + + // Get a specific file + stat: function(path) { + var that = this; + + return rpc.execute("fs/stat", { + 'path': path + }) + .then(function(file) { + that.del("buffer", { silent: true }); + return that.set(file); + }) + .thenResolve(that); + }, + + // Read file content + read: function() { + if (this.isBuffer()) return Q(this.get("buffer")); + + return rpc.execute("fs/read", { + 'path': this.get("path") + }) + .get("content") + .then(hash.atob); + }, + + // Write file content + write: function(content) { + var that = this; + + return Q() + .then(function() { + if (that.isBuffer()) return Q(that.set("buffer", content)); + + return rpc.execute("fs/write", { + 'path': that.get("path"), + 'content': hash.btoa(content) }); - }, - - // Create a new folder - mkdir: function(path, name) { - return rpc.execute("fs/mkdir", { - 'path': path, - 'name': name - }) - .then(function(f) { - return new File({}, f); + }) + .then(function() { + that.trigger("write", content); + }); + }, + + // Rename + rename: function(name) { + var that = this; + if (this.isBuffer()) return Q(); + + return rpc.execute("fs/rename", { + 'from': this.get("path"), + 'name': name + }) + .then(function(f) { + that.set(f); + }); + }, + + // Remove this file + remove: function() { + if (this.isBuffer()) return Q(this.destroy()); + + return rpc.execute("fs/remove", { + 'path': this.get("path") + }) + .then(this.destroy.bind(this)); + }, + + // Create a file in this folder + create: function(name) { + return File.create(this.get("path"), name); + }, + + // Create a folder in this folder + mkdir: function(name) { + return File.mkdir(this.get("path"), name); + }, + + // Get by extension + getExtension: function() { + return "."+this.get("name").split('.').pop(); + }, + + // Save file + save: function(content) { + var that = this; + + return Q() + .then(function() { + if (!that.isBuffer() || !that.options.saveAsFile) return that.write(content); + + return dialogs.prompt("Save as:", that.get("name")) + .then(function(_path) { + return rpc.execute("fs/write", { + 'path': _path, + 'content': hash.btoa(content), + 'override': false + }) + .then(function() { + return that.stat(_path); + }) + .fail(dialogs.error); }); - } - }); - - return File; -}); \ No newline at end of file + }); + } +}, { + // Get a specific file + get: function(path) { + var f = new File(); + + return f.stat(path); + }, + + // Create a file buffer + buffer: function(name, content, id, options) { + var f = new File(options || {}, { + 'name': name, + 'buffer': content, + 'path': "buffer://"+(id || _.uniqueId("tmp")), + 'directory': false + }); + + return f; + }, + + // Create a new file + create: function(path, name) { + return rpc.execute("fs/create", { + 'path': path, + 'name': name + }) + .then(function(f) { + return new File({}, f); + }); + }, + + // Create a new folder + mkdir: function(path, name) { + return rpc.execute("fs/mkdir", { + 'path': path, + 'name': name + }) + .then(function(f) { + return new File({}, f); + }); + } +}); + +module.exports = File; diff --git a/editor/models/package.js b/editor/models/package.js index 446398c8..a7ce5a07 100644 --- a/editor/models/package.js +++ b/editor/models/package.js @@ -1,74 +1,73 @@ -define([ - "hr/hr" -], function(hr) { - var logging = hr.Logger.addNamespace("package"); +var Q = require("q"); +var _ = require("hr.utils"); +var Model = require("hr.model"); +var logger = require("hr.logger")("package"); - var Package = hr.Model.extend({ - defaults: { - name: null, - errors: [] - }, - idAttribute: "name", +var Package = Model.extend({ + defaults: { + name: null, + errors: [] + }, + idAttribute: "name", - /* - * Return base url for the addon - */ - url: function() { - var basePath = window.location.pathname; - basePath = basePath.substring(0, basePath.lastIndexOf('/')+1); - return basePath+"packages/"+this.get("name"); - }, + /* + * Return base url for the addon + */ + url: function() { + var basePath = window.location.pathname; + basePath = basePath.substring(0, basePath.lastIndexOf('/')+1); + return basePath+"packages/"+this.get("name"); + }, - /** - * Load the addon - */ - load: function() { - var context, main, pkgRequireConfig, pkgRequire, that = this - var d = Q.defer(); + /** + * Load the addon + */ + load: function() { + var context, main, pkgRequireConfig, pkgRequire, that = this + var d = Q.defer(); - if (!this.get("main")) return Q(); + if (!this.get("main")) return Q(); - logging.log("Load", this.get("name")); - context = "package."+this.get("name"); - main = this.get("main", "index"); + logger.log("Load", this.get("name")); + context = "package."+this.get("name"); + main = this.get("main", "index"); - // Require config - pkgRequireConfig = { - 'context': context, - 'baseUrl': this.url(), - 'waitSeconds': 200, - 'paths': {}, - 'map': { - '*': { - 'css': 'require-tools/css/css', - 'less': 'require-tools/less/less', - 'text': 'require-tools/text/text' - } + // Require config + pkgRequireConfig = { + 'context': context, + 'baseUrl': this.url(), + 'waitSeconds': 200, + 'paths': {}, + 'map': { + '*': { + 'css': 'require-tools/css/css', + 'less': 'require-tools/less/less', + 'text': 'require-tools/text/text' } - }; - pkgRequireConfig.paths[main] = "pkg-build"; + } + }; + pkgRequireConfig.paths[main] = "pkg-build"; - // Require context - pkgRequire = require.config(pkgRequireConfig); + // Require context + pkgRequire = require.config(pkgRequireConfig); - // Load main module - pkgRequire([main], function(globals) { - d.resolve() - }, function(err) { - logging.error(err); - d.reject(err); - }); + // Load main module + pkgRequire([main], function(globals) { + d.resolve() + }, function(err) { + logger.error(err); + d.reject(err); + }); - return d.promise.timeout(5000, "This addon took to long to load (> 5seconds)"); - }, + return d.promise.timeout(5000, "This addon took to long to load (> 5seconds)"); + }, - /** - * Return version as a number - */ - version: function() { - return parseInt(this.get("version").replace(/\./g,"")); - } - }); + /** + * Return version as a number + */ + version: function() { + return parseInt(this.get("version").replace(/\./g,"")); + } +}); - return Package; -}); \ No newline at end of file +module.exports = Package; diff --git a/editor/models/schema.js b/editor/models/schema.js index 8ecf5d69..04df21e8 100644 --- a/editor/models/schema.js +++ b/editor/models/schema.js @@ -1,49 +1,47 @@ -define([ - "hr/hr" -], function(hr) { - var _getDefaults = function(schema) { - if (typeof schema['default'] !== 'undefined') { - return schema['default']; - } else if (schema.type === 'object') { - if (!schema.properties) { return {}; } - - for (var key in schema.properties) { - if (schema.properties.hasOwnProperty(key)) { - schema.properties[key] = _getDefaults(schema.properties[key]); - - if (typeof schema.properties[key] === 'undefined') { - delete schema.properties[key]; - } +var Model = require("hr.model"); + +var _getDefaults = function(schema) { + if (typeof schema['default'] !== 'undefined') { + return schema['default']; + } else if (schema.type === 'object') { + if (!schema.properties) { return {}; } + + for (var key in schema.properties) { + if (schema.properties.hasOwnProperty(key)) { + schema.properties[key] = _getDefaults(schema.properties[key]); + + if (typeof schema.properties[key] === 'undefined') { + delete schema.properties[key]; } } - - return schema.properties; - } else if (schema.type === 'array') { - if (!schema.items) { return []; } - return [_getDefaults(schema.items)]; } - }; - var Schema = hr.Model.extend({ - defaults: { - id: null, - schema: {} - }, + return schema.properties; + } else if (schema.type === 'array') { + if (!schema.items) { return []; } + return [_getDefaults(schema.items)]; + } +}; - initialize: function() { - Schema.__super__.initialize.apply(this, arguments); +var Schema = Model.extend({ + defaults: { + id: null, + schema: {} + }, - this.data = new hr.Model(); - }, + initialize: function() { + Schema.__super__.initialize.apply(this, arguments); - getDefaults: function(schema) { - return _getDefaults(this.toJSON().schema || {}); - }, + this.data = new Model(); + }, - getData: function() { - return _.extend({}, this.getDefaults(), this.data.toJSON()); - } - }); + getDefaults: function(schema) { + return _getDefaults(this.toJSON().schema || {}); + }, + + getData: function() { + return _.extend({}, this.getDefaults(), this.data.toJSON()); + } +}); - return Schema; -}); \ No newline at end of file +module.exports = Schema; diff --git a/editor/models/user.js b/editor/models/user.js index 95a00cf3..bf4febc3 100644 --- a/editor/models/user.js +++ b/editor/models/user.js @@ -1,29 +1,28 @@ -define([ - "hr/hr", - "hr/utils", - "core/rpc" -], function(hr, _, rpc) { - var logging = hr.Logger.addNamespace("users"); +var Q = require("q"); +var _ = require("hr.utils"); +var Model = require("hr.model"); +var logger = require("hr.logger")("users"); - var User = hr.Model.extend({ - defaults: { - id: null, - name: null, - email: null, - color: null - }, +var rpc = require("../core/rpc"); - // Identify the logged in user - whoami: function() { - var that = this; +var User = hr.Model.extend({ + defaults: { + id: null, + name: null, + email: null, + color: null + }, - return rpc.execute("users/whoami") - .then(function(data) { - return that.set(data); - }) - .thenResolve(that); - }, - }); + // Identify the logged in user + whoami: function() { + var that = this; - return User; -}); \ No newline at end of file + return rpc.execute("users/whoami") + .then(function(data) { + return that.set(data); + }) + .thenResolve(that); + }, +}); + +module.exports = User; diff --git a/editor/resources/init.js b/editor/resources/init.js deleted file mode 100644 index 1919172d..00000000 --- a/editor/resources/init.js +++ /dev/null @@ -1,12 +0,0 @@ -define([ - "hr/hr", - "hr/promise" -], function(hr, Q) { - hr.Resources.addNamespace("templates", { - loader: "text" - }); - - return function() { - return Q(); - }; -}); \ No newline at end of file diff --git a/editor/utils/dragdrop.js b/editor/utils/dragdrop.js deleted file mode 100644 index 02eea172..00000000 --- a/editor/utils/dragdrop.js +++ /dev/null @@ -1,258 +0,0 @@ -define([ - 'hr/utils', - 'hr/hr', - 'hr/dom' -], function (_, hr, $) { - // Events for tablet and desktop - var events = { - 'start': "mousedown", - 'stop': "mouseup", - 'move': "mousemove", - 'enter': "mouseenter", - 'leave': "mouseleave" - }; - - if (navigator.userAgent.search('Mobile') > 0) { - events = { - 'start': "touchstart", - 'stop': "touchend", - 'move': "touchmove", - 'enter': "touchenter", - 'leave': "touchleave" - }; - } - - // Define cursor - var storedStylesheet = null; - var setCursor = function(cs) { - // Reset cursor - if (storedStylesheet) storedStylesheet.remove(); - storedStylesheet = null; - - // Set new cursor - if (cs) storedStylesheet = $( "" ).appendTo($("body")); - }; - var resetCursor = _.partial(setCursor, null); - - var DropArea = hr.Class.extend({ - defaults: { - // View for this area - view: null, - - // Class when drop data - className: "dragover", - - // Draggable type - dragType: null, - - // Handler for drop - handler: null, - - // Constrain elastic - constrain: null - }, - - initialize: function() { - DropArea.__super__.initialize.apply(this, arguments); - var that = this; - - this.view = this.options.view; - this.$el = this.view.$el; - - this.dragType = this.options.dragType; - - this.$el.on(events["enter"], function(e) { - if (that.dragType.isDragging()) { - e.stopPropagation(); - that.dragType.enterDropArea(that); - that.$el.addClass("dragover"); - } - }); - - this.$el.on(events["leave"], function(e) { - that.$el.removeClass("dragover"); - that.dragType.exitDropArea(); - }); - - this.on("drop", function() { - that.$el.removeClass("dragover"); - }); - - if (this.options.handler) this.on("drop", this.options.handler); - } - }); - - var DraggableType = hr.Class.extend({ - initialize: function() { - DraggableType.__super__.initialize.apply(this, arguments); - - // Data transfered - this.data = null; - - // State - this.state = true; - - // Drop handler - this.drop = []; - }, - - // Toggle enable/disable drag and drop - toggle: function(st) { - this.state = st; - return this; - }, - - // Is currently dragging data - isDragging: function() { - return this.data != null; - }, - - // Get drop - getDrop: function() { - return (this.drop.length > 0)? this.drop[this.drop.length - 1] : null; - }, - - // Enter drop area - enterDropArea: function(area) { - this.drop.push(area); - }, - - // Exit drop area - exitDropArea: function() { - this.drop.pop(); - }, - - // Enable drag and drop in a object - enableDrag: function(options) { - var that = this, $document = $(document), $el, data; - - options = _.defaults(options || {}, { - // View to drag - view: null, - - // Element to drag - el: null, - - // Data to transfer - data: null, - - // Base drop area - baseDropArea: null, - - // Before dragging - start: null, - - // Cursor - cursor: "copy" - }); - if (options.el) $el = $(options.el); - if (options.view) $el = options.view.$el, data = options.view; - if (options.data) data = options.data; - - $el.on(events["start"], function(e) { - if (e.type == 'mousedown' && e.originalEvent.button != 0) return; - if (!that.state) return; - e.preventDefault(); - e.stopPropagation(); - - var dx, dy, hasMove = false; - - // origin mouse - var oX = e.pageX; - var oY = e.pageY; - - // origin element - var poX = $el.offset().left; - var poY = $el.offset().top; - - // element new position - var ex, ey, ew, eh; - ew = $el.width(); - eh = $el.height(); - - // Constrain element - var cw, ch, cx, cy; - - if (options.start && options.start() === false) return; - - that.drop = []; - if (options.baseDropArea) that.enterDropArea(options.baseDropArea); - that.data = data; - - var f = function(e) { - var _drop = that.getDrop(); - - dx = oX - e.pageX; - dy = oY - e.pageY; - - if (Math.abs(dx) > 20 || Math.abs(dy) > 20) { - if (!hasMove) { - setCursor(options.cursor); - $el.addClass("move"); - that.trigger("drag:start"); - } - hasMove = true; - } else { - return; - } - - ex = poX - dx; - ey = poY - dy; - - if (_drop && _drop.options.constrain) { - cw = _drop.$el.width(); - ch = _drop.$el.height(); - cx = _drop.$el.offset().left; - cy = _drop.$el.offset().top; - - if (Math.abs(ey - cy) < 50) ey = cy; - if (Math.abs((ey + eh) - (cy+ch)) < 50) ey = cy + ch - eh; - if (Math.abs(ex - cx) < 50) ex = cx; - if (Math.abs((ex + ew) - (cx+cw)) < 50) ex = cx + cw - ew; - } - - $el.css({ - 'left': ex, - 'top': ey - }); - }; - - $document.on(events["move"], f); - $document.one(events["stop"], function(e) { - $document.unbind(events["move"], f); - resetCursor(); - - var _drop = that.getDrop(); - - if (hasMove && (!options.baseDropArea || !_drop || (options.baseDropArea.cid != _drop.cid))) { - if (_drop) { - _drop.trigger("drop", that.data); - } - that.trigger("drop", _drop, that.data); - } - - that.trigger("drag:end"); - - that.data = null; - that.drop = []; - - $el.removeClass("move"); - $el.css({ - 'left': "auto", - 'top': "auto" - }); - }); - }); - } - }); - - return { - events: events, - cursor: { - set: setCursor, - reset: resetCursor - }, - DropArea: DropArea, - DraggableType: DraggableType - }; -}); \ No newline at end of file diff --git a/editor/utils/string.js b/editor/utils/string.js index bde74a24..363fe3fe 100644 --- a/editor/utils/string.js +++ b/editor/utils/string.js @@ -24,7 +24,7 @@ define([ startAt = 0, fuzzies = 1, fuzzyFactor; - + // Cache fuzzyFactor for speed increase if (fuzziness) fuzzyFactor = 1 - fuzziness; @@ -35,7 +35,7 @@ define([ // Find next first case-insensitive match of a character. idxOf = lString.indexOf(lWord[i], startAt); - + if (-1 === idxOf) { fuzzies += fuzzyFactor; continue; @@ -50,19 +50,19 @@ define([ // preceded it with two perfect character matches. if (string[idxOf - 1] === ' ') charScore += 0.8; } - + // Same case bonus. - if (string[idxOf] === word[i]) charScore += 0.1; - + if (string[idxOf] === word[i]) charScore += 0.1; + // Update scores and startAt position for next round of indexOf runningScore += charScore; startAt = idxOf + 1; } } else { for (var i = 0; i < wordLength; ++i) { - + idxOf = lString.indexOf(lWord[i], startAt); - + if (-1 === idxOf) { return 0; } else if (startAt === idxOf) { @@ -72,8 +72,8 @@ define([ if (string[idxOf - 1] === ' ') charScore += 0.8; } - if (string[idxOf] === word[i]) charScore += 0.1; - + if (string[idxOf] === word[i]) charScore += 0.1; + runningScore += charScore; startAt = idxOf + 1; } @@ -81,11 +81,11 @@ define([ // Reduce penalty for longer strings. finalScore = 0.5 * (runningScore / strLength + runningScore / wordLength) / fuzzies; - + if ((lWord[0] === lString[0]) && (finalScore < 0.85)) { finalScore += 0.15; } - + return finalScore; }; diff --git a/editor/views/grid.js b/editor/views/grid.js deleted file mode 100644 index ea388d00..00000000 --- a/editor/views/grid.js +++ /dev/null @@ -1,299 +0,0 @@ -define([ - "hr/utils", - "hr/dom", - "hr/hr", - "utils/dragdrop" -], function(_, $, hr, dnd) { - var GridView = hr.View.extend({ - className: "component-grid", - defaults: { - columns: 0 // 0 means auto - }, - events: { - - }, - - initialize: function() { - GridView.__super__.initialize.apply(this, arguments); - - this.columns = this.options.columns; - this.views = []; - }, - - /* - * Add a view - */ - addView: function(view, options) { - view._grid = this; - view._gridOptions = _.defaults(options || {}, { - width: null, - at: null - }); - - if (view._gridOptions.at !== null) { - this.views.splice(view._gridOptions.at, 0, view); - } else { - this.views.push(view); - } - this.update(); - - return view; - }, - - /* - * Remove a view - */ - removeView: function(view) { - if (!_.isString(view)) view = view.cid; - - this.views = _.filter(this.views, function(_v) { - return _v.cid != view; - }); - this.update(); - }, - - /* - * Change layout by defining - */ - setLayout: function(n) { - this.columns = n; - this.update(); - }, - - /* - * Return current layout - */ - getLayout: function() { - var layout = this.columns || Math.floor(Math.sqrt(this.views.length)); - - var nColumns = Math.min(layout, this.views.length); - var nLines = Math.ceil(this.views.length/layout); - - return { - 'columns': nColumns, - 'lines': nLines - }; - }, - - /* - * Signal an update on tha layout to all views - */ - signalLayout: function() { - _.each(this.views, function(view) { - view.trigger("grid:layout"); - }); - }, - - /* - * Re-render the complete layout - */ - render: function() { - var x, y, lineW; - - // Detach view - _.each(this.views, function(view) { - view.detach(); - }); - - // Clear the view - this.$el.empty(); - - // Calcul layout - var layout = this.getLayout(); - - var sectionWidth = (100/layout.columns).toFixed(3); - var sectionHeight = (100/layout.lines).toFixed(3); - - // Add grid content - x = 0; y = 0; lineW = 100; - - _.each(this.views, function(view, i) { - var $section, $content, w, dw; - - // Calcul width for this section using optional width - dw = (lineW/(layout.columns - x)); - w = view._gridOptions.width || dw - - w = w.toFixed(4); - - // Container object - $section = $("
", { - 'class': 'grid-section', - 'css': { - 'left': (100 - lineW)+"%", - 'top': (y * sectionHeight)+"%", - 'width': w+"%", - 'height': sectionHeight+"%" - } - }); - $section.appendTo(this.$el); - - lineW = lineW - w; - - // Content - $content = $("
", { - 'class': 'grid-section-content' - }); - $content.append(view.$el); - $content.appendTo($section); - view.trigger("grid:layout"); - - // Resize bar - if (x < (layout.columns - 1)) { - // Horizontal - var hBar = $("
", { - 'class': "grid-resize-bar-h", - 'mousedown': this.resizerHandler(x, y, "h") - }); - hBar.appendTo($section); - $content.addClass("with-bar-h"); - } - - if (y < (layout.lines - 1)) { - // Vertical - var vBar = $("
", { - 'class': "grid-resize-bar-v", - 'mousedown': this.resizerHandler(x, y, "v") - }); - vBar.appendTo($section); - $content.addClass("with-bar-v"); - } - - // Calcul next position - x = x + 1; - if (x >= layout.columns) { - x = 0; - y = y + 1; - lineW = 100; - } - }, this); - - return this.ready(); - }, - - // Create a resizer handler - resizerHandler: function(x, y, type) { - var that = this; - var $document = $(document); - var oX, oY, dX, dY; - return function(e) { - e.preventDefault(); - oX = e.pageX; - oY = e.pageY; - - dnd.cursor.set(type == "h" ? "col-resize" : "row-resize"); - - var f = function(e) { - dx = oX - e.pageX; - dy = oY - e.pageY; - - if (type == "h") { - that.resizeColumn(x, -dx); - } else { - that.resizeLine(y, -dy); - } - - oX = e.pageX; - oY = e.pageY; - }; - - $document.mousemove(f); - $document.mouseup(function(e) { - $document.unbind('mousemove', f); - dnd.cursor.reset(); - }); - }; - }, - - getSection: function(sx, sy) { - var x, y, layout = this.getLayout(), that = this; - - x = 0; y = 0; - return this.$("> .grid-section").filter(function() { - var r = false; - - if ((sx !== null && sx == x) - || (sy !== null && sy == y)) { - r = true; - } - - // Calcul next position - x = x + 1; - if (x >= layout.columns) { - x = 0; - y = y + 1; - } - - return r; - }); - }, - - _resize: function(type, i, d) { - var getSection = _.bind(_.partialRight(this.getSection, null), this); - var pixelToPercent = _.bind(_.partialRight(this.pixelToPercent, null), this); - var position = "left"; - var size = "width"; - - if (type == "h") { - getSection = _.bind(_.partial(this.getSection, null), this); - pixelToPercent = _.bind(_.partial(this.pixelToPercent, null), this); - position = "top"; - size = "height"; - } - - // Convert update to percent - d = pixelToPercent(d); - - var $sections = getSection(i); - var $sectionsAfter = getSection(i+1); - - // New size for next sections - // We use el.get(0).style and not el.css because el.css returns pixel and not the real value - var sAfterN = this.strToPercent($sectionsAfter.get(0).style[size])-d; - - // New size for current sections - var sCurrentN = this.strToPercent($sections.get(0).style[size])+d; - - // Limited size - if (sCurrentN < 10 || sAfterN < 10) return false; - - // Resize next line - $sectionsAfter.css(_.object( - [position, size], - [ - (this.strToPercent($sectionsAfter.get(0).style[position])+d).toFixed(2)+"%", - sAfterN.toFixed(2)+"%" - ] - )); - - // Resize current line - $sections.css(_.object( - [size], - [sCurrentN.toFixed(2)+"%"] - )); - - this.signalLayout(); - - return true; - }, - - resizeLine: function(i, d) { - return this._resize("h", i, d); - }, - - resizeColumn: function(i, d) { - return this._resize("w", i, d); - }, - - pixelToPercent: function(x, y) { - if (x !== null) return ((x*100) / this.$el.width()); - if (y !== null) return ((y*100) / this.$el.height()); - }, - - strToPercent: function(size) { - return parseFloat(size.replace("%", "")) - } - }); - - return GridView; -}); \ No newline at end of file diff --git a/package.json b/package.json index 70128dfa..378a6b95 100644 --- a/package.json +++ b/package.json @@ -66,10 +66,15 @@ "hr.view": "*", "hr.collection": "*", "hr.class": "*", + "hr.dnd": "*", + "hr.gridview": "*", + "hr.logger": "*", + "hr.backend": "*", "octicons": "2.2.0", "mousetrap": "1.4.6", "moment": "2.9.0", - "sockjs-client": "0.1.3" + "sockjs-client": "0.1.3", + "axios": "0.5.2" }, "packageDependencies": { "about": "CodeboxIDE/package-about", From 8eae62ab99186c0012fe47db44893370c98b312e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 7 Apr 2015 16:45:13 +0200 Subject: [PATCH 005/101] Adapt most of the modules --- editor/collections/commands.js | 2 +- editor/collections/packages.js | 2 +- editor/collections/users.js | 2 +- editor/core/rpc.js | 3 + editor/core/socket.js | 2 +- editor/core/users.js | 1 + editor/main.js | 1 - editor/models/user.js | 2 +- editor/settings/keybindings.js | 95 +++---- editor/utils/date.js | 18 -- editor/utils/dialogs.js | 264 +++++++++-------- editor/utils/hash.js | 456 +++++++++++++++--------------- editor/utils/keyboard.js | 197 +++++++------ editor/utils/menu.js | 236 ++++++++-------- editor/utils/string.js | 174 ++++++------ editor/utils/taphold.js | 166 ++++++----- editor/utils/upload.js | 401 +++++++++++++------------- editor/views/dialogs/container.js | 172 ++++++----- editor/views/dialogs/input.js | 110 +++---- editor/views/dialogs/list.js | 385 ++++++++++++------------- editor/views/form.js | 322 +++++++++++---------- editor/views/menu.js | 135 +++++---- gulpfile.js | 2 +- package.json | 6 +- 24 files changed, 1556 insertions(+), 1598 deletions(-) delete mode 100644 editor/utils/date.js diff --git a/editor/collections/commands.js b/editor/collections/commands.js index d2ab2062..0ed19e40 100644 --- a/editor/collections/commands.js +++ b/editor/collections/commands.js @@ -5,7 +5,7 @@ var logger = require("hr.logger")("commands"); var Command = require("../models/command"); -var Commands = hr.Collection.extend({ +var Commands = Collection.extend({ model: Command, // Initialize diff --git a/editor/collections/packages.js b/editor/collections/packages.js index 215f4371..a356dfd6 100644 --- a/editor/collections/packages.js +++ b/editor/collections/packages.js @@ -7,7 +7,7 @@ var Package = require("../models/package"); var rpc = require("../core/rpc"); -var Packages = hr.Collection.extend({ +var Packages = Collection.extend({ model: Package, // Get packages list from backend diff --git a/editor/collections/users.js b/editor/collections/users.js index 62ba62ea..8d7a60f0 100644 --- a/editor/collections/users.js +++ b/editor/collections/users.js @@ -6,7 +6,7 @@ var logger = require("hr.logger")("users"); var User = require("../models/user"); var rpc = require("../core/rpc"); -var Users = hr.Collection.extend({ +var Users = Collection.extend({ model: User, listAll: function() { diff --git a/editor/core/rpc.js b/editor/core/rpc.js index 9928e63f..ccbc9780 100644 --- a/editor/core/rpc.js +++ b/editor/core/rpc.js @@ -8,6 +8,9 @@ var rpc = new Backend({ rpc.defaultMethod({ execute: function(args, options, method) { + console.log("request", method, args, options); + + return Q(axios.post("rpc/"+method, args)) .then(function(data) { return data.result; diff --git a/editor/core/socket.js b/editor/core/socket.js index 00a5c3e4..356e44b6 100644 --- a/editor/core/socket.js +++ b/editor/core/socket.js @@ -1,5 +1,5 @@ var Class = require("hr.class"); -var sockjs = require("sockjs-client"); +var SockJS = require("sockjs-client"); var logger = require("hr.logger")("socket"); var Socket = Class.extend({ diff --git a/editor/core/users.js b/editor/core/users.js index 76f4a6d2..6f8f8676 100644 --- a/editor/core/users.js +++ b/editor/core/users.js @@ -1,4 +1,5 @@ var Users = require("../collections/users"); +var events = require("./events"); var users = new Users(); diff --git a/editor/main.js b/editor/main.js index f1007ad6..8a163bf9 100644 --- a/editor/main.js +++ b/editor/main.js @@ -12,7 +12,6 @@ var dialogs = require("./utils/dialogs"); var menu = require("./utils/menu"); var File = require("./models/file"); -var date = require("./utils/date"); var keybindings = require("./settings/keybindings"); var upload = require("./utils/upload"); diff --git a/editor/models/user.js b/editor/models/user.js index bf4febc3..253be2fb 100644 --- a/editor/models/user.js +++ b/editor/models/user.js @@ -5,7 +5,7 @@ var logger = require("hr.logger")("users"); var rpc = require("../core/rpc"); -var User = hr.Model.extend({ +var User = Model.extend({ defaults: { id: null, name: null, diff --git a/editor/settings/keybindings.js b/editor/settings/keybindings.js index 14aa2b63..8aa3cde6 100644 --- a/editor/settings/keybindings.js +++ b/editor/settings/keybindings.js @@ -1,55 +1,54 @@ -define([ - "core/commands", - "core/settings" -], function(commands, settings) { - /* - * The key bindings configuration allow the user to - * change the default keyboard shortcuts for specific commands - */ - - var keyBindings = settings.schema("keybindings", { - title: "Key bindings", - type: "object", - properties: { - commands: { - type: "array", - items: { - "command": { - type: "string" - }, - "keys": { - type: "array" - } +var _ = require("hr.utils"); +var commands = require("../core/commands"); +var settings = require("../core/settings"); + +/* + * The key bindings configuration allow the user to + * change the default keyboard shortcuts for specific commands + */ + +var keyBindings = settings.schema("keybindings", { + title: "Key bindings", + type: "object", + properties: { + commands: { + type: "array", + items: { + "command": { + type: "string" + }, + "keys": { + type: "array" } } } - }); - - // Update a command - var updateCommand = function(cmd) { - var bindings = keyBindings.data.get("commands"); - var bind = _.find(bindings, { 'command': cmd.id }); - - if (!bind) { - if (cmd.get("originalShortcuts")) cmd.set("shortcuts", cmd.get("originalShortcuts")); - } else { - if (!cmd.get("originalShortcuts")) cmd.set("originalShortcuts", cmd.get("shortcuts")); - cmd.del("shortcuts", { silent: true }); - cmd.set("shortcuts", bind.keys); - } - }; + } +}); + +// Update a command +var updateCommand = function(cmd) { + var bindings = keyBindings.data.get("commands"); + var bind = _.find(bindings, { 'command': cmd.id }); + + if (!bind) { + if (cmd.get("originalShortcuts")) cmd.set("shortcuts", cmd.get("originalShortcuts")); + } else { + if (!cmd.get("originalShortcuts")) cmd.set("originalShortcuts", cmd.get("shortcuts")); + cmd.del("shortcuts", { silent: true }); + cmd.set("shortcuts", bind.keys); + } +}; - // Update all commands - var updateAll = function() { - commands.each(updateCommand); - }; +// Update all commands +var updateAll = function() { + commands.each(updateCommand); +}; - // Update commands everytime settings change and adapt new commands - keyBindings.data.on("change", updateAll); - commands.on("add", updateCommand); - commands.on("reset", updateAll); +// Update commands everytime settings change and adapt new commands +keyBindings.data.on("change", updateAll); +commands.on("add", updateCommand); +commands.on("reset", updateAll); - updateAll +updateAll(); - return keyBindings; -}); \ No newline at end of file +module.exports = keyBindings; diff --git a/editor/utils/date.js b/editor/utils/date.js deleted file mode 100644 index 6e7d80ad..00000000 --- a/editor/utils/date.js +++ /dev/null @@ -1,18 +0,0 @@ -define([ - 'hr/hr', - 'moment' -], function (hr, moment) { - var relativeDate = function(d) { - return moment(d).fromNow(); - }; - - var date = { - 'relative': relativeDate - }; - - hr.Template.extendContext({ - '$date': date - }); - - return date; -}); \ No newline at end of file diff --git a/editor/utils/dialogs.js b/editor/utils/dialogs.js index 3e017038..77c43f29 100644 --- a/editor/utils/dialogs.js +++ b/editor/utils/dialogs.js @@ -1,136 +1,134 @@ -define([ - "hr/utils", - "hr/promise", - "hr/hr", - "views/dialogs/container", - "views/dialogs/input", - "views/dialogs/list", - "text!resources/templates/dialogs/alert.html", - "text!resources/templates/dialogs/confirm.html", - "text!resources/templates/dialogs/prompt.html", - "text!resources/templates/dialogs/schema.html" -], function(_, Q, hr, Dialog, DialogInputView, DialogListView, -alertTemplate, confirmTemplate, promptTemplate, schemaTemplate) { - - // Open a dialog - var open = function(View, options) { - var d = Q.defer(); - - // Create the dialog - var diag = new Dialog(_.extend(options || {}, { - View: View - })); - - // Bind close - diag.on("close", function(force) { - if (force) return d.reject(new Error("Dialog was been closed")); - d.resolve(diag.view); +var _ = require("hr.utils"); +var Q = require("q"); +var Collection = require("hr.collection"); + +var Dialog = require("../views/dialogs/container"); +var DialogInputView = require("../views/dialogs/input"); +var DialogListView = require("../views/dialogs/list"); + +var alertTemplate = require("../resources/templates/dialogs/alert.html"); +var confirmTemplate = require("../resources/templates/dialogs/confirm.html"); +var promptTemplate = require("../resources/templates/dialogs/prompt.html"); +var schemaTemplate = require("../resources/templates/dialogs/schema.html"); + +// Open a dialog +var open = function(View, options) { + var d = Q.defer(); + + // Create the dialog + var diag = new Dialog(_.extend(options || {}, { + View: View + })); + + // Bind close + diag.on("close", function(force) { + if (force) return d.reject(new Error("Dialog was been closed")); + d.resolve(diag.view); + }); + + // Open it (add it to dom) + diag.render(); + + return d.promise; +}; + +// Input dialog +var openInput = function(viewOptions, options, View) { + return open(View || DialogInputView, _.extend(options || {}, { + view: viewOptions || {} + })) + .then(function(view) { + var value = view.getValue(); + + if (value == null) return Q.reject(new Error("Dialog return empty value")); + return value; + }); +}; + +// Alert +var openAlert = function(text, options) { + options = _.defaults(options || {}, { + isHtml: false + }); + return openInput({ + template: alertTemplate, + text: text, + isHtml: options.isHtml + }); +}; +var openErrorAlert = function(err) { + return openAlert("Error: "+(err.message || err)) + .fin(function() { + return Q.reject(err); + }); +}; + +// Confirm +var openConfirm = function(text, options) { + return openInput({ + template: confirmTemplate, + text: text + }); +}; + +// Prompt +var openPrompt = function(text, value, options) { + return openInput({ + template: promptTemplate, + text: text, + defaultValue: value, + value: function(d) { return d.$("input").val(); } + }); +}; + +// List +var openList = function(source, options) { + if (_.isArray(source)) { + source = new Collection({ + models: _.map(source, function(item) { + if (!_.isObject(item)) return { value: item }; + return item; + }) }); - - // Open it (add it to dom) - diag.render(); - - return d.promise; - }; - - // Input dialog - var openInput = function(viewOptions, options, View) { - return open(View || DialogInputView, _.extend(options || {}, { - view: viewOptions || {} - })) - .then(function(view) { - var value = view.getValue(); - - if (value == null) return Q.reject(new Error("Dialog return empty value")); - return value; - }); - }; - - // Alert - var openAlert = function(text, options) { - options = _.defaults(options || {}, { - isHtml: false - }); - return openInput({ - template: alertTemplate, - text: text, - isHtml: options.isHtml - }); - }; - var openErrorAlert = function(err) { - return openAlert("Error: "+(err.message || err)) - .fin(function() { - return Q.reject(err); - }); - }; - - // Confirm - var openConfirm = function(text, options) { - return openInput({ - template: confirmTemplate, - text: text - }); - }; - - // Prompt - var openPrompt = function(text, value, options) { - return openInput({ - template: promptTemplate, - text: text, - defaultValue: value, - value: function(d) { return d.$("input").val(); } - }); - }; - - // List - var openList = function(source, options) { - if (_.isArray(source)) { - source = new hr.Collection({ - models: _.map(source, function(item) { - if (!_.isObject(item)) return { value: item }; - return item; - }) + } + + return openInput( + _.extend({ + template: "
<%- item.get('value') %>
", + placeholder: "", + filter: function() { return true; } + }, options, { + source: source + }), {}, DialogListView); +}; + +// Schema +var openSchema = function(schema, values) { + values = values || {}; + + return openInput({ + template: schemaTemplate, + schema: schema, + defaultValues: values, + value: function(d) { + var nvalues = _.clone(values); + + _.each(schema.properties, function(property, key) { + var v = d.$("*[name='"+key+"']").val(); + nvalues[key] = v; }); - } - return openInput( - _.extend({ - template: "
<%- item.get('value') %>
", - placeholder: "", - filter: function() { return true; } - }, options, { - source: source - }), {}, DialogListView); - }; - - // Schema - var openSchema = function(schema, values) { - values = values || {}; - - return openInput({ - template: schemaTemplate, - schema: schema, - defaultValues: values, - value: function(d) { - var nvalues = _.clone(values); - - _.each(schema.properties, function(property, key) { - var v = d.$("*[name='"+key+"']").val(); - nvalues[key] = v; - }); - - return nvalues; - } - }); - }; - - return { - open: open, - alert: openAlert, - error: openErrorAlert, - confirm: openConfirm, - prompt: openPrompt, - list: openList, - schema: openSchema - }; -}); \ No newline at end of file + return nvalues; + } + }); +}; + +module.exports = { + open: open, + alert: openAlert, + error: openErrorAlert, + confirm: openConfirm, + prompt: openPrompt, + list: openList, + schema: openSchema +}; diff --git a/editor/utils/hash.js b/editor/utils/hash.js index bd7b21f5..833f94f5 100644 --- a/editor/utils/hash.js +++ b/editor/utils/hash.js @@ -1,261 +1,259 @@ -define(function () { - var crc32table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; - - var utf8Encode = function (string) { - string = string.replace(/\r\n/g,"\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) { - - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } +var crc32table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; - } - - return utftext; - }; - - /** - * Convert value as 8-bit unsigned integer to 2 digit hexadecimal number. - */ - var hex8 = function(val) - { - var n = val & 0xFF, - str = n.toString(16).toUpperCase() - ; - - while(str.length < 2) - str = "0" + str; - - return str; - }; - - /** - * Convert value as 16-bit unsigned integer to 4 digit hexadecimal number. - */ - var hex16 = function(val) - { - return hex8(val >> 8) + hex8(val); - }; - - /** - * Convert value as 32-bit unsigned integer to 8 digit hexadecimal number. - */ - var hex32 = function(val) - { - return hex16(val >> 16) + hex16(val); - }; - - var crc32= function(str) { - str = utf8Encode(str); - - var crc = 0; - var x = 0; - var y = 0; - - crc = crc ^ (-1); - for (var i = 0, iTop = str.length; i < iTop; i++) { - y = (crc ^ str.charCodeAt(i)) & 0xFF; - x = "0x" + crc32table.substr(y * 9, 8); - crc = (crc >>> 8) ^ x; - } +var utf8Encode = function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; - return (crc ^ (-1)).toString(); - }; - - /*\ - |*| - |*| Base64 / binary data / UTF-8 strings utilities - |*| - |*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding - |*| - \*/ - - /* Array of bytes to base64 string decoding */ - - function b64ToUint6 (nChr) { - - return nChr > 64 && nChr < 91 ? - nChr - 65 - : nChr > 96 && nChr < 123 ? - nChr - 71 - : nChr > 47 && nChr < 58 ? - nChr + 4 - : nChr === 43 ? - 62 - : nChr === 47 ? - 63 - : - 0; + for (var n = 0; n < string.length; n++) { - } + var c = string.charCodeAt(n); - function base64DecToArr (sBase64, nBlocksSize) { + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } - var - sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, - nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen); + } - for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { - nMod4 = nInIdx & 3; - nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; - if (nMod4 === 3 || nInLen - nInIdx === 1) { - for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { - taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; - } - nUint24 = 0; + return utftext; +}; + +/** + * Convert value as 8-bit unsigned integer to 2 digit hexadecimal number. + */ +var hex8 = function(val) +{ + var n = val & 0xFF, + str = n.toString(16).toUpperCase() + ; + + while(str.length < 2) + str = "0" + str; + + return str; +}; + +/** + * Convert value as 16-bit unsigned integer to 4 digit hexadecimal number. + */ +var hex16 = function(val) +{ + return hex8(val >> 8) + hex8(val); +}; + +/** + * Convert value as 32-bit unsigned integer to 8 digit hexadecimal number. + */ +var hex32 = function(val) +{ + return hex16(val >> 16) + hex16(val); +}; + +var crc32= function(str) { + str = utf8Encode(str); + + var crc = 0; + var x = 0; + var y = 0; + + crc = crc ^ (-1); + for (var i = 0, iTop = str.length; i < iTop; i++) { + y = (crc ^ str.charCodeAt(i)) & 0xFF; + x = "0x" + crc32table.substr(y * 9, 8); + crc = (crc >>> 8) ^ x; + } + return (crc ^ (-1)).toString(); +}; + +/*\ +|*| +|*| Base64 / binary data / UTF-8 strings utilities +|*| +|*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding +|*| +\*/ + +/* Array of bytes to base64 string decoding */ + +function b64ToUint6 (nChr) { + + return nChr > 64 && nChr < 91 ? + nChr - 65 + : nChr > 96 && nChr < 123 ? + nChr - 71 + : nChr > 47 && nChr < 58 ? + nChr + 4 + : nChr === 43 ? + 62 + : nChr === 47 ? + 63 + : + 0; + +} + +function base64DecToArr (sBase64, nBlocksSize) { + + var + sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, + nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen); + + for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { + nMod4 = nInIdx & 3; + nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; + if (nMod4 === 3 || nInLen - nInIdx === 1) { + for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { + taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; } - } + nUint24 = 0; - return taBytes; + } } - /* Base64 string to array encoding */ + return taBytes; +} - function uint6ToB64 (nUint6) { +/* Base64 string to array encoding */ - return nUint6 < 26 ? - nUint6 + 65 - : nUint6 < 52 ? - nUint6 + 71 - : nUint6 < 62 ? - nUint6 - 4 - : nUint6 === 62 ? - 43 - : nUint6 === 63 ? - 47 - : - 65; +function uint6ToB64 (nUint6) { - } + return nUint6 < 26 ? + nUint6 + 65 + : nUint6 < 52 ? + nUint6 + 71 + : nUint6 < 62 ? + nUint6 - 4 + : nUint6 === 62 ? + 43 + : nUint6 === 63 ? + 47 + : + 65; - function base64EncArr (aBytes) { +} - var nMod3, sB64Enc = ""; +function base64EncArr (aBytes) { - for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { - nMod3 = nIdx % 3; - if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; } - nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); - if (nMod3 === 2 || aBytes.length - nIdx === 1) { - sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63)); - nUint24 = 0; - } - } + var nMod3, sB64Enc = ""; - return sB64Enc.replace(/A(?=A$|$)/g, "="); + for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { + nMod3 = nIdx % 3; + if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; } + nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); + if (nMod3 === 2 || aBytes.length - nIdx === 1) { + sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63)); + nUint24 = 0; + } + } + return sB64Enc.replace(/A(?=A$|$)/g, "="); + +} + +/* UTF-8 array to DOMString and vice versa */ + +function UTF8ArrToStr (aBytes) { + + var sView = ""; + + for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) { + nPart = aBytes[nIdx]; + sView += String.fromCharCode( + nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */ + /* (nPart - 252 << 32) is not possible in ECMAScript! So...: */ + (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 + : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */ + (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 + : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */ + (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 + : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */ + (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 + : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */ + (nPart - 192 << 6) + aBytes[++nIdx] - 128 + : /* nPart < 127 ? */ /* one byte */ + nPart + ); } - /* UTF-8 array to DOMString and vice versa */ - - function UTF8ArrToStr (aBytes) { - - var sView = ""; - - for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) { - nPart = aBytes[nIdx]; - sView += String.fromCharCode( - nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */ - /* (nPart - 252 << 32) is not possible in ECMAScript! So...: */ - (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 - : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */ - (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 - : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */ - (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 - : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */ - (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128 - : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */ - (nPart - 192 << 6) + aBytes[++nIdx] - 128 - : /* nPart < 127 ? */ /* one byte */ - nPart - ); - } + return sView; - return sView; +} - } +function strToUTF8Arr (sDOMStr) { - function strToUTF8Arr (sDOMStr) { + var aBytes, nChr, nStrLen = sDOMStr.length, nArrLen = 0; - var aBytes, nChr, nStrLen = sDOMStr.length, nArrLen = 0; + /* mapping... */ - /* mapping... */ + for (var nMapIdx = 0; nMapIdx < nStrLen; nMapIdx++) { + nChr = sDOMStr.charCodeAt(nMapIdx); + nArrLen += nChr < 0x80 ? 1 : nChr < 0x800 ? 2 : nChr < 0x10000 ? 3 : nChr < 0x200000 ? 4 : nChr < 0x4000000 ? 5 : 6; + } - for (var nMapIdx = 0; nMapIdx < nStrLen; nMapIdx++) { - nChr = sDOMStr.charCodeAt(nMapIdx); - nArrLen += nChr < 0x80 ? 1 : nChr < 0x800 ? 2 : nChr < 0x10000 ? 3 : nChr < 0x200000 ? 4 : nChr < 0x4000000 ? 5 : 6; + aBytes = new Uint8Array(nArrLen); + + /* transcription... */ + + for (var nIdx = 0, nChrIdx = 0; nIdx < nArrLen; nChrIdx++) { + nChr = sDOMStr.charCodeAt(nChrIdx); + if (nChr < 128) { + /* one byte */ + aBytes[nIdx++] = nChr; + } else if (nChr < 0x800) { + /* two bytes */ + aBytes[nIdx++] = 192 + (nChr >>> 6); + aBytes[nIdx++] = 128 + (nChr & 63); + } else if (nChr < 0x10000) { + /* three bytes */ + aBytes[nIdx++] = 224 + (nChr >>> 12); + aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); + aBytes[nIdx++] = 128 + (nChr & 63); + } else if (nChr < 0x200000) { + /* four bytes */ + aBytes[nIdx++] = 240 + (nChr >>> 18); + aBytes[nIdx++] = 128 + (nChr >>> 12 & 63); + aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); + aBytes[nIdx++] = 128 + (nChr & 63); + } else if (nChr < 0x4000000) { + /* five bytes */ + aBytes[nIdx++] = 248 + (nChr >>> 24); + aBytes[nIdx++] = 128 + (nChr >>> 18 & 63); + aBytes[nIdx++] = 128 + (nChr >>> 12 & 63); + aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); + aBytes[nIdx++] = 128 + (nChr & 63); + } else /* if (nChr <= 0x7fffffff) */ { + /* six bytes */ + aBytes[nIdx++] = 252 + /* (nChr >>> 32) is not possible in ECMAScript! So...: */ (nChr / 1073741824); + aBytes[nIdx++] = 128 + (nChr >>> 24 & 63); + aBytes[nIdx++] = 128 + (nChr >>> 18 & 63); + aBytes[nIdx++] = 128 + (nChr >>> 12 & 63); + aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); + aBytes[nIdx++] = 128 + (nChr & 63); } + } - aBytes = new Uint8Array(nArrLen); - - /* transcription... */ - - for (var nIdx = 0, nChrIdx = 0; nIdx < nArrLen; nChrIdx++) { - nChr = sDOMStr.charCodeAt(nChrIdx); - if (nChr < 128) { - /* one byte */ - aBytes[nIdx++] = nChr; - } else if (nChr < 0x800) { - /* two bytes */ - aBytes[nIdx++] = 192 + (nChr >>> 6); - aBytes[nIdx++] = 128 + (nChr & 63); - } else if (nChr < 0x10000) { - /* three bytes */ - aBytes[nIdx++] = 224 + (nChr >>> 12); - aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); - aBytes[nIdx++] = 128 + (nChr & 63); - } else if (nChr < 0x200000) { - /* four bytes */ - aBytes[nIdx++] = 240 + (nChr >>> 18); - aBytes[nIdx++] = 128 + (nChr >>> 12 & 63); - aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); - aBytes[nIdx++] = 128 + (nChr & 63); - } else if (nChr < 0x4000000) { - /* five bytes */ - aBytes[nIdx++] = 248 + (nChr >>> 24); - aBytes[nIdx++] = 128 + (nChr >>> 18 & 63); - aBytes[nIdx++] = 128 + (nChr >>> 12 & 63); - aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); - aBytes[nIdx++] = 128 + (nChr & 63); - } else /* if (nChr <= 0x7fffffff) */ { - /* six bytes */ - aBytes[nIdx++] = 252 + /* (nChr >>> 32) is not possible in ECMAScript! So...: */ (nChr / 1073741824); - aBytes[nIdx++] = 128 + (nChr >>> 24 & 63); - aBytes[nIdx++] = 128 + (nChr >>> 18 & 63); - aBytes[nIdx++] = 128 + (nChr >>> 12 & 63); - aBytes[nIdx++] = 128 + (nChr >>> 6 & 63); - aBytes[nIdx++] = 128 + (nChr & 63); - } - } + return aBytes; - return aBytes; +} +module.exports = { + 'crc32': crc32, + 'hex8': hex8, + 'hex16': hex16, + 'hex32': hex32, + 'atob': function(s) { + return UTF8ArrToStr(base64DecToArr(s)); + }, + 'btoa': function(s) { + return base64EncArr(strToUTF8Arr(s)); } - - return { - 'crc32': crc32, - 'hex8': hex8, - 'hex16': hex16, - 'hex32': hex32, - 'atob': function(s) { - return UTF8ArrToStr(base64DecToArr(s)); - }, - 'btoa': function(s) { - return base64EncArr(strToUTF8Arr(s)); - } - }; -}); +}; diff --git a/editor/utils/keyboard.js b/editor/utils/keyboard.js index 3f9ca0fd..f341390e 100644 --- a/editor/utils/keyboard.js +++ b/editor/utils/keyboard.js @@ -1,116 +1,115 @@ -define([ - 'hr/hr', - 'hr/utils', - 'vendors/mousetrap/mousetrap' -], function (hr, _, Mousetrap) { - var originalStopCallback = Mousetrap.stopCallback; - Mousetrap.stopCallback = function(e, element) { - if (e.mousetrap) { - return false; - } - return originalStopCallback(e, element); - }; - - var Keyboard = hr.Class.extend({ - initialize: function() { - this.bindings = {}; - return this; - }, - - /* - * Enable keyboard shortcut for a specific event - */ - enableKeyEvent: function(e) { - e.mousetrap = true; - }, - - /* - * Bind keyboard shortcuts to callback - */ - bind: function(keys, callback, context) { - if (_.isArray(keys)) { - _.each(keys, function(key) { this.bind(key, callback, context) }, this); - return; - } - - // Map shortcut -> action - if (_.isObject(keys)) { - _.each(keys, function(method, key) { - this.bind(key, method, callback); - }, this) - return; - } - - // Bind - if (this.bindings[keys] == null) { - this.bindings[keys] = new hr.Class(); - Mousetrap.bind(keys, _.bind(function(e) { - this.bindings[keys].trigger("action", e); - }, this)); - } - context.listenTo(this.bindings[keys], "action", callback); +var Class = require("hr.class"); +var _ = require("hr.utils"); + +var Mousetrap = require("mousetrap"); + +var originalStopCallback = Mousetrap.stopCallback; +Mousetrap.stopCallback = function(e, element) { + if (e.mousetrap) { + return false; + } + return originalStopCallback(e, element); +}; + +var Keyboard = Class.extend({ + initialize: function() { + this.bindings = {}; + return this; + }, + + /* + * Enable keyboard shortcut for a specific event + */ + enableKeyEvent: function(e) { + e.mousetrap = true; + }, + + /* + * Bind keyboard shortcuts to callback + */ + bind: function(keys, callback, context) { + if (_.isArray(keys)) { + _.each(keys, function(key) { this.bind(key, callback, context) }, this); return; - }, - - /* - * Unbind keyboard shortcuts - */ - unbind: function(keys, context, callback) { - if (_.isArray(keys)) { - _.each(keys, function(key) { this.unbind(key, context, callback) }, this); - return; - } + } - if (!this.bindings[keys]) return; + // Map shortcut -> action + if (_.isObject(keys)) { + _.each(keys, function(method, key) { + this.bind(key, method, callback); + }, this) + return; + } - context.stopListening(this.bindings[keys], "action", callback); + // Bind + if (this.bindings[keys] == null) { + this.bindings[keys] = new Class(); + Mousetrap.bind(keys, _.bind(function(e) { + this.bindings[keys].trigger("action", e); + }, this)); + } + context.listenTo(this.bindings[keys], "action", callback); + return; + }, + + /* + * Unbind keyboard shortcuts + */ + unbind: function(keys, context, callback) { + if (_.isArray(keys)) { + _.each(keys, function(key) { this.unbind(key, context, callback) }, this); return; - }, + } - /* - * Prevent default browser shortcut - */ - preventDefault: function(keys) { - return this.bind(keys, function(e) { - e.preventDefault(); - }, this); - }, + if (!this.bindings[keys]) return; - /* - * Convert shortcut or list of shortcut to a string - */ - toText: function(shortcut) { - if (_.isArray(shortcut)) shortcut = _.first(shortcut); - if (!shortcut) return null; + context.stopListening(this.bindings[keys], "action", callback); + return; + }, - var isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform); + /* + * Prevent default browser shortcut + */ + preventDefault: function(keys) { + return this.bind(keys, function(e) { + e.preventDefault(); + }, this); + }, - // Replace mod by equivalent for mac or windows - shortcut = shortcut.replace("mod", isMac ? '⌘' : 'ctrl'); + /* + * Convert shortcut or list of shortcut to a string + */ + toText: function(shortcut) { + if (_.isArray(shortcut)) shortcut = _.first(shortcut); + if (!shortcut) return null; - // Replace ctrl - shortcut = shortcut.replace("ctrl", "⌃"); + var isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform); - // Replace shift - shortcut = shortcut.replace("shift", "⇧"); + // Replace mod by equivalent for mac or windows + shortcut = shortcut.replace("mod", isMac ? '⌘' : 'ctrl'); - if (isMac) { - shortcut = shortcut.replace("alt", "⌥"); - } else { - shortcut = shortcut.replace("alt", "⎇"); - } + // Replace ctrl + shortcut = shortcut.replace("ctrl", "⌃"); - // Replace + - shortcut = shortcut.replace(/\+/g, " "); + // Replace shift + shortcut = shortcut.replace("shift", "⇧"); - return shortcut.toUpperCase(); + if (isMac) { + shortcut = shortcut.replace("alt", "⌥"); + } else { + shortcut = shortcut.replace("alt", "⎇"); } - }); - var keyboard = new Keyboard(); + // Replace + + shortcut = shortcut.replace(/\+/g, " "); + + return shortcut.toUpperCase(); + } +}); + +var keyboard = new Keyboard(); - // Prevent some browser default keyboard interactions - keyboard.preventDefault("mod+r"); +// Prevent some browser default keyboard interactions +keyboard.preventDefault("mod+r"); - return keyboard; -}); \ No newline at end of file +module.exports = keyboard; diff --git a/editor/utils/menu.js b/editor/utils/menu.js index 57f66253..9bf6400a 100644 --- a/editor/utils/menu.js +++ b/editor/utils/menu.js @@ -1,141 +1,133 @@ -define([ - 'hr/dom', - 'hr/utils', - 'views/menu', - 'utils/taphold' -], function ($, _, MenuView, taphold) { +var $ = require("jquery"); +var _ = require("hr.utils"); +var MenuView = require("../views/menu"); +var taphold = require("./taphold"); + +var menu = { + lastTimeOpened: 0, + origin: null, + + /** + * Clear current context menu + */ + clear: function() { + $(".ui-context-menu").removeClass("ui-context-menu"); + $("#ui-context-menu").remove(); + }, /** - * Context menu manager + * Generate menu from menuItems or a generator function * - * @class + * @param {array} menuItems */ - var Menu = { - lastTimeOpened: 0, - origin: null, - - /** - * Clear current context menu - */ - clear: function() { - $(".ui-context-menu").removeClass("ui-context-menu"); - $("#ui-context-menu").remove(); - }, - - /** - * Generate menu from menuItems or a generator function - * - * @param {array} menuItems - */ - generateMenu: function(menuItems) { - if (_.isFunction(menuItems)) menuItems = menuItems(); - - var menu = new MenuView({ - items: menuItems - }); - menu.$el.appendTo($("body")); - menu.on("action", function() { - Menu.clear(); - }) - menu.render(); - return menu; - }, - - /** - * Create a new context menu - * - * @param {array} menuItems - * @param {object} pos position for the menu - */ - open: function(menuItems, pos) { - Menu.clear(); - Menu.lastTimeOpened = Date.now(); - - var menu = Menu.generateMenu(menuItems); - - var w = menu.$el.width(); - var h = menu.$el.height(); - - var windowW = $(window).width(); - var windowH = $(window).height(); - - if ((pos.left+w) > windowW) { - pos.left = pos.left - w; - menu.$el.addClass("submenus-right"); - } - if ((pos.top+h) > windowH) { - pos.top = pos.top - h; - menu.$el.addClass("submenus-top"); - } + generateView: function(menuItems) { + if (_.isFunction(menuItems)) menuItems = menuItems(); + + var menuView = new MenuView({ + items: menuItems + }); + menuView.$el.appendTo($("body")); + menuView.on("action", function() { + menu.clear(); + }) + menuView.render(); + return menuView; + }, - menu.$el.css(_.extend({ - 'position': "fixed", - 'z-index': 100 - }, pos)); - menu.$el.attr("id", "ui-context-menu"); - }, - - /** - * Add a context menu to an element - * the menu can be open by left click and tap hold on ipad - * - * @param {jqueryElement} el - * @param {array} menu menu items - * @param {object} options - */ - add: function(el, menu, options) { - var $el = $(el); - - options = _.defaults({}, options, { - // Menu accessible in textinput - 'textinput': false - }); + /** + * Create a new context menu + * + * @param {array} menuItems + * @param {object} pos position for the menu + */ + open: function(menuItems, pos) { + menu.clear(); + menu.lastTimeOpened = Date.now(); - var handler = function(e) { - Menu.origin = e.type; - var target = e.target || e.srcElement; + var menuView = menu.generateView(menuItems); - // Ignore Menu on textinput - if (!options.textinput && - (target.tagName == 'INPUT' || target.tagName == 'SELECT' || target.tagName == 'TEXTAREA' || target.isContentEditable)) { - return; - } + var w = menuView.$el.width(); + var h = menuView.$el.height(); - var x = e.pageX || e.originalEvent.touches[0].pageX; - var y = e.pageY || e.originalEvent.touches[0].pageY; + var windowW = $(window).width(); + var windowH = $(window).height(); - Menu.open(menu, { - 'left': x, - 'top': y - }); + if ((pos.left+w) > windowW) { + pos.left = pos.left - w; + menuView.$el.addClass("submenus-right"); + } + if ((pos.top+h) > windowH) { + pos.top = pos.top - h; + menuView.$el.addClass("submenus-top"); + } + + menuView.$el.css(_.extend({ + 'position': "fixed", + 'z-index': 100 + }, pos)); + menuView.$el.attr("id", "ui-context-menu"); + }, - $el.addClass("ui-context-menu"); - return false; + /** + * Add a context menu to an element + * the menu can be open by left click and tap hold on ipad + * + * @param {jqueryElement} el + * @param {array} menu menu items + * @param {object} options + */ + add: function(el, menuView, options) { + var $el = $(el); + + options = _.defaults({}, options, { + // Menu accessible in textinput + 'textinput': false + }); + + var handler = function(e) { + menu.origin = e.type; + var target = e.target || e.srcElement; + + // Ignore Menu on textinput + if (!options.textinput && + (target.tagName == 'INPUT' || target.tagName == 'SELECT' || target.tagName == 'TEXTAREA' || target.isContentEditable)) { + return; } - $el.on("contextmenu", handler); - if (navigator.userAgent.match(/iPad/i) != null) taphold.bind($el, handler); - }, + var x = e.pageX || e.originalEvent.touches[0].pageX; + var y = e.pageY || e.originalEvent.touches[0].pageY; - remove: function(el) { - var $el = $(el); + menu.open(menuView, { + 'left': x, + 'top': y + }); - $el.off("contextmenu"); - taphold.unbind($el); + $el.addClass("ui-context-menu"); + return false; } - }; - // Click on the page: clse context menu - $(document).on("click", function (e) { - if (Menu.lastTimeOpened > (Date.now() - 600) && Menu.origin != "contextmenu") return; - Menu.clear(); - }); + $el.on("contextmenu", handler); + if (navigator.userAgent.match(/iPad/i) != null) taphold.bind($el, handler); + }, + + remove: function(el) { + var $el = $(el); + + $el.off("contextmenu"); + taphold.unbind($el); + } +}; + +// Click on the page: clse context menu +$(document).on("click", function (e) { + if (menu.lastTimeOpened > (Date.now() - 600) && menu.origin != "contextmenu") return; + menu.clear(); +}); - // Open new Menu: close other context menu - $(document).on("contextmenu", function() { - Menu.clear(); - }); +// Open new Menu: close other context menu +$(document).on("contextmenu", function() { + menu.clear(); +}); - return Menu; -}); \ No newline at end of file +module.exports = menu; diff --git a/editor/utils/string.js b/editor/utils/string.js index 363fe3fe..067fbdd9 100644 --- a/editor/utils/string.js +++ b/editor/utils/string.js @@ -1,100 +1,96 @@ -define([ - 'hr/hr' -], function (hr) { - /** - * Scores a string against another string. - * score('Hello World', 'he'); //=> 0.5931818181818181 - * score('Hello World', 'Hello'); //=> 0.7318181818181818 - */ - var score = function(string, word, fuzziness) { - // If the string is equal to the word, perfect match. - if (string == word) return 1; - - //if it's not a perfect match and is empty return 0 - if( word == "") return 0; - - var runningScore = 0, - charScore, - finalScore, - lString = string.toLowerCase(), - strLength = string.length, - lWord = word.toLowerCase(), - wordLength = word.length, - idxOf, - startAt = 0, - fuzzies = 1, - fuzzyFactor; - - // Cache fuzzyFactor for speed increase - if (fuzziness) fuzzyFactor = 1 - fuzziness; - - // Walk through word and add up scores. - // Code duplication occurs to prevent checking fuzziness inside for loop - if (fuzziness) { - for (var i = 0; i < wordLength; ++i) { - - // Find next first case-insensitive match of a character. - idxOf = lString.indexOf(lWord[i], startAt); - - if (-1 === idxOf) { - fuzzies += fuzzyFactor; - continue; - } else if (startAt === idxOf) { - // Consecutive letter & start-of-string Bonus - charScore = 0.7; - } else { - charScore = 0.1; - - // Acronym Bonus - // Weighing Logic: Typing the first character of an acronym is as if you - // preceded it with two perfect character matches. - if (string[idxOf - 1] === ' ') charScore += 0.8; - } - - // Same case bonus. - if (string[idxOf] === word[i]) charScore += 0.1; - - // Update scores and startAt position for next round of indexOf - runningScore += charScore; - startAt = idxOf + 1; +/** + * Scores a string against another string. + * score('Hello World', 'he'); //=> 0.5931818181818181 + * score('Hello World', 'Hello'); //=> 0.7318181818181818 + */ +var score = function(string, word, fuzziness) { + // If the string is equal to the word, perfect match. + if (string == word) return 1; + + //if it's not a perfect match and is empty return 0 + if( word == "") return 0; + + var runningScore = 0, + charScore, + finalScore, + lString = string.toLowerCase(), + strLength = string.length, + lWord = word.toLowerCase(), + wordLength = word.length, + idxOf, + startAt = 0, + fuzzies = 1, + fuzzyFactor; + + // Cache fuzzyFactor for speed increase + if (fuzziness) fuzzyFactor = 1 - fuzziness; + + // Walk through word and add up scores. + // Code duplication occurs to prevent checking fuzziness inside for loop + if (fuzziness) { + for (var i = 0; i < wordLength; ++i) { + + // Find next first case-insensitive match of a character. + idxOf = lString.indexOf(lWord[i], startAt); + + if (-1 === idxOf) { + fuzzies += fuzzyFactor; + continue; + } else if (startAt === idxOf) { + // Consecutive letter & start-of-string Bonus + charScore = 0.7; + } else { + charScore = 0.1; + + // Acronym Bonus + // Weighing Logic: Typing the first character of an acronym is as if you + // preceded it with two perfect character matches. + if (string[idxOf - 1] === ' ') charScore += 0.8; } - } else { - for (var i = 0; i < wordLength; ++i) { - idxOf = lString.indexOf(lWord[i], startAt); + // Same case bonus. + if (string[idxOf] === word[i]) charScore += 0.1; - if (-1 === idxOf) { - return 0; - } else if (startAt === idxOf) { - charScore = 0.7; - } else { - charScore = 0.1; - if (string[idxOf - 1] === ' ') charScore += 0.8; - } + // Update scores and startAt position for next round of indexOf + runningScore += charScore; + startAt = idxOf + 1; + } + } else { + for (var i = 0; i < wordLength; ++i) { + + idxOf = lString.indexOf(lWord[i], startAt); + + if (-1 === idxOf) { + return 0; + } else if (startAt === idxOf) { + charScore = 0.7; + } else { + charScore = 0.1; + if (string[idxOf - 1] === ' ') charScore += 0.8; + } - if (string[idxOf] === word[i]) charScore += 0.1; + if (string[idxOf] === word[i]) charScore += 0.1; - runningScore += charScore; - startAt = idxOf + 1; - } + runningScore += charScore; + startAt = idxOf + 1; } + } - // Reduce penalty for longer strings. - finalScore = 0.5 * (runningScore / strLength + runningScore / wordLength) / fuzzies; + // Reduce penalty for longer strings. + finalScore = 0.5 * (runningScore / strLength + runningScore / wordLength) / fuzzies; - if ((lWord[0] === lString[0]) && (finalScore < 0.85)) { - finalScore += 0.15; - } + if ((lWord[0] === lString[0]) && (finalScore < 0.85)) { + finalScore += 0.15; + } - return finalScore; - }; + return finalScore; +}; - var endsWith = function(s, suffix) { - return s.indexOf(suffix, s.length - suffix.length) !== -1; - }; +var endsWith = function(s, suffix) { + return s.indexOf(suffix, s.length - suffix.length) !== -1; +}; - return { - 'score': score, - 'endsWith': endsWith - }; -}); \ No newline at end of file +module.exports = { + 'score': score, + 'endsWith': endsWith +}; diff --git a/editor/utils/taphold.js b/editor/utils/taphold.js index 3fbf421f..f0e2f54e 100644 --- a/editor/utils/taphold.js +++ b/editor/utils/taphold.js @@ -1,98 +1,96 @@ -define([ - 'hr/dom' -], function ($) { - var $document = $(document); - - function triggerCustomEvent( obj, eventType, event, bubble ) { - var originalType = event.type; - event.type = eventType; - if ( bubble ) { - $.event.trigger( event, undefined, obj ); - } else { - $.event.dispatch.call( obj, event ); - } - event.type = originalType; +var $ = require("jquery"); + +var $document = $(document); + +function triggerCustomEvent( obj, eventType, event, bubble ) { + var originalType = event.type; + event.type = eventType; + if ( bubble ) { + $.event.trigger( event, undefined, obj ); + } else { + $.event.dispatch.call( obj, event ); } + event.type = originalType; +} - $.event.special.taphold = { - setup: function(data, namespaces){ - var thisObject = this, - $this = $( thisObject ); - var timeout = null; - var duration = 500; - var maxMove = 5; - var oX, oY; - - // mousemove or touchmove callback - function mousemove_callback(e) { - var x = e.pageX || e.originalEvent.touches[0].pageX; - var y = e.pageY || e.originalEvent.touches[0].pageY; - - if (Math.abs(oX - x) > maxMove || Math.abs(oY - y) > maxMove) { - if (timeout) clearTimeout(timeout); - } - } +$.event.special.taphold = { + setup: function(data, namespaces){ + var thisObject = this, + $this = $( thisObject ); + var timeout = null; + var duration = 500; + var maxMove = 5; + var oX, oY; - // mouseup or touchend callback - function mouseup_callback(e) { - unbindDoc(); - if (timeout) clearTimeout(timeout); - } + // mousemove or touchmove callback + function mousemove_callback(e) { + var x = e.pageX || e.originalEvent.touches[0].pageX; + var y = e.pageY || e.originalEvent.touches[0].pageY; - var bindDoc = function() { - $document.on('mousemove', mousemove_callback); - $document.on('touchmove', mousemove_callback); - $document.on('mouseup', mouseup_callback); - $document.on('touchend', mouseup_callback); + if (Math.abs(oX - x) > maxMove || Math.abs(oY - y) > maxMove) { + if (timeout) clearTimeout(timeout); } + } - var unbindDoc = function() { - $document.unbind('mousemove', mousemove_callback); - $document.unbind('touchmove', mousemove_callback); - $document.unbind('mouseup', mouseup_callback); - $document.unbind('touchend', mouseup_callback); - } + // mouseup or touchend callback + function mouseup_callback(e) { + unbindDoc(); + if (timeout) clearTimeout(timeout); + } - // mousedown or touchstart callback - function mousedown_callback(e) { - // Only accept left click - if (e.type == 'mousedown' && e.originalEvent.button != 0) return; - oX = e.pageX || e.originalEvent.touches[0].pageX; - oY = e.pageY || e.originalEvent.touches[0].pageY; + var bindDoc = function() { + $document.on('mousemove', mousemove_callback); + $document.on('touchmove', mousemove_callback); + $document.on('mouseup', mouseup_callback); + $document.on('touchend', mouseup_callback); + } - bindDoc(); + var unbindDoc = function() { + $document.unbind('mousemove', mousemove_callback); + $document.unbind('touchmove', mousemove_callback); + $document.unbind('mouseup', mouseup_callback); + $document.unbind('touchend', mouseup_callback); + } - // set a timeout to call the longpress callback when time elapses - timeout = setTimeout(function() { - unbindDoc(); + // mousedown or touchstart callback + function mousedown_callback(e) { + // Only accept left click + if (e.type == 'mousedown' && e.originalEvent.button != 0) return; + oX = e.pageX || e.originalEvent.touches[0].pageX; + oY = e.pageY || e.originalEvent.touches[0].pageY; - triggerCustomEvent(thisObject, "taphold", $.Event( "taphold", { - target: e.target, - pageX: oX, - pageY: oY - } )); - }, duration); + bindDoc(); - e.stopPropagation(); - } + // set a timeout to call the longpress callback when time elapses + timeout = setTimeout(function() { + unbindDoc(); - // Browser Support - $this.on('mousedown', mousedown_callback); + triggerCustomEvent(thisObject, "taphold", $.Event( "taphold", { + target: e.target, + pageX: oX, + pageY: oY + } )); + }, duration); - // Mobile Support - $this.on('touchstart', mousedown_callback); - }, - teardown: function(namespaces){ - $(this).unbind(namespaces) - } - }; - - return { - bind: function(el, fn ) { - return el.bind("taphold", fn ); - }, - unbind: function() { - return el.unbind("taphold"); + e.stopPropagation(); } - }; -}); \ No newline at end of file + + // Browser Support + $this.on('mousedown', mousedown_callback); + + // Mobile Support + $this.on('touchstart', mousedown_callback); + }, + teardown: function(namespaces){ + $(this).unbind(namespaces) + } +}; + +module.exports = { + bind: function(el, fn ) { + return el.bind("taphold", fn ); + }, + unbind: function() { + return el.unbind("taphold"); + } +}; diff --git a/editor/utils/upload.js b/editor/utils/upload.js index 59fc3ed3..6539abe3 100644 --- a/editor/utils/upload.js +++ b/editor/utils/upload.js @@ -1,226 +1,225 @@ -define([ - 'hr/hr', - 'hr/promise', - 'hr/dom', - 'hr/utils' -],function(hr, Q, $, _) { - var logging = hr.Logger.addNamespace("uploader"); - - try { - if (XMLHttpRequest.prototype.sendAsBinary){} else { - XMLHttpRequest.prototype.sendAsBinary = function(datastr) { - function byteValue(x) { - return x.charCodeAt(0) & 0xff; - } - var ords = Array.prototype.map.call(datastr, byteValue); - var ui8a = new Uint8Array(ords); - this.send(ui8a); - } - } - } catch(e) {} - - var Uploader = hr.Class.extend({ - defaults: { - url: "", - data: {} - }, - - /* - * Constructor for the uploader - */ - initialize: function(){ - Uploader.__super__.initialize.apply(this, arguments); - - this.lock = false; - this.maxFileSize = 10*1048576; - - return this; - }, - - /* - * Connect to a input file - */ - connect: function(input) { - var self = this; - input.on("change", function(e) { - e.preventDefault(); - self.upload(this.files); - }); - }, +var $ = require("jquery"); +var Q = require("q"); +var _ = require("hr.utils"); +var Class = require("hr.class"); - /* - * Run upload - * @files : html5 files - */ - upload: function(files) { - var d = Q.defer(); - var totalFilesSize = 0; - var that = this; +var logger = require("hr.logger")("uploader"); - if (that.lock == true) { - return Q.reject("Upload already in progress"); +try { + if (XMLHttpRequest.prototype.sendAsBinary){} else { + XMLHttpRequest.prototype.sendAsBinary = function(datastr) { + function byteValue(x) { + return x.charCodeAt(0) & 0xff; } + var ords = Array.prototype.map.call(datastr, byteValue); + var ui8a = new Uint8Array(ords); + this.send(ui8a); + } + } +} catch(e) {} + +var Uploader = Class.extend({ + defaults: { + url: "", + data: {} + }, + + /* + * Constructor for the uploader + */ + initialize: function(){ + Uploader.__super__.initialize.apply(this, arguments); + + this.lock = false; + this.maxFileSize = 10*1048576; + + return this; + }, + + /* + * Connect to a input file + */ + connect: function(input) { + var self = this; + input.on("change", function(e) { + e.preventDefault(); + self.upload(this.files); + }); + }, + + + /* + * Run upload + * @files : html5 files + */ + upload: function(files) { + var d = Q.defer(); + var totalFilesSize = 0; + var that = this; + + if (that.lock == true) { + return Q.reject("Upload already in progress"); + } - // Calcul total files size - totalFilesSize = _.reduce(files, function(memo, file){ return memo + (file.size != null ? file.size : 0); }, 0); + // Calcul total files size + totalFilesSize = _.reduce(files, function(memo, file){ return memo + (file.size != null ? file.size : 0); }, 0); - _.reduce(files, function(d, file) { - return d.then(function() { - return that.uploadFile(file); - }); - }, Q({})).then(function() { - d.resolve(); - }, function(err) { - d.reject(err); - }, function(progress) { - d.notify(progress); + _.reduce(files, function(d, file) { + return d.then(function() { + return that.uploadFile(file); }); + }, Q({})).then(function() { + d.resolve(); + }, function(err) { + d.reject(err); + }, function(progress) { + d.notify(progress); + }); + + return d.promise; + }, + + /* + * Upload one file + */ + uploadFile: function(file) { + var that = this; + var d = Q.defer(); + var filename = file.webkitRelativePath || file.name; + + var error = function(err) { + logging.error("error uploading", filename, ":", err); + that.trigger("error", err); + that.lock = false; + d.reject(err); + } - return d.promise; - }, - - /* - * Upload one file - */ - uploadFile: function(file) { - var that = this; - var d = Q.defer(); - var filename = file.webkitRelativePath || file.name; - - var error = function(err) { - logging.error("error uploading", filename, ":", err); - that.trigger("error", err); - that.lock = false; - d.reject(err); - } + var progress = function(percent) { + logging.log("notify ", filename, percent); + that.trigger("state", percent); + d.notify({ + 'filename': filename, + 'percent': percent + }); + }; - var progress = function(percent) { - logging.log("notify ", filename, percent); - that.trigger("state", percent); - d.notify({ - 'filename': filename, - 'percent': percent - }); - }; - - var end = function(text) { - logging.log("end", text); - that.trigger("end", text); - that.lock = false; - d.resolve(text); - }; - - if (file.name == "." || file.name == "..") { - return Q(); - } + var end = function(text) { + logging.log("end", text); + that.trigger("end", text); + that.lock = false; + d.resolve(text); + }; - if (file.name == null || file.size == null || file.size >= that.maxFileSize) { - return Q.reject(new Error("Invalid file or file too big")); - } + if (file.name == "." || file.name == "..") { + return Q(); + } - that.lock = true; - - logging.log("upload file ", filename, " in ", that.options.url, file.size,"/", file.size); - - var xhr = new XMLHttpRequest(), - upload = xhr.upload, - start_time = new Date().getTime(), - uploadurl = that.options.url.replace(":file", filename); - - logging.log("start uploading ", filename); - - upload.file = file; - upload.downloadStartTime = start_time; - upload.currentStart = start_time; - upload.currentProgress = 0; - upload.startData = 0; - upload.addEventListener("progress",function(e){ - if (e.lengthComputable) { - var percentage = Math.round((e.loaded * 100) / file.size); - progress(percentage); - } - }, false); - - xhr.open("PUT", uploadurl, true); - xhr.onreadystatechange = function(e){ - if (xhr.status != 200) { - error(new Error(xhr.status+": "+xhr.responseText)); - e.preventDefault(); - return; - } - }; - - var formData = new FormData(); - formData.append(filename, file); - _.each(that.options.data, function(v, k) { - formData.append(k, v); - }); + if (file.name == null || file.size == null || file.size >= that.maxFileSize) { + return Q.reject(new Error("Invalid file or file too big")); + } - progress(0); - xhr.onload = function() { - if (xhr.status == 200) { - end(xhr.responseText || ""); - } - } + that.lock = true; - xhr.send(formData); + logging.log("upload file ", filename, " in ", that.options.url, file.size,"/", file.size); - return d.promise; - } - }, { - // Upload file - upload: function(options) { - var d = Q.defer(); - - options = _.defaults({}, options || {}, { - 'url': null, - 'data': {}, - 'directory': false, - 'multiple': true - }); + var xhr = new XMLHttpRequest(), + upload = xhr.upload, + start_time = new Date().getTime(), + uploadurl = that.options.url.replace(":file", filename); - // Uploader - var uploader = new Uploader(options); + logging.log("start uploading ", filename); - var $f = $("input.cb-file-uploader"); - if ($f.length == 0) { - var $f = $("", { - "type": "file", - "class": "cb-file-uploader" - }); - $f.appendTo($("body")); + upload.file = file; + upload.downloadStartTime = start_time; + upload.currentStart = start_time; + upload.currentProgress = 0; + upload.startData = 0; + upload.addEventListener("progress",function(e){ + if (e.lengthComputable) { + var percentage = Math.round((e.loaded * 100) / file.size); + progress(percentage); } + }, false); - $f.hide(); + xhr.open("PUT", uploadurl, true); + xhr.onreadystatechange = function(e){ + if (xhr.status != 200) { + error(new Error(xhr.status+": "+xhr.responseText)); + e.preventDefault(); + return; + } + }; + + var formData = new FormData(); + formData.append(filename, file); + _.each(that.options.data, function(v, k) { + formData.append(k, v); + }); + + progress(0); + xhr.onload = function() { + if (xhr.status == 200) { + end(xhr.responseText || ""); + } + } - $f.prop("webkitdirectory", options.directory); - $f.prop("directory", options.directory); - $f.prop("multiple", options.multiple); + xhr.send(formData); + + return d.promise; + } +}, { + // Upload file + upload: function(options) { + var d = Q.defer(); + + options = _.defaults({}, options || {}, { + 'url': null, + 'data': {}, + 'directory': false, + 'multiple': true + }); + + // Uploader + var uploader = new Uploader(options); + + var $f = $("input.cb-file-uploader"); + if ($f.length == 0) { + var $f = $("", { + "type": "file", + "class": "cb-file-uploader" + }); + $f.appendTo($("body")); + } - // Create file element for selection - $f.change(function(e) { - e.preventDefault(); + $f.hide(); + + $f.prop("webkitdirectory", options.directory); + $f.prop("directory", options.directory); + $f.prop("multiple", options.multiple); + + // Create file element for selection + $f.change(function(e) { + e.preventDefault(); - uploader.upload(e.currentTarget.files) - .progress(function(p) { - d.notify(p.percent); - }) - .then(function() { - d.resolve() - }, function(err) { - d.reject(err); - }) - .fin(function() { - $f.remove(); - }); + uploader.upload(e.currentTarget.files) + .progress(function(p) { + d.notify(p.percent); + }) + .then(function() { + d.resolve() + }, function(err) { + d.reject(err); + }) + .fin(function() { + $f.remove(); }); - $f.trigger('click'); + }); + $f.trigger('click'); - return d.promise; - } + return d.promise; + } - }); +}); - return Uploader; -}); \ No newline at end of file +module.exports = Uploader; diff --git a/editor/views/dialogs/container.js b/editor/views/dialogs/container.js index 29f9cc83..267d8980 100644 --- a/editor/views/dialogs/container.js +++ b/editor/views/dialogs/container.js @@ -1,89 +1,87 @@ -define([ - "hr/utils", - "hr/dom", - "hr/hr" -], function(_, $, hr) { - var DialogView = hr.View.extend({ - className: "component-dialog", - defaults: { - keyboard: true, - keyboardEnter: true, - - View: hr.View, - view: {}, - size: "medium" - }, - events: { - "keydown": "keydown" - }, - - initialize: function(options) { - DialogView.__super__.initialize.apply(this, arguments); - - // Bind keyboard - this.keydownHandler = _.bind(this.keydown, this) - if (this.options.keyboard) $(document).bind("keydown", this.keydownHandler); - - // Adapt style - this.$el.addClass("size-"+this.options.size); - - // Build view - this.view = new options.View(this.options.view, this); - }, - - render: function() { - this.view.render(); - this.view.appendTo(this); - - return this.ready(); - }, - - finish: function() { - this.open(); - return DialogView.__super__.finish.apply(this, arguments); - }, - - open: function() { - if (DialogView.current != null) DialogView.current.close(); - - this.$el.appendTo($("body")); - DialogView.current = this; - - this.trigger("open"); - - return this; - }, - - close: function(e, force) { - if (e) e.preventDefault(); - - // Unbind document keydown - $(document).unbind("keydown", this.keydownHandler); - - // Hide modal - this.trigger("close", force); - this.remove(); - - DialogView.current = null; - }, - - keydown: function(e) { - if (!this.options.keyboard) return; - - var key = e.keyCode || e.which; - - // Enter: valid - if (key == 13 && this.options.keyboardEnter) { - this.close(e); - } else - // Esc: close - if (key == 27) { - this.close(e, true); - } +var _ = require("hr.utils"); +var $ = require("jquery"); +var View = require("hr.view"); + +var DialogView = View.extend({ + className: "component-dialog", + defaults: { + keyboard: true, + keyboardEnter: true, + + View: View, + view: {}, + size: "medium" + }, + events: { + "keydown": "keydown" + }, + + initialize: function(options) { + DialogView.__super__.initialize.apply(this, arguments); + + // Bind keyboard + this.keydownHandler = _.bind(this.keydown, this) + if (this.options.keyboard) $(document).bind("keydown", this.keydownHandler); + + // Adapt style + this.$el.addClass("size-"+this.options.size); + + // Build view + this.view = new options.View(this.options.view, this); + }, + + render: function() { + this.view.render(); + this.view.appendTo(this); + + return this.ready(); + }, + + finish: function() { + this.open(); + return DialogView.__super__.finish.apply(this, arguments); + }, + + open: function() { + if (DialogView.current != null) DialogView.current.close(); + + this.$el.appendTo($("body")); + DialogView.current = this; + + this.trigger("open"); + + return this; + }, + + close: function(e, force) { + if (e) e.preventDefault(); + + // Unbind document keydown + $(document).unbind("keydown", this.keydownHandler); + + // Hide modal + this.trigger("close", force); + this.remove(); + + DialogView.current = null; + }, + + keydown: function(e) { + if (!this.options.keyboard) return; + + var key = e.keyCode || e.which; + + // Enter: valid + if (key == 13 && this.options.keyboardEnter) { + this.close(e); + } else + // Esc: close + if (key == 27) { + this.close(e, true); } - }, { - current: null, - }); + } +}, { + current: null, +}); - return DialogView; -}); \ No newline at end of file +module.exports = DialogView; diff --git a/editor/views/dialogs/input.js b/editor/views/dialogs/input.js index 9100623a..69717036 100644 --- a/editor/views/dialogs/input.js +++ b/editor/views/dialogs/input.js @@ -1,66 +1,66 @@ -define([ - "hr/utils", - "hr/dom", - "hr/hr", - "views/form" -], function(_, $, hr, FormView) { - var DialogInputView = hr.View.extend({ - className: "dialog-input", - defaults: { - className: "", - template: "", - value: true - }, - events: { - "click .do-close": "onClose", - "click .do-confirm": "onConfirm" - }, +var _ = require("hr.utils"); +var $ = require("jquery"); +var View = require("hr.view"); - initialize: function(options) { - DialogInputView.__super__.initialize.apply(this, arguments); +var FormView = require("../form"); - // Adapt style - this.$el.addClass(this.options.className); - // Value - this.value = this.options.value; - }, +var DialogInputView = View.extend({ + className: "dialog-input", + defaults: { + className: "", + template: "", + value: true + }, + events: { + "click .do-close": "onClose", + "click .do-confirm": "onConfirm" + }, - finish: function() { - this.$("input").first().select(); - return DialogInputView.__super__.finish.apply(this, arguments); - }, + initialize: function(options) { + DialogInputView.__super__.initialize.apply(this, arguments); - template: function() { - return this.options.template; - }, - templateContext: function() { - return { - options: this.options - }; - }, + // Adapt style + this.$el.addClass(this.options.className); - getValue: function() { - var selector = this.options.value; - if (_.isFunction(selector)) { - this.value = selector(this); - } else if (_.isString(selector)) { - this.value = this[selector](); - } + // Value + this.value = this.options.value; + }, - return this.value; - }, + finish: function() { + this.$("input").first().select(); + return DialogInputView.__super__.finish.apply(this, arguments); + }, - onConfirm: function(e) { - if (e) e.preventDefault(); + template: function() { + return this.options.template; + }, + templateContext: function() { + return { + options: this.options + }; + }, - this.parent.close(e); - }, - onClose: function(e) { - if (e) e.preventDefault(); - this.parent.close(null, true); + getValue: function() { + var selector = this.options.value; + if (_.isFunction(selector)) { + this.value = selector(this); + } else if (_.isString(selector)) { + this.value = this[selector](); } - }); - return DialogInputView; -}); \ No newline at end of file + return this.value; + }, + + onConfirm: function(e) { + if (e) e.preventDefault(); + + this.parent.close(e); + }, + onClose: function(e) { + if (e) e.preventDefault(); + this.parent.close(null, true); + } +}); + +module.exports = DialogInputView; diff --git a/editor/views/dialogs/list.js b/editor/views/dialogs/list.js index 3bc6e1b3..e9f3c7a8 100644 --- a/editor/views/dialogs/list.js +++ b/editor/views/dialogs/list.js @@ -1,213 +1,214 @@ -define([ - "hr/utils", - "hr/dom", - "hr/hr", - "utils/string", - "views/dialogs/input" -], function(_, $, hr, string, DialogInputView) { - var ListItem = hr.List.Item.extend({ - className: "list-item", - - initialize: function(options) { - ListItem.__super__.initialize.apply(this, arguments); - - this.dialogContent = this.list.parent; - }, - - template: function() { - return this.dialogContent.options.template; - }, - templateContext: function() { - return { - item: this.model - }; +var _ = require("hr.utils"); +var $ = require("jquery"); +var View = require("hr.view"); +var ListView = require("hr.list"); + +var string = require("../../utils/string"); +var DialogInputView = require("./input"); + + +var ListItem = ListView.Item.extend({ + className: "list-item", + + initialize: function(options) { + ListItem.__super__.initialize.apply(this, arguments); + + this.dialogContent = this.list.parent; + }, + + template: function() { + return this.dialogContent.options.template; + }, + templateContext: function() { + return { + item: this.model + }; + } +}); + +var ListView = ListView.extend({ + Item: ListItem, + className: "ui-content-list" +}); + +var DialogListView = DialogInputView.extend({ + defaults: { + textIndex: function(model) { return JSON.stringify(model.toJSON()); } + }, + className: "dialog-list", + + initialize: function() { + DialogListView.__super__.initialize.apply(this, arguments); + + // Source for items + this.results = new Collection(); + this.source = this.options.source; + + if (this.options.source instanceof Collection) { + // Source is a collection + this.results = new this.options.source.constructor() + this.source = function(q) { + return this.options.source.filter(function(model) { + return this.searchText(model, q); + }, this); + }.bind(this); } - }); - - var ListView = hr.List.extend({ - Item: ListItem, - className: "ui-content-list" - }); - - var DialogListView = DialogInputView.extend({ - defaults: { - textIndex: function(model) { return JSON.stringify(model.toJSON()); } - }, - className: "dialog-list", - - initialize: function() { - DialogListView.__super__.initialize.apply(this, arguments); - - // Source for items - this.results = new hr.Collection(); - this.source = this.options.source; - - if (this.options.source instanceof hr.Collection) { - // Source is a collection - this.results = new this.options.source.constructor() - this.source = function(q) { - return this.options.source.filter(function(model) { - return this.searchText(model, q); - }, this); - }.bind(this); - } - // Filter for items - this.$filterInput = $("", { - 'type': "text", - 'placeholder': this.options.placeholder - }); - this.keydownInterval = null; - this.$filterInput.on("keydown", this.onFilterKeydown.bind(this)); - this.$filterInput.on("keyup", this.onFilterKeyup.bind(this)); - this.$filterInput.appendTo(this.$el); - - // Items list - this.list = new ListView({ - collection: this.results - }, this); - this.list.appendTo(this.$el); - - // Focus input - this.listenTo(this.parent, "open", function() { - this.$filterInput.focus(); - this.doSearch(""); - this.selectItem(0); + // Filter for items + this.$filterInput = $("", { + 'type': "text", + 'placeholder': this.options.placeholder + }); + this.keydownInterval = null; + this.$filterInput.on("keydown", this.onFilterKeydown.bind(this)); + this.$filterInput.on("keyup", this.onFilterKeyup.bind(this)); + this.$filterInput.appendTo(this.$el); + + // Items list + this.list = new ListView({ + collection: this.results + }, this); + this.list.appendTo(this.$el); + + // Focus input + this.listenTo(this.parent, "open", function() { + this.$filterInput.focus(); + this.doSearch(""); + this.selectItem(0); + }); + }, + + render: function() { + return this.ready(); + }, + + searchText: function(model, q) { + var t = this.options.textIndex(model); + + var words = q.split("") + + t = t.toLowerCase(); + q = q.toLowerCase(); + + return _.every(q.split(" "), function(_q) { + return t.search(_q) !== -1; + }); + }, + + doSearch: function(query) { + var that = this, toRemove = []; + if (this.list.collection.query == query) return; + + if (this.list.collection.query && query + && query.indexOf(this.list.collection.query) == 0) { + // Continue current search + this.list.collection.query = query; + this.list.collection.each(function(model) { + if (!that.searchText(model, query)) { + toRemove.push(model); + } }); - }, - - render: function() { - return this.ready(); - }, - - searchText: function(model, q) { - var t = this.options.textIndex(model); - - var words = q.split("") - - t = t.toLowerCase(); - q = q.toLowerCase(); + this.list.collection.remove(toRemove); + //this.list.collection.sort(); + this.selectItem(this.getSelectedItem()); + } else { + // Different search + this.list.collection.query = query; + this.list.collection.reset([]); + + Q() + .then(function() { + return that.source(query); + }) + .then(function(result) { + that.list.collection.add(_.filter(result, that.options.filter)); + that.selectItem(that.getSelectedItem()); + }, console.error.bind(console)); + } + }, - return _.every(q.split(" "), function(_q) { - return t.search(_q) !== -1; - }); - }, - - doSearch: function(query) { - var that = this, toRemove = []; - if (this.list.collection.query == query) return; - - if (this.list.collection.query && query - && query.indexOf(this.list.collection.query) == 0) { - // Continue current search - this.list.collection.query = query; - this.list.collection.each(function(model) { - if (!that.searchText(model, query)) { - toRemove.push(model); - } - }); - this.list.collection.remove(toRemove); - //this.list.collection.sort(); - this.selectItem(this.getSelectedItem()); - } else { - // Different search - this.list.collection.query = query; - this.list.collection.reset([]); - - Q() - .then(function() { - return that.source(query); - }) - .then(function(result) { - that.list.collection.add(_.filter(result, that.options.filter)); - that.selectItem(that.getSelectedItem()); - }, console.error.bind(console)); - } - }, + selectItem: function(i) { + var i, boxH = this.list.$el.height(); - selectItem: function(i) { - var i, boxH = this.list.$el.height(); + this.selected = i; - this.selected = i; + if (this.selected >= this.list.collection.size()) this.selected = this.list.collection.size() - 1; + if (this.selected < 0) this.selected = 0; - if (this.selected >= this.list.collection.size()) this.selected = this.list.collection.size() - 1; - if (this.selected < 0) this.selected = 0; + i = 0; + this.list.collection.each(function(model) { + var y, h, item = this.list.items[model.id]; + item.$el.toggleClass("active", i == this.selected); - i = 0; - this.list.collection.each(function(model) { - var y, h, item = this.list.items[model.id]; - item.$el.toggleClass("active", i == this.selected); - - if (i == this.selected) { - h = item.$el.outerHeight(); - y = item.$el.position().top; - - if (y > (boxH-(h/2))) { - this.list.$el.scrollTop((i+1)*h - boxH) - } else if (y <= (h/2)) { - this.list.$el.scrollTop((i)*h) - } - } + if (i == this.selected) { + h = item.$el.outerHeight(); + y = item.$el.position().top; - i = i + 1; - }, this); - }, - - getSelectedItem: function() { - var _ret = 0; - this.list.collection.each(function(model, i) { - var item = this.list.items[model.id]; - if (item.$el.hasClass("active")) { - _ret = i; - return false; + if (y > (boxH-(h/2))) { + this.list.$el.scrollTop((i+1)*h - boxH) + } else if (y <= (h/2)) { + this.list.$el.scrollTop((i)*h) } - }, this); - return _ret; - }, + } - getValue: function() { - return this.list.collection.at(this.getSelectedItem()); - }, + i = i + 1; + }, this); + }, + + getSelectedItem: function() { + var _ret = 0; + this.list.collection.each(function(model, i) { + var item = this.list.items[model.id]; + if (item.$el.hasClass("active")) { + _ret = i; + return false; + } + }, this); + return _ret; + }, - onFilterKeydown: function(e) { - var key = e.which || e.keyCode; + getValue: function() { + return this.list.collection.at(this.getSelectedItem()); + }, - if (key == 38 || key == 40 || key == 13) { - e.preventDefault(); - } + onFilterKeydown: function(e) { + var key = e.which || e.keyCode; - var interval = function() { - var selected = this.getSelectedItem(); - var pSelected = selected; + if (key == 38 || key == 40 || key == 13) { + e.preventDefault(); + } - if (key == 38) { - /* UP */ - selected = selected - 1; - } else if (key == 40) { - /* DOWN */ - selected = selected + 1; - } - if (selected != pSelected) this.selectItem(selected); - }.bind(this); + var interval = function() { + var selected = this.getSelectedItem(); + var pSelected = selected; - if (this.keydownInterval) { - clearInterval(this.keydownInterval); - this.keydownInterval = null; + if (key == 38) { + /* UP */ + selected = selected - 1; + } else if (key == 40) { + /* DOWN */ + selected = selected + 1; } - interval(); - this.keydownInterval = setInterval(interval, 600); - }, - onFilterKeyup: function(e) { - var q = this.$filterInput.val().toLowerCase(); + if (selected != pSelected) this.selectItem(selected); + }.bind(this); + + if (this.keydownInterval) { + clearInterval(this.keydownInterval); + this.keydownInterval = null; + } + interval(); + this.keydownInterval = setInterval(interval, 600); + }, + onFilterKeyup: function(e) { + var q = this.$filterInput.val().toLowerCase(); - this.doSearch(q); + this.doSearch(q); - if (this.keydownInterval) { - clearInterval(this.keydownInterval); - this.keydownInterval = null; - } + if (this.keydownInterval) { + clearInterval(this.keydownInterval); + this.keydownInterval = null; } - }); + } +}); - return DialogListView; -}); \ No newline at end of file +module.exports = DialogListView; diff --git a/editor/views/form.js b/editor/views/form.js index 225772d2..bc0d278c 100644 --- a/editor/views/form.js +++ b/editor/views/form.js @@ -1,181 +1,179 @@ -define([ - "hr/utils", - "hr/dom", - "hr/hr" -], function(_, $, hr) { - - var GETTER = { - 'boolean': function() { - return $(this).is(":checked"); - }, - 'string': function() { - return $(this).val(); - }, - 'number': function() { - return parseInt($(this).val()); - } - }; - - var RENDERER = { - 'boolean': function(propertyName, property, value) { - return $("", { - 'type': "checkbox", - 'checked': value === true, - 'name': propertyName - }); - }, - - 'number': function(propertyName, property, value) { - return $("", { - 'type': "number", - 'value': value, - 'name': propertyName, - 'min': property.minimum, - 'max': property.maximum, - 'step': property.multipleOf || 1 - }); - }, - - 'string': function(propertyName, property, value) { - if (property['enum']) return RENDERER.select.apply(this, arguments); - - return $("", { - 'type': "text", - 'value': value, - 'name': propertyName - }); - }, - - 'select': function(propertyName, property, value) { - var $select = $("", { + 'type': "checkbox", + 'checked': value === true, + 'name': propertyName + }); + }, + + 'number': function(propertyName, property, value) { + return $("", { + 'type': "number", + 'value': value, + 'name': propertyName, + 'min': property.minimum, + 'max': property.maximum, + 'step': property.multipleOf || 1 + }); + }, + + 'string': function(propertyName, property, value) { + if (property['enum']) return RENDERER.select.apply(this, arguments); + + return $("", { + 'type': "text", + 'value': value, + 'name': propertyName + }); + }, + + 'select': function(propertyName, property, value) { + var $select = $(" +
+
+ + +
+
+
+
+
+ +
+
+ +
+
- -
- - -
\ No newline at end of file diff --git a/editor/resources/templates/dialogs/schema.html b/editor/resources/templates/dialogs/schema.html index d5ae55a6..70699a9d 100644 --- a/editor/resources/templates/dialogs/schema.html +++ b/editor/resources/templates/dialogs/schema.html @@ -1,5 +1,7 @@ -

<%- options.schema.title %>

- +
+ <%- options.schema.title %> +
+
<% _.each(options.schema.properties, function(property, key) { var value = options.defaultValues[key] || property.default; @@ -16,8 +18,14 @@
<% } %> <% }); %> - -
- - -
\ No newline at end of file +
+
+
+
+ +
+
+ +
+
+
From 0a069adfc5b8231361f9f7d2e470cfb072b3403b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 11 Apr 2015 22:46:11 -0500 Subject: [PATCH 029/101] Fix option 'isHtml' for alert dialogs --- editor/resources/templates/dialogs/alert.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/resources/templates/dialogs/alert.html b/editor/resources/templates/dialogs/alert.html index 0bf547a8..d65608ba 100644 --- a/editor/resources/templates/dialogs/alert.html +++ b/editor/resources/templates/dialogs/alert.html @@ -1,5 +1,5 @@
-<% if (options.isHtml) { %> +<% if (!options.isHtml) { %>

<%- options.text %>

<% } else { %> <%= options.text %> From 60d1d78b017111b4b4e48df6dfe0990754a2d531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 06:56:25 -0500 Subject: [PATCH 030/101] Focus first input after dialog creation --- editor/views/dialogs/input.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/editor/views/dialogs/input.js b/editor/views/dialogs/input.js index e98be5d1..5d605269 100644 --- a/editor/views/dialogs/input.js +++ b/editor/views/dialogs/input.js @@ -28,7 +28,10 @@ var DialogInputView = View.Template.extend({ }, finish: function() { - this.$("input").first().select(); + var that = this; + _.defer(function() { + that.$("input").first().focus(); + }); return DialogInputView.__super__.finish.apply(this, arguments); }, From b8b6c637006995e276a2821624a4a86fd409dae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 08:36:13 -0500 Subject: [PATCH 031/101] Improve command resolver to use pattern --- editor/collections/commands.js | 20 +++++++++++++++++++- editor/models/command.js | 20 ++++++++++++++++++++ editor/models/file.js | 2 +- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/editor/collections/commands.js b/editor/collections/commands.js index 3a71f014..9ae0115e 100644 --- a/editor/collections/commands.js +++ b/editor/collections/commands.js @@ -27,12 +27,30 @@ var Commands = Collection.extend({ // Run a command run: function(_cmd, args) { - var cmd = this.get(_cmd); + var cmd = this.resolve(_cmd); if (!cmd) return Q.reject(new Error("Command not found: '"+_cmd+"'")); return cmd.run(args); }, + // Resolve a command + resolve: function(_cmd) { + return _.chain(this.models) + .map(function(m) { + return { + cmd: m, + score: m.resolve(_cmd) + }; + }) + .filter(function(r) { + return r.score > 0; + }) + .sortBy("score") + .pluck("cmd") + .last() + .value(); + }, + // Set context setContext: function(id, data) { logger.log("update context", id); diff --git a/editor/models/command.js b/editor/models/command.js index ad4ecabd..24b64aeb 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -88,6 +88,26 @@ var Command = Model.extend({ || !this.collection || !this.collection.context || _.contains(context, this.collection.context.type)); + }, + + // Valid a command name against this command + resolve: function(cmd) { + var score = 0; + var parts = cmd.split("."); + var thisParts = this.get("id").split("."); + + _.each(parts, function(part, i) { + if (!thisParts[i]) return false; + + var r = new RegExp(thisParts[i]); + if (part.match(r) == null) { + return false; + } + + score = score + 1; + }); + + return score/thisParts.length; } }); diff --git a/editor/models/file.js b/editor/models/file.js index 6d31b9f1..d917e11c 100644 --- a/editor/models/file.js +++ b/editor/models/file.js @@ -54,7 +54,7 @@ var File = Model.extend({ // Open this file open: function() { - return commands.run("file.open", { + return commands.run("file.open."+this.getExtension().slice(1), { path: this.get("path") }); }, From 9b655a7b420ea3789fad65e6a18459d58d01e22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 09:34:34 -0500 Subject: [PATCH 032/101] Return mime type in fs rpc api --- lib/services/fs.js | 4 +++- package.json | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/services/fs.js b/lib/services/fs.js index 06ac7fe5..75f165ce 100644 --- a/lib/services/fs.js +++ b/lib/services/fs.js @@ -4,6 +4,7 @@ var fs = require('fs'); var path = require('path'); var wrench = require('wrench'); var stream = require('stream'); +var mime = require('mime'); var workspace = require('../workspace'); var base64 = require('../utils/base64'); @@ -16,7 +17,8 @@ var fileInfos = function(_path, stat) { 'size': stat.size, 'mtime': stat.mtime.getTime(), 'atime': stat.atime.getTime(), - 'mode': stat.mode + 'mode': stat.mode, + 'mime': mime.lookup(_path) }; }; diff --git a/package.json b/package.json index 8bf30982..6fa0009e 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "open": "0.0.5", "ini": "1.2.1", "basic-auth-connect": "1.0.0", - "connect-multiparty": "1.1.0" + "connect-multiparty": "1.1.0", + "mime": "1.3.4" }, "devDependencies": { "gulp": "^3.8.11", From 35732e3581822cc57f56d28ab9096578c4d6bf36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 09:35:03 -0500 Subject: [PATCH 033/101] Make it possible to read as base64 --- editor/models/file.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/editor/models/file.js b/editor/models/file.js index d917e11c..1771ec73 100644 --- a/editor/models/file.js +++ b/editor/models/file.js @@ -17,7 +17,8 @@ var File = Model.extend({ size: 0, mtime: 0, atime: 0, - buffer: null + buffer: null, + mime: "text/plain" }, idAttribute: "name", @@ -103,14 +104,25 @@ var File = Model.extend({ }, // Read file content - read: function() { - if (this.isBuffer()) return Q(this.get("buffer")); + read: function(opts) { + opts = _.defaults(opts || {}, { + base64: false + }); - return rpc.execute("fs/read", { - 'path': this.get("path") - }) - .get("content") - .then(hash.atob); + var p; + + + if (this.isBuffer()) p = Q(hash.btoa(this.get("buffer"))); + else { + p = rpc.execute("fs/read", { + 'path': this.get("path") + }) + .get("content"); + } + + if (!opts.base64) p = p.then(hash.atob); + + return p; }, // Write file content From 6f17e08e42bfe287f01de5897c1accfb10d436c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 09:38:10 -0500 Subject: [PATCH 034/101] Add image package as default --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 6fa0009e..515f6856 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "merge-stream": "0.1.7" }, "packageDependencies": { + "image": "CodeboxIDE/package-image#1.0.0", "about": "CodeboxIDE/package-about", "command-palette": "CodeboxIDE/package-command-palette", "files-tree": "CodeboxIDE/package-files-tree", From 2018c5ddaf955ae69487db41ed3bbcc511b57f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 09:45:27 -0500 Subject: [PATCH 035/101] Fix plugins loading error dialog --- editor/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/main.js b/editor/main.js index 636ac5ae..05f1f09c 100644 --- a/editor/main.js +++ b/editor/main.js @@ -58,7 +58,7 @@ Q.delay(500) }).join("\n")+ ""; } - return dialogs.alert(message, { html: true }) + return dialogs.alert(message, { isHtml: true }) }); }) .then(app.start.bind(app)) From e7822acea4ea14375821129fbfc33db8d40b63ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 10:04:14 -0500 Subject: [PATCH 036/101] Improve rpc error handling --- editor/core/rpc.js | 12 ++++-------- editor/main.js | 1 + 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/editor/core/rpc.js b/editor/core/rpc.js index 4a9cb157..ccc75be7 100644 --- a/editor/core/rpc.js +++ b/editor/core/rpc.js @@ -12,14 +12,10 @@ rpc.defaultMethod({ .then(function(res) { return res.data.result || {}; }, function(err) { - try { - var errContent = JSON.parse(err.httpRes); - var e = new Error(errContent.error || err.message); - e.code = errContent.code || err.status || 500; - return Q.reject(e); - } catch(e) { - return Q.reject(err); - } + var e = new Error(err.data.error || err); + e.code = err.status; + + return Q.reject(e); }); } }); diff --git a/editor/main.js b/editor/main.js index 05f1f09c..461f2560 100644 --- a/editor/main.js +++ b/editor/main.js @@ -64,5 +64,6 @@ Q.delay(500) .then(app.start.bind(app)) .fail(function(err) { logger.error("Error:", err.message || "", err.stack || err); + return dialogs.error(err); }); From 11d950fb9ea316656dd4986c25a582273677f484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 12 Apr 2015 10:06:57 -0500 Subject: [PATCH 037/101] Don't fail if error loading settings locally --- lib/configs/local.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/configs/local.js b/lib/configs/local.js index b78160c0..c3a7bdc1 100644 --- a/lib/configs/local.js +++ b/lib/configs/local.js @@ -23,8 +23,8 @@ module.exports = function(options) { options.hooks = _.defaults(options.hooks, { 'settings.get': function(args) { return Q.nfcall(fs.readFile, SETTINGS_FILE, "utf-8") - .fail(_.constant("{}")) .then(JSON.parse) + .fail(_.constant({})) .then(function(config) { if (!config[options.id]) config[options.id] = {}; return config[options.id][args.user] || {}; @@ -34,8 +34,8 @@ module.exports = function(options) { return Q.nfcall(fs.readFile, SETTINGS_FILE, "utf-8") - .fail(_.constant("{}")) .then(JSON.parse) + .fail(_.constant({})) .then(function(config) { if (!config[options.id]) config[options.id] = {}; config[options.id][args.user] = args.settings; From 92af1a4c0146c179dfc6c80192ddff688f408606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 13 Apr 2015 14:12:16 -0500 Subject: [PATCH 038/101] Fix commands resolving --- editor/models/command.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/models/command.js b/editor/models/command.js index 24b64aeb..e03d045c 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -107,7 +107,7 @@ var Command = Model.extend({ score = score + 1; }); - return score/thisParts.length; + return score/parts.length; } }); From 509ae7fda7c70b819d3ab75c4826b9f128062fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 13 Apr 2015 14:38:10 -0500 Subject: [PATCH 039/101] Add method dialogs.input --- editor/utils/dialogs.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/utils/dialogs.js b/editor/utils/dialogs.js index e40eaacb..47dc812d 100644 --- a/editor/utils/dialogs.js +++ b/editor/utils/dialogs.js @@ -130,5 +130,6 @@ module.exports = { confirm: openConfirm, prompt: openPrompt, list: openList, - schema: openSchema + schema: openSchema, + input: openInput }; From 10576d90a89fc8e6369ee94cf6c4359d9b7e4382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 13 Apr 2015 14:38:23 -0500 Subject: [PATCH 040/101] Give http get access to workspace fs --- editor/models/file.js | 5 +++++ lib/index.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/editor/models/file.js b/editor/models/file.js index 1771ec73..f302aba9 100644 --- a/editor/models/file.js +++ b/editor/models/file.js @@ -125,6 +125,11 @@ var File = Model.extend({ return p; }, + // Access url + accessUrl: function() { + return "/fs/"+this.get("path"); + }, + // Write file content write: function(content) { var that = this; diff --git a/lib/index.js b/lib/index.js index 4fae230e..d82dbc1e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -115,6 +115,11 @@ var start = function(config) { // RPC services app.use('/rpc', rpc.router); + // Fs direct access + app.use('/fs', _.memoize(function(req, res, next) { + return express.static(workspace.root()).apply(this, arguments); + })); + // Error handling app.use(function(req, res, next) { var e = new Error("Page not found"); From cde12b9c4f47071ffdc2c5421700eff8aaaf71b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 13 Apr 2015 14:45:42 -0500 Subject: [PATCH 041/101] Fix fs static access middleware --- lib/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/index.js b/lib/index.js index d82dbc1e..69d1f597 100644 --- a/lib/index.js +++ b/lib/index.js @@ -116,9 +116,11 @@ var start = function(config) { app.use('/rpc', rpc.router); // Fs direct access - app.use('/fs', _.memoize(function(req, res, next) { - return express.static(workspace.root()).apply(this, arguments); - })); + var _fsmiddleware; + app.use('/fs', function(req, res, next) { + if (!_fsmiddleware) _fsmiddleware = express.static(workspace.root()); + return _fsmiddleware.apply(this, arguments); + }); // Error handling app.use(function(req, res, next) { From 0840054b33a42e9b1b626158e04f4f70c4d56d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 13 Apr 2015 15:30:59 -0500 Subject: [PATCH 042/101] Accept multiple command contexts --- editor/collections/commands.js | 9 +++------ editor/models/command.js | 13 ++++++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/editor/collections/commands.js b/editor/collections/commands.js index 9ae0115e..75d11248 100644 --- a/editor/collections/commands.js +++ b/editor/collections/commands.js @@ -52,12 +52,9 @@ var Commands = Collection.extend({ }, // Set context - setContext: function(id, data) { - logger.log("update context", id); - this.context = { - 'type': id, - 'data': data - }; + setContext: function(ctx) { + logger.log("update context", _.keys(ctx)); + this.context = ctx || {}; this.trigger("context", this.context); } }); diff --git a/editor/models/command.js b/editor/models/command.js index e03d045c..a828f535 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -69,7 +69,7 @@ var Command = Model.extend({ return Q() .then(function() { - return that.get("run").apply(that, [ args || {}, that.collection.context.data, origin ]); + return that.get("run").apply(that, [ args || {}, that.collection.context, origin ]); }) .fail(function(err) { logger.error("Command failed", err); @@ -84,13 +84,12 @@ var Command = Model.extend({ // Valid context isValidContext: function() { var context = this.get("context") || []; - return (context.length == 0 - || !this.collection - || !this.collection.context - || _.contains(context, this.collection.context.type)); + var currentContext = _.keys(this.collection.context); + + return _.difference(context, currentContext).length == 0; }, - // Valid a command name against this command + // Valid a command name against this command and return a match score resolve: function(cmd) { var score = 0; var parts = cmd.split("."); @@ -107,7 +106,7 @@ var Command = Model.extend({ score = score + 1; }); - return score/parts.length; + return (score/parts.length) + (score/thisParts.length); } }); From ef772b72af9a88727578bb79907fcf6da969fae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 14 Apr 2015 10:04:30 -0500 Subject: [PATCH 043/101] Change method name hasValidContext --- editor/models/command.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/models/command.js b/editor/models/command.js index a828f535..ea95146c 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -63,7 +63,7 @@ var Command = Model.extend({ var that = this; // Check context - if (!this.isValidContext()) return Q(); + if (!this.hasValidContext()) return Q(); logger.log("Run", this.get("id")); @@ -82,7 +82,7 @@ var Command = Model.extend({ }, // Valid context - isValidContext: function() { + hasValidContext: function() { var context = this.get("context") || []; var currentContext = _.keys(this.collection.context); From a2f570ad2517fe68094c385ac34f63b29b69b303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 14 Apr 2015 10:04:43 -0500 Subject: [PATCH 044/101] Improve rpc error logging --- lib/rpc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rpc.js b/lib/rpc.js index 097bae53..68a6d60d 100644 --- a/lib/rpc.js +++ b/lib/rpc.js @@ -64,7 +64,7 @@ var init = function() { var method = service[req.params.method]; if (!method) { - var e = new Error("Methodnot found"); + var e = new Error("Method not found"); e.code = 404; return next(e); } @@ -79,7 +79,7 @@ var init = function() { }); }) .fail(function(err) { - logger.error("Error with method '"+method+"'"); + logger.error("Error with method '"+req.params.method+"'"); logger.exception(err, false); res.send(500, { error: err.message || err, From 1fe82128d1a9c55668072fe74798dc20a16af8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 14 Apr 2015 10:13:57 -0500 Subject: [PATCH 045/101] Add state "enabled" for commands --- editor/models/command.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/editor/models/command.js b/editor/models/command.js index ea95146c..96977ee0 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -27,7 +27,13 @@ var Command = Model.extend({ arguments: [], // Keyboard shortcuts - shortcuts: [] + shortcuts: [], + + // Hidden from command palette + hidden: false, + + // Disabled (not runnable) + enabled: true }, // Constructor @@ -63,7 +69,7 @@ var Command = Model.extend({ var that = this; // Check context - if (!this.hasValidContext()) return Q(); + if (!this.isRunnable()) return Q(); logger.log("Run", this.get("id")); @@ -107,6 +113,11 @@ var Command = Model.extend({ }); return (score/parts.length) + (score/thisParts.length); + }, + + // Check if command is runnable + isRunnable: function() { + return this.hasValidContext() && this.get("enabled"); } }); From 51eff0fa87426675bd91a70b0ee35133ea4c06e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 14 Apr 2015 11:40:54 -0500 Subject: [PATCH 046/101] Fix command resolving for short command --- editor/models/command.js | 1 + lib/configs/local.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/models/command.js b/editor/models/command.js index 96977ee0..cc10352f 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -112,6 +112,7 @@ var Command = Model.extend({ score = score + 1; }); + if (score < thisParts.length) return 0; return (score/parts.length) + (score/thisParts.length); }, diff --git a/lib/configs/local.js b/lib/configs/local.js index c3a7bdc1..42dbcedc 100644 --- a/lib/configs/local.js +++ b/lib/configs/local.js @@ -30,9 +30,8 @@ module.exports = function(options) { return config[options.id][args.user] || {}; }); }, - 'settings.set': function(args) { - + 'settings.set': function(args) { return Q.nfcall(fs.readFile, SETTINGS_FILE, "utf-8") .then(JSON.parse) .fail(_.constant({})) From e2a92c2481d5d35b1deb94d66834f8bf7f90042d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 15 Apr 2015 11:59:42 -0500 Subject: [PATCH 047/101] Fix size of dialogs --- editor/resources/stylesheets/ui/dialogs.less | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/editor/resources/stylesheets/ui/dialogs.less b/editor/resources/stylesheets/ui/dialogs.less index 302eb1cd..00938ba2 100644 --- a/editor/resources/stylesheets/ui/dialogs.less +++ b/editor/resources/stylesheets/ui/dialogs.less @@ -6,12 +6,23 @@ left: 0px; right: 0px; background: rgba(0, 0, 0, 0.4); + overflow-y: auto; + + &.size-large .dialog-wrapper { + width: @dialog-size-large; + margin-left: -@dialog-size-large/2; + } + + &.size-small .dialog-wrapper { + width: @dialog-size-small; + margin-left: -@dialog-size-small/2; + } .dialog-wrapper { position: absolute; z-index: 1001; - top: 10%; + margin: 2% 0px; left: 50%; width: @dialog-size-medium; @@ -19,16 +30,6 @@ background: @color-0-1; - &.size-large { - width: @dialog-size-large; - margin-left: -@dialog-size-large/2; - } - - &.size-small { - width: @dialog-size-small; - margin-left: -@dialog-size-small/2; - } - .dialog-list { input { width: 100%; From b63b3d0f3a3c83270c2d28f85b951cb9dfbe35f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 15 Apr 2015 14:09:27 -0500 Subject: [PATCH 048/101] Add more packages as default (ctags, audio, image) --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 515f6856..a3219255 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,6 @@ "merge-stream": "0.1.7" }, "packageDependencies": { - "image": "CodeboxIDE/package-image#1.0.0", "about": "CodeboxIDE/package-about", "command-palette": "CodeboxIDE/package-command-palette", "files-tree": "CodeboxIDE/package-files-tree", @@ -95,7 +94,10 @@ "find": "CodeboxIDE/package-find", "git": "CodeboxIDE/package-git", "settings": "CodeboxIDE/package-settings", - "menubar": "CodeboxIDE/package-menubar" + "menubar": "CodeboxIDE/package-menubar", + "image": "CodeboxIDE/package-image#1.0.0", + "ctags": "CodeboxIDE/package-ctags", + "audio": "CodeboxIDE/package-audio" }, "scripts": { "test": "export TESTING=true; mocha --reporter list" From 628253424f912b4963b65a68c1de8f93d6fc0cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 15 Apr 2015 16:39:10 -0500 Subject: [PATCH 049/101] Add logs for package events --- lib/packages.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/packages.js b/lib/packages.js index 7e0326ee..8085d145 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -16,9 +16,11 @@ var manager = new Packager({ }); manager.on("add", function(pkg) { + logger.log("add package", pkg.pkg.name); events.emit("packages:add", pkg.infos()); }); manager.on("remove", function(pkg) { + logger.log("remove package", pkg.pkg.name); events.emit("packages:remove", pkg.infos()); }); manager.on("log", function(log) { From 31618f686f04cb8c3724659b4a6ec162792e4647 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 09:29:38 -0500 Subject: [PATCH 050/101] Update happy rhino and use dedupe --- gulpfile.js | 8 +++++++- package.json | 25 +++++++++++++------------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index a2c71e1a..8beab60b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,6 +10,7 @@ var rename = require('gulp-rename'); var uglify = require('gulp-uglify'); var stringify = require('stringify'); var merge = require('merge-stream'); +var exec = require('child_process').exec; // Compile Javascript gulp.task('scripts', function() { @@ -23,6 +24,11 @@ gulp.task('scripts', function() { .pipe(gulp.dest('./build/static/js')); }); +// Dedupe modules +gulp.task('dedupe', function (cb) { + exec('npm dedupe', cb); +}) + // Copy html gulp.task('html', function() { return gulp.src('editor/index.html') @@ -68,5 +74,5 @@ gulp.task('clean', function(cb) { }); gulp.task('default', function(cb) { - runSequence('clean', ['scripts', 'styles', 'html', 'assets'], cb); + runSequence('clean', 'dedupe', ['scripts', 'styles', 'html', 'assets'], cb); }); diff --git a/package.json b/package.json index a3219255..6df48803 100644 --- a/package.json +++ b/package.json @@ -60,18 +60,19 @@ "mocha": "2.2.4", "chai": "2.2.0", "jquery": "~2.1.3", - "hr.utils": "*", - "hr.app": "*", - "hr.list": "*", - "hr.storage": "*", - "hr.model": "0.1.1", - "hr.view": "*", - "hr.collection": "0.1.2", - "hr.class": "*", - "hr.dnd": "*", - "hr.gridview": "0.2.0", - "hr.logger": "0.1.1", - "hr.backend": "0.1.1", + "hr.utils": "0.1.0", + "hr.app": "0.2.0", + "hr.list": "0.3.0", + "hr.storage": "0.2.0", + "hr.model": "0.2.0", + "hr.view": "0.2.0", + "hr.collection": "0.2.0", + "hr.class": "0.4.0", + "hr.dnd": "0.2.0", + "hr.gridview": "0.3.0", + "hr.logger": "0.2.0", + "hr.backend": "0.2.0", + "hr.queue": "0.2.0", "octicons": "2.2.0", "mousetrap": "1.5.2", "moment": "2.9.0", From 57deacfd1229dfd84e268e8d241c3927cf758cf4 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 09:54:42 -0500 Subject: [PATCH 051/101] Update hr.list@0.3.1 --- gulpfile.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 8beab60b..1e9927bd 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -27,7 +27,7 @@ gulp.task('scripts', function() { // Dedupe modules gulp.task('dedupe', function (cb) { exec('npm dedupe', cb); -}) +}); // Copy html gulp.task('html', function() { diff --git a/package.json b/package.json index 6df48803..abc58a2b 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "jquery": "~2.1.3", "hr.utils": "0.1.0", "hr.app": "0.2.0", - "hr.list": "0.3.0", + "hr.list": "0.3.1", "hr.storage": "0.2.0", "hr.model": "0.2.0", "hr.view": "0.2.0", From 56bdc9a05226c376adc25a800325aab3c8075c5a Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 12:43:26 -0500 Subject: [PATCH 052/101] Add method File.saveAs --- editor/models/file.js | 58 ++++++++++++++++++++++++++++++++--------- editor/utils/dialogs.js | 2 ++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/editor/models/file.js b/editor/models/file.js index f302aba9..ada685ad 100644 --- a/editor/models/file.js +++ b/editor/models/file.js @@ -67,7 +67,7 @@ var File = Model.extend({ // Check if a file is a buffer or exists isBuffer: function() { - return this.get("buffer") != null; + return _.isString(this.get("buffer")); }, // Test if a path is child @@ -111,7 +111,6 @@ var File = Model.extend({ var p; - if (this.isBuffer()) p = Q(hash.btoa(this.get("buffer"))); else { p = rpc.execute("fs/read", { @@ -131,16 +130,19 @@ var File = Model.extend({ }, // Write file content - write: function(content) { + write: function(content, opts) { var that = this; + opts = _.defaults(opts || {}, { + base64: false + }); - return Q() - .then(function() { - if (that.isBuffer()) return Q(that.set("buffer", content)); + return opts.base64? Q(content) : File.btoa(content) + .then(function(_content) { + if (that.isBuffer()) return Q(that.set("buffer", hash.atob(content))); return rpc.execute("fs/write", { 'path': that.get("path"), - 'content': hash.btoa(content) + 'content': content }); }) .then(function() { @@ -188,18 +190,18 @@ var File = Model.extend({ }, // Save file - save: function(content) { + save: function(content, opts) { var that = this; - return Q() - .then(function() { - if (!that.isBuffer() || !that.options.saveAsFile) return that.write(content); + return File.btoa(content) + .then(function(_content) { + if (!that.isBuffer() || !that.options.saveAsFile) return that.write(_content, { base64: true }); return dialogs.prompt("Save as:", that.get("name")) .then(function(_path) { return rpc.execute("fs/write", { 'path': _path, - 'content': hash.btoa(content), + 'content': _content, 'override': false }) .then(function() { @@ -221,7 +223,7 @@ var File = Model.extend({ buffer: function(name, content, id, options) { var f = new File(options || {}, { 'name': name, - 'buffer': content, + 'buffer': content || "", 'path': "buffer://"+(id || _.uniqueId("tmp")), 'directory': false }); @@ -249,6 +251,36 @@ var File = Model.extend({ .then(function(f) { return new File({}, f); }); + }, + + // Save as + saveAs: function(filename, content, opts) { + var f = File.buffer(filename); + return f.save(content, opts); + }, + + // Convert string or blob to base64 + btoa: function(b) { + var d = Q.defer(); + + if (b instanceof Blob) { + var reader = new window.FileReader(); + reader.readAsDataURL(b); + reader.onerror = function(err) { + d.reject(err); + } + reader.onloadend = function() { + d.resolve(reader.result); + }; + } else { + try { + d.resolve(hash.btoa(b)); + } catch (e) { + d.reject(e); + } + } + + return d.promise; } }); diff --git a/editor/utils/dialogs.js b/editor/utils/dialogs.js index 47dc812d..aff9513f 100644 --- a/editor/utils/dialogs.js +++ b/editor/utils/dialogs.js @@ -57,6 +57,7 @@ var openAlert = function(text, options) { }); }; var openErrorAlert = function(err) { + console.log("error", err); return openAlert("Error: "+(err.message || err)) .fin(function() { return Q.reject(err); @@ -123,6 +124,7 @@ var openSchema = function(schema, values) { }); }; + module.exports = { open: open, alert: openAlert, From f20b8cb84628d8ca9be95867f4634feb028afb51 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 14:40:53 -0500 Subject: [PATCH 053/101] Improve write of large file using File.write --- editor/models/file.js | 39 ++++++++++++++++++++----- editor/utils/upload.js | 2 +- lib/index.js | 65 ++++++++++++++++++++++++++++++++++++------ lib/services/fs.js | 18 ++++++++---- package.json | 8 ++++-- 5 files changed, 106 insertions(+), 26 deletions(-) diff --git a/editor/models/file.js b/editor/models/file.js index ada685ad..e273100d 100644 --- a/editor/models/file.js +++ b/editor/models/file.js @@ -1,3 +1,5 @@ +var path = require("path"); +var axios = require("axios"); var Q = require("q"); var _ = require("hr.utils"); var Model = require("hr.model"); @@ -136,13 +138,12 @@ var File = Model.extend({ base64: false }); - return opts.base64? Q(content) : File.btoa(content) + return (opts.base64? Q(content) : File.btoa(content)) .then(function(_content) { if (that.isBuffer()) return Q(that.set("buffer", hash.atob(content))); - return rpc.execute("fs/write", { - 'path': that.get("path"), - 'content': content + return File.writeContent(that.get("path"), _content, { + }); }) .then(function() { @@ -199,9 +200,7 @@ var File = Model.extend({ return dialogs.prompt("Save as:", that.get("name")) .then(function(_path) { - return rpc.execute("fs/write", { - 'path': _path, - 'content': _content, + return File.writeContent(_path, _content, { 'override': false }) .then(function() { @@ -281,6 +280,32 @@ var File = Model.extend({ } return d.promise; + }, + + // Write content (large or small) + writeContent: function(filename, content, opts) { + opts = _.extend({ + base64: true + }, opts || {}, { + path: filename + }); + + if (content.length > 1000) { + opts.path = path.dirname(filename); + + var data = new FormData(); + var blob = new Blob([content], { type: 'text/plain' }); + + _.each(opts, function(value, key) { + data.append(key, JSON.stringify(value)); + }); + data.append("content", blob, path.basename(filename)); + + return Q(axios.put('/rpc/fs/upload', data)); + } else { + opts.content = content; + return rpc.execute("fs/write", opts); + } } }); diff --git a/editor/utils/upload.js b/editor/utils/upload.js index 8a40d1f9..fb7757ca 100644 --- a/editor/utils/upload.js +++ b/editor/utils/upload.js @@ -154,7 +154,7 @@ var Uploader = Class.extend({ var formData = new FormData(); formData.append(filename, file); _.each(that.options.data, function(v, k) { - formData.append(k, v); + formData.append(k, JSON.stringify(v)); }); progress(0); diff --git a/lib/index.js b/lib/index.js index 69d1f597..82899add 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,13 +1,16 @@ var Q = require('q'); var _ = require('lodash'); var path = require('path'); +var os = require('os'); +var uuid = require('uuid'); +var fs = require('fs'); var http = require('http'); var express = require('express'); var bodyParser = require('body-parser'); var basicAuth = require('basic-auth-connect'); var cookieParser = require('cookie-parser'); var session = require('express-session'); -var multipart = require('connect-multiparty'); +var Busboy = require('busboy'); var configs = require('./configs'); var hooks = require('./hooks'); @@ -38,20 +41,64 @@ var prepare = function(config) { .then(_.partial(packages.init, config)) }; -var multipartMiddleware = multipart(); -var bodyParserMiddleware = bodyParser(); - var start = function(config) { var app = express(); var server = http.createServer(app); + // Parse form data app.use(function(req, res, next) { - if (req.method == "PUT") { - multipartMiddleware(req, res, next); - } else { - bodyParserMiddleware(req, res, next); - } + if ( + (req.headers['content-type'] || "").indexOf('multipart/form-data') < 0 + && req.method.toLowerCase() != "put" + ) return next(); + + var files = {}, fields = {}; + + var busboy = new Busboy({ + headers: req.headers + }); + busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { + var saveTo = path.join(os.tmpDir(), uuid.v4()); + file.pipe(fs.createWriteStream(saveTo)); + files[fieldname] = { + filename: filename, + path: saveTo, + mimetype: mimetype + }; + }); + busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) { + var pval; + + try { + pval = JSON.parse(val); + } catch (e) { + pval = val; + } + + if (fields[fieldname]) { + if (!_.isArray(fields[fieldname])) fields[fieldname] = [fields[fieldname]]; + fields[fieldname].push(pval); + } else { + fields[fieldname] = pval; + } + }); + busboy.on('finish', function() { + req.body = fields; + req._body = true; + req.files = files; + + res.on ('finish', function () { + _.each(req.files, function(file) { + try { fs.unlinkSync(file.path); } catch(e) {} + }); + req.files = {}; + }); + + next(); + }); + req.pipe(busboy); }); + app.use(bodyParser()); app.use(cookieParser()); // Auth diff --git a/lib/services/fs.js b/lib/services/fs.js index 75f165ce..6f872ef2 100644 --- a/lib/services/fs.js +++ b/lib/services/fs.js @@ -5,6 +5,7 @@ var path = require('path'); var wrench = require('wrench'); var stream = require('stream'); var mime = require('mime'); +var base64Stream = require('base64-stream'); var workspace = require('../workspace'); var base64 = require('../utils/base64'); @@ -118,8 +119,10 @@ var write = function(args) { 'createParent': true }); + var isStream = args.content instanceof stream.Readable; + if (!args.path || args.content == null) throw "Need 'path' and 'content'"; - if (args.base64) args.content = base64.atob(args.content); + if (args.base64 && !isStream) args.content = base64.atob(args.content); return workspace.path(args.path) .then(function(_path) { @@ -132,7 +135,7 @@ var write = function(args) { } // Write stream - if (args.content instanceof stream.Readable) { + if (isStream) { var d = Q.defer(); var writeStream = fs.createWriteStream(_path); @@ -144,7 +147,11 @@ var write = function(args) { d.reject(err); }); - args.content.pipe(writeStream); + var s = args.content; + + if (args.base64) s = s.pipe(base64Stream.decode()); + + s.pipe(writeStream); return d.promise; } else { @@ -217,13 +224,12 @@ var rename = function(args) { // Upload a file var upload = function(args, meta) { args.path = args.path || "."; - return _.reduce(meta.req.files, function(prev, file) { return prev .then(function() { return write(_.extend({}, args, { - 'path': path.join(args.path, file.originalFilename), - 'base64': false, + 'path': path.join(args.path, file.filename), + 'base64': args.base64? true : false, 'content': fs.createReadStream(file.path) })); }); diff --git a/package.json b/package.json index abc58a2b..efa36ee6 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,10 @@ "open": "0.0.5", "ini": "1.2.1", "basic-auth-connect": "1.0.0", - "connect-multiparty": "1.1.0", - "mime": "1.3.4" + "mime": "1.3.4", + "busboy": "0.2.9", + "uuid": "2.0.1", + "base64-stream": "0.1.2" }, "devDependencies": { "gulp": "^3.8.11", @@ -77,7 +79,7 @@ "mousetrap": "1.5.2", "moment": "2.9.0", "sockjs-client": "1.0.0-beta.12", - "axios": "0.5.2", + "axios": "0.5.4", "merge-stream": "0.1.7" }, "packageDependencies": { From c325ae1437db6f1d0793f8fbdbf9b4ff33a5f1a6 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 15:09:14 -0500 Subject: [PATCH 054/101] Improve File.write to support blob --- editor/models/file.js | 98 ++++++++++++++++++++++++------------------- editor/utils/hash.js | 96 +++++++++++++++++++----------------------- 2 files changed, 99 insertions(+), 95 deletions(-) diff --git a/editor/models/file.js b/editor/models/file.js index e273100d..13babebd 100644 --- a/editor/models/file.js +++ b/editor/models/file.js @@ -138,13 +138,16 @@ var File = Model.extend({ base64: false }); - return (opts.base64? Q(content) : File.btoa(content)) - .then(function(_content) { - if (that.isBuffer()) return Q(that.set("buffer", hash.atob(content))); - - return File.writeContent(that.get("path"), _content, { + return Q() + .then(function() { + if (that.isBuffer()) { + return File.blobToString(content) + .then(function(s) { + that.set("buffer", s); + }); + } - }); + return File.writeContent(that.get("path"), content); }) .then(function() { that.trigger("write", content); @@ -194,17 +197,17 @@ var File = Model.extend({ save: function(content, opts) { var that = this; - return File.btoa(content) - .then(function(_content) { - if (!that.isBuffer() || !that.options.saveAsFile) return that.write(_content, { base64: true }); + return Q() + .then(function() { + if (!that.isBuffer() || !that.options.saveAsFile) return that.write(content, opts); return dialogs.prompt("Save as:", that.get("name")) - .then(function(_path) { - return File.writeContent(_path, _content, { + .then(function(filename) { + return File.writeContent(filename, content, { 'override': false }) .then(function() { - return that.stat(_path); + return that.stat(filename); }) .fail(dialogs.error); }); @@ -258,54 +261,65 @@ var File = Model.extend({ return f.save(content, opts); }, - // Convert string or blob to base64 - btoa: function(b) { + // Convert blob to string + blobToString: function(b) { var d = Q.defer(); if (b instanceof Blob) { var reader = new window.FileReader(); - reader.readAsDataURL(b); reader.onerror = function(err) { d.reject(err); } - reader.onloadend = function() { + reader.onload = function() { d.resolve(reader.result); }; + reader.readAsText(b); } else { - try { - d.resolve(hash.btoa(b)); - } catch (e) { - d.reject(e); - } + d.resolve(b); } return d.promise; }, - // Write content (large or small) + // Write content to a file (blob, arraybuffer, string) writeContent: function(filename, content, opts) { - opts = _.extend({ - base64: true - }, opts || {}, { - path: filename + opts = _.defaults(opts || {}, { + base64: false }); + var useUpload = false; - if (content.length > 1000) { - opts.path = path.dirname(filename); - - var data = new FormData(); - var blob = new Blob([content], { type: 'text/plain' }); - - _.each(opts, function(value, key) { - data.append(key, JSON.stringify(value)); - }); - data.append("content", blob, path.basename(filename)); - - return Q(axios.put('/rpc/fs/upload', data)); - } else { - opts.content = content; - return rpc.execute("fs/write", opts); - } + return Q() + .then(function() { + if (_.isString(content)) { + if (opts.base64) return content; + + useUpload = (content.length > 1000); + opts.base64 = true; + return hash.btoa(content); + } else { + useUpload = true; + return content; + } + }) + .then(function(_content) { + if (useUpload) { + opts.path = path.dirname(filename); + + var data = new FormData(); + var blob = new Blob([_content]); + + _.each(opts, function(value, key) { + data.append(key, JSON.stringify(value)); + }); + data.append("content", blob, path.basename(filename)); + + return Q(axios.put('/rpc/fs/upload', data)); + } else { + opts.path = filename; + opts.content = _content; + return rpc.execute("fs/write", opts); + } + }); } }); diff --git a/editor/utils/hash.js b/editor/utils/hash.js index 833f94f5..bd6c53e8 100644 --- a/editor/utils/hash.js +++ b/editor/utils/hash.js @@ -1,77 +1,72 @@ var crc32table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; var utf8Encode = function (string) { - string = string.replace(/\r\n/g,"\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) { - - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } - return utftext; + return utftext; }; /** * Convert value as 8-bit unsigned integer to 2 digit hexadecimal number. */ -var hex8 = function(val) -{ - var n = val & 0xFF, - str = n.toString(16).toUpperCase() - ; +var hex8 = function(val) { + var n = val & 0xFF, + str = n.toString(16).toUpperCase() + ; - while(str.length < 2) - str = "0" + str; + while(str.length < 2) + str = "0" + str; - return str; + return str; }; /** * Convert value as 16-bit unsigned integer to 4 digit hexadecimal number. */ -var hex16 = function(val) -{ - return hex8(val >> 8) + hex8(val); +var hex16 = function(val) { + return hex8(val >> 8) + hex8(val); }; /** * Convert value as 32-bit unsigned integer to 8 digit hexadecimal number. */ -var hex32 = function(val) -{ - return hex16(val >> 16) + hex16(val); +var hex32 = function(val) { + return hex16(val >> 16) + hex16(val); }; var crc32= function(str) { - str = utf8Encode(str); + str = utf8Encode(str); - var crc = 0; - var x = 0; - var y = 0; + var crc = 0; + var x = 0; + var y = 0; - crc = crc ^ (-1); - for (var i = 0, iTop = str.length; i < iTop; i++) { - y = (crc ^ str.charCodeAt(i)) & 0xFF; - x = "0x" + crc32table.substr(y * 9, 8); - crc = (crc >>> 8) ^ x; - } + crc = crc ^ (-1); + for (var i = 0, iTop = str.length; i < iTop; i++) { + y = (crc ^ str.charCodeAt(i)) & 0xFF; + x = "0x" + crc32table.substr(y * 9, 8); + crc = (crc >>> 8) ^ x; + } - return (crc ^ (-1)).toString(); + return (crc ^ (-1)).toString(); }; /*\ @@ -85,7 +80,6 @@ var crc32= function(str) { /* Array of bytes to base64 string decoding */ function b64ToUint6 (nChr) { - return nChr > 64 && nChr < 91 ? nChr - 65 : nChr > 96 && nChr < 123 ? @@ -98,11 +92,9 @@ function b64ToUint6 (nChr) { 63 : 0; - } function base64DecToArr (sBase64, nBlocksSize) { - var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen); @@ -118,14 +110,12 @@ function base64DecToArr (sBase64, nBlocksSize) { } } - return taBytes; } /* Base64 string to array encoding */ function uint6ToB64 (nUint6) { - return nUint6 < 26 ? nUint6 + 65 : nUint6 < 52 ? @@ -138,11 +128,9 @@ function uint6ToB64 (nUint6) { 47 : 65; - } function base64EncArr (aBytes) { - var nMod3, sB64Enc = ""; for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { @@ -156,7 +144,6 @@ function base64EncArr (aBytes) { } return sB64Enc.replace(/A(?=A$|$)/g, "="); - } /* UTF-8 array to DOMString and vice versa */ @@ -255,5 +242,8 @@ module.exports = { }, 'btoa': function(s) { return base64EncArr(strToUTF8Arr(s)); + }, + 'base64': { + 'encodeArray': base64EncArr } }; From 779fe747847d4fb5ef70834f5cb3a9746f5a326c Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 16:26:21 -0500 Subject: [PATCH 055/101] Improve packages and commands error loading --- editor/models/command.js | 2 +- editor/models/package.js | 47 +++++++++++++++++++++++++++++++++------- package.json | 2 +- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/editor/models/command.js b/editor/models/command.js index cc10352f..f1933d81 100644 --- a/editor/models/command.js +++ b/editor/models/command.js @@ -78,7 +78,7 @@ var Command = Model.extend({ return that.get("run").apply(that, [ args || {}, that.collection.context, origin ]); }) .fail(function(err) { - logger.error("Command failed", err); + logger.exception("Command failed", err); }); }, diff --git a/editor/models/package.js b/editor/models/package.js index 3e124308..59d97560 100644 --- a/editor/models/package.js +++ b/editor/models/package.js @@ -4,6 +4,39 @@ var _ = require("hr.utils"); var Model = require("hr.model"); var logger = require("hr.logger")("package"); +function getScript(url, callback) { + var head = document.getElementsByTagName("head")[0]; + var script = document.createElement("script"); + script.src = url; + + // Handle Script loading + { + var done = false; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function(){ + if ( !done && (!this.readyState || + this.readyState == "loaded" || this.readyState == "complete") ) { + done = true; + if (callback) + callback(); + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + } + }; + + script.onerror = function(err) { + callback(err); + }; + } + + head.appendChild(script); + + // We handle everything using the script element injection + return undefined; +} + var Package = Model.extend({ defaults: { name: null, @@ -30,16 +63,14 @@ var Package = Model.extend({ if (!this.get("browser")) return Q(); logger.log("Load", this.get("name")); - $.getScript(this.url()+"/pkg-build.js") - .done(function(script, textStatus) { - d.resolve(); - }) - .fail(function(jqxhr, settings, exception) { - logger.error("Error loading plugin:", exception.stack || exception.message || exception); - d.reject(exception); + getScript(this.url()+"/pkg-build.js", function(err) { + if (!err) return d.resolve(); + + logger.exception("Error loading plugin:", err); + d.reject(err); }); - return d.promise.timeout(5000, "This addon took to long to load (> 5seconds)"); + return d.promise.timeout(10000, "This addon took to long to load (> 10seconds)"); }, /** diff --git a/package.json b/package.json index efa36ee6..7d7bcfc9 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "hr.class": "0.4.0", "hr.dnd": "0.2.0", "hr.gridview": "0.3.0", - "hr.logger": "0.2.0", + "hr.logger": "0.3.0", "hr.backend": "0.2.0", "hr.queue": "0.2.0", "octicons": "2.2.0", From 17dafc7f7a3e483e7886f9bc563ba0e075892847 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 16:40:23 -0500 Subject: [PATCH 056/101] Update critical error in hr.class --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d7bcfc9..ad7cc684 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "hr.model": "0.2.0", "hr.view": "0.2.0", "hr.collection": "0.2.0", - "hr.class": "0.4.0", + "hr.class": "0.4.1", "hr.dnd": "0.2.0", "hr.gridview": "0.3.0", "hr.logger": "0.3.0", From 186853581439f5b12b52a1476aa194c95cedf034 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 16 Apr 2015 21:54:31 -0500 Subject: [PATCH 057/101] Update hr.view and hr.class to fix inheritance --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ad7cc684..c4383140 100644 --- a/package.json +++ b/package.json @@ -67,9 +67,9 @@ "hr.list": "0.3.1", "hr.storage": "0.2.0", "hr.model": "0.2.0", - "hr.view": "0.2.0", + "hr.view": "1.0.0", "hr.collection": "0.2.0", - "hr.class": "0.4.1", + "hr.class": "1.2.1", "hr.dnd": "0.2.0", "hr.gridview": "0.3.0", "hr.logger": "0.3.0", From 6cbe71a93a2998307169a92aef29106a64976db5 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 09:27:32 -0500 Subject: [PATCH 058/101] Update hr.class --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c4383140..06629dd9 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "hr.model": "0.2.0", "hr.view": "1.0.0", "hr.collection": "0.2.0", - "hr.class": "1.2.1", + "hr.class": "1.2.2", "hr.dnd": "0.2.0", "hr.gridview": "0.3.0", "hr.logger": "0.3.0", From ad38e13d2c99a24a7c0565970156c900c63704ff Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 09:27:46 -0500 Subject: [PATCH 059/101] Add base for publish task --- gulpfile.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 1e9927bd..487bed1a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -68,11 +68,43 @@ gulp.task('styles', function() { // Clean output gulp.task('clean', function(cb) { del([ + '.tmp/**', 'build/**', 'packages/*/pkg-build.js' ], cb); }); -gulp.task('default', function(cb) { +// Build client code +gulp.task('build', function(cb) { runSequence('clean', 'dedupe', ['scripts', 'styles', 'html', 'assets'], cb); }); + +// Copy everything to .tmp +gulp.task('copy-tmp', function() { + return gulp.src([ + // Most files except the ones below + "./**", + + // Ignore gitignore + "!.gitignore", + + // Ignore dev related things + "!./tmp/**", + "!./.git/**", + "!./packages/**", + "!./editor/**", + '!./editor', + "!./node_modules/**", + '!./node_modules' + ]) + .pipe(gulp.dest('.tmp')); +}); + +// Publish to NPM +gulp.task('publish', function(cb) { + runSequence('clean', 'build', 'copy-tmp', cb); +}); + +gulp.task('default', function(cb) { + runSequence('build', cb); +}); From 81da5655919084a001a6caba0f135418e4175ee5 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 10:10:47 -0500 Subject: [PATCH 060/101] Use an external folder to store packages --- bin/codebox.js | 1 - lib/configs/default.js | 5 +++ lib/configs/local.js | 6 +++- lib/packages.js | 79 +++++++++++++++++++++++++++++++----------- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/bin/codebox.js b/bin/codebox.js index 1f3f6a56..1597f92c 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -44,7 +44,6 @@ var options = { } }; - codebox.start(options) .then(function() { if (program.email) return program.email; diff --git a/lib/configs/default.js b/lib/configs/default.js index 04240f26..9f2148d3 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -45,6 +45,11 @@ module.exports = function(options) { 'email': data.email }; }, + }, + + // Packages + 'packages': { + 'root': undefined } }, _.defaults); diff --git a/lib/configs/local.js b/lib/configs/local.js index 42dbcedc..60e0fe6d 100644 --- a/lib/configs/local.js +++ b/lib/configs/local.js @@ -11,7 +11,7 @@ var LOCAL_SETTINGS_DIR = path.join( '.codebox' ); -var SETTINGS_FILE = path.join(LOCAL_SETTINGS_DIR, 'settings.json') +var SETTINGS_FILE = process.env.WORKSPACE_CODEBOX_DIR || path.join(LOCAL_SETTINGS_DIR, 'settings.json') // Base structure for a local workspace // Store the workspace configuration in a file, ... @@ -48,6 +48,10 @@ module.exports = function(options) { } }); + options.packages = _.defaults(options.packages, { + 'root': process.env.WORKSPACE_ADDONS_DIR || path.resolve(LOCAL_SETTINGS_DIR, 'packages') + }); + // Create .codebox folder logger.log("Creating", LOCAL_SETTINGS_DIR); wrench.mkdirSyncRecursive(LOCAL_SETTINGS_DIR); diff --git a/lib/packages.js b/lib/packages.js index 8085d145..a0ccd2df 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -1,33 +1,36 @@ var Q = require("q"); var _ = require("lodash"); +var fs = require("fs"); var path = require("path"); +var wrench = require("wrench"); var Packager = require("pkgm"); var pkg = require("../package.json"); var events = require("./events"); var logger = require("./utils/logger")("packages"); -var context; -var manager = new Packager({ - 'engine': "codebox", - 'version': pkg.version, - 'folder': path.resolve(__dirname, "../packages"), - 'lessInclude': path.resolve(__dirname, "../editor/resources/stylesheets/variables.less") -}); - -manager.on("add", function(pkg) { - logger.log("add package", pkg.pkg.name); - events.emit("packages:add", pkg.infos()); -}); -manager.on("remove", function(pkg) { - logger.log("remove package", pkg.pkg.name); - events.emit("packages:remove", pkg.infos()); -}); -manager.on("log", function(log) { - logger[log.type].apply(logger, log.arguments); -}); - -var init = function() { +var context, manager; + +var init = function(config) { + manager = manager = new Packager({ + 'engine': "codebox", + 'version': pkg.version, + 'folder': config.packages.root, + 'lessInclude': path.resolve(__dirname, "../editor/resources/stylesheets/variables.less") + }); + + manager.on("add", function(pkg) { + logger.log("add package", pkg.pkg.name); + events.emit("packages:add", pkg.infos()); + }); + manager.on("remove", function(pkg) { + logger.log("remove package", pkg.pkg.name); + events.emit("packages:remove", pkg.infos()); + }); + manager.on("log", function(log) { + logger[log.type].apply(logger, log.arguments); + }); + context = { utils: _, promise: Q, @@ -42,6 +45,40 @@ var init = function() { logger.log("Load and prepare packages ("+_.size(pkg.packageDependencies)+" dependencies)"); return Q() + .then(function() { + var defaultPackagesRoot = path.resolve(__dirname, "../packages"); + if (defaultPackagesRoot == config.packages.root) return; + + // Copy default packages to the packages folder + var defaultPackages = fs.readdirSync(defaultPackagesRoot); + + // Create packages folder + wrench.mkdirSyncRecursive(config.packages.root); + + return _.each(defaultPackages, function(defaultPkg) { + var pkgPath = path.resolve(defaultPackagesRoot, defaultPkg); + var outPath = path.resolve(config.packages.root, defaultPkg); + + // Remove output if folder or symlink + try { + var stat = fs.lstatSync(outPath); + if (stat.isDirectory()) { + wrench.rmdirSyncRecursive(outPath); + } else { + fs.unlinkSync(outPath); + } + } catch (e) { + if (e.code != "ENOENT") throw e; + } + + // Create a new symlink + logger.log("symlink default package", defaultPkg); + fs.symlinkSync( + pkgPath, + outPath + ); + }); + }) .then(function() { return manager.prepare(pkg.packageDependencies); }) From c971b1eccd102e29efe342133abbc1669c81787f Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 10:55:40 -0500 Subject: [PATCH 061/101] Add utility to pre-install packages --- bin/codebox-pkg.js | 40 ++++++++++++++++++++++++++++++++++++++ lib/configs/default.js | 3 ++- lib/index.js | 2 +- lib/packages.js | 44 +++++++++++++++++++++++++++++++----------- lib/utils/logger.js | 5 ++++- package.json | 5 ++--- 6 files changed, 82 insertions(+), 17 deletions(-) create mode 100755 bin/codebox-pkg.js diff --git a/bin/codebox-pkg.js b/bin/codebox-pkg.js new file mode 100755 index 00000000..d1f738e7 --- /dev/null +++ b/bin/codebox-pkg.js @@ -0,0 +1,40 @@ +#! /usr/bin/env node + +var _ = require("lodash"); +var path = require("path"); +var program = require('commander'); + +var pkg = require("../package.json"); +var codebox = require("../lib"); + +program +.version(pkg.version) +.option('-r, --root [path]', 'Root folder to store packages') +.option('-p, --packages ', 'Comma separated list of packages to install', function (val) { + return _.chain(val.split(",")) + .compact() + .map(function(pkgref) { + var parts = pkgref.split(":"); + var name = _.first(parts); + var url = parts.slice(1).join(":"); + if (!name || !url) throw "Packages need to be formatted as name:url"; + + return [name,url]; + }) + .object() + .value() +}, []) +.parse(process.argv); + +codebox.prepare({ + packages: { + root: program.root, + install: program.packages + } +}) +.then(function() { + process.exit(0); +}) +.fail(function(err) { + console.log(err.stack || err.message || err); +}); \ No newline at end of file diff --git a/lib/configs/default.js b/lib/configs/default.js index 9f2148d3..15893800 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -49,7 +49,8 @@ module.exports = function(options) { // Packages 'packages': { - 'root': undefined + 'root': undefined, + 'install': {} } }, _.defaults); diff --git a/lib/index.js b/lib/index.js index 82899add..2f551c2b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -197,7 +197,7 @@ var start = function(config) { logger.error(err.stack || err); }); - return prepare(config) + return prepare(_.extend(config, { run: true })) .then(_.partial(socket.init, server, config)) .then(function() { logger.log(""); diff --git a/lib/packages.js b/lib/packages.js index a0ccd2df..a49f0eb8 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -11,6 +11,21 @@ var logger = require("./utils/logger")("packages"); var context, manager; +// Remove output if folder or symlink +function cleanFolder(outPath) { + try { + var stat = fs.lstatSync(outPath); + if (stat.isDirectory()) { + wrench.rmdirSyncRecursive(outPath); + } else { + fs.unlinkSync(outPath); + } + } catch (e) { + if (e.code != "ENOENT") throw e; + } +} + + var init = function(config) { manager = manager = new Packager({ 'engine': "codebox", @@ -45,6 +60,21 @@ var init = function(config) { logger.log("Load and prepare packages ("+_.size(pkg.packageDependencies)+" dependencies)"); return Q() + + // Keep package foler clean (only packages, ...) + .then(function() { + var packages = fs.readdirSync(config.packages.root); + + return _.each(packages, function(iPkg) { + var pkgPath = path.resolve(config.packages.root, iPkg); + + if (!fs.existsSync(path.resolve(pkgPath, "package.json"))) { + logger.warn("remove non-package", pkgPath); + cleanFolder(pkgPath); + } + }); + }) + .then(function() { var defaultPackagesRoot = path.resolve(__dirname, "../packages"); if (defaultPackagesRoot == config.packages.root) return; @@ -60,16 +90,7 @@ var init = function(config) { var outPath = path.resolve(config.packages.root, defaultPkg); // Remove output if folder or symlink - try { - var stat = fs.lstatSync(outPath); - if (stat.isDirectory()) { - wrench.rmdirSyncRecursive(outPath); - } else { - fs.unlinkSync(outPath); - } - } catch (e) { - if (e.code != "ENOENT") throw e; - } + cleanFolder(outPath); // Create a new symlink logger.log("symlink default package", defaultPkg); @@ -80,9 +101,10 @@ var init = function(config) { }); }) .then(function() { - return manager.prepare(pkg.packageDependencies); + return manager.prepare(_.extend(pkg.packageDependencies, config.packages.install || {})); }) .then(function() { + if (!config.run) return; return manager.runAll(context); }); }; diff --git a/lib/utils/logger.js b/lib/utils/logger.js index 93913bff..425bba7a 100644 --- a/lib/utils/logger.js +++ b/lib/utils/logger.js @@ -6,7 +6,8 @@ var enabled = true; // Colors for log types var colors = { 'log': ['\x1B[36m', '\x1B[39m'], - 'error': ['\x1B[31m', '\x1B[39m'] + 'error': ['\x1B[31m', '\x1B[39m'], + 'warn': ['\x1B[33m', '\x1B[39m'] }; // Base print method @@ -20,6 +21,7 @@ var print = function(logType, logSection) { var error = _.partial(print, 'error'); var log = _.partial(print, 'log'); +var warn = _.partial(print, 'warn'); var exception = _.wrap(error, function(func, logSection, err, kill) { func(logSection, err.message || err); if (err.stack) console.error(err.stack); @@ -44,6 +46,7 @@ module.exports = function(name) { return { 'log': _.partial(log, name), 'error': _.partial(error, name), + 'warn': _.partial(warn, name), 'exception': _.partial(exception, name) }; }; diff --git a/package.json b/package.json index 06629dd9..79bd7ba5 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "eventemitter2": "0.4.14", "crc": "0.2.1", "request": "2.37.0", - "commander": "2.3.0", + "commander": "2.8.0", "open": "0.0.5", "ini": "1.2.1", "basic-auth-connect": "1.0.0", @@ -99,8 +99,7 @@ "settings": "CodeboxIDE/package-settings", "menubar": "CodeboxIDE/package-menubar", "image": "CodeboxIDE/package-image#1.0.0", - "ctags": "CodeboxIDE/package-ctags", - "audio": "CodeboxIDE/package-audio" + "ctags": "CodeboxIDE/package-ctags" }, "scripts": { "test": "export TESTING=true; mocha --reporter list" From 93138000a0788b1d894dfae4d2abd370ca4d5488 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 10:56:29 -0500 Subject: [PATCH 062/101] Add codebox-pkg to bin scripts --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 79bd7ba5..45710417 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ } ], "bin": { - "codebox": "./bin/codebox.js" + "codebox": "./bin/codebox.js", + "codebox-pkg": "./bin/codebox-pkg.js" }, "dependencies": { "q": "~1.2.0", From 64ea738bc73b4864cff987dead48b17d981cfcec Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 11:12:03 -0500 Subject: [PATCH 063/101] Fix codebox-pkg to not use packages in module --- bin/codebox-pkg.js | 7 ++++--- lib/configs/default.js | 2 ++ lib/packages.js | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bin/codebox-pkg.js b/bin/codebox-pkg.js index d1f738e7..e1a81d6d 100755 --- a/bin/codebox-pkg.js +++ b/bin/codebox-pkg.js @@ -17,7 +17,7 @@ program var parts = pkgref.split(":"); var name = _.first(parts); var url = parts.slice(1).join(":"); - if (!name || !url) throw "Packages need to be formatted as name:url"; + if (!name || !url) throw "Packages need to be formatted as 'name:url'"; return [name,url]; }) @@ -28,8 +28,9 @@ program codebox.prepare({ packages: { - root: program.root, - install: program.packages + root: program.root? path.resolve(process.cwd(), program.root) : undefined, + install: program.packages, + defaults: null } }) .then(function() { diff --git a/lib/configs/default.js b/lib/configs/default.js index 15893800..843d1037 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -1,5 +1,6 @@ var _ = require('lodash'); var crc = require('crc'); +var path = require('path'); // Base structure for a configuration module.exports = function(options) { @@ -50,6 +51,7 @@ module.exports = function(options) { // Packages 'packages': { 'root': undefined, + 'defaults': path.resolve(__dirname, "../packages"), 'install': {} } }, _.defaults); diff --git a/lib/packages.js b/lib/packages.js index a49f0eb8..46197c97 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -76,8 +76,8 @@ var init = function(config) { }) .then(function() { - var defaultPackagesRoot = path.resolve(__dirname, "../packages"); - if (defaultPackagesRoot == config.packages.root) return; + var defaultPackagesRoot = config.packages.defaults; + if (!defaultPackagesRoot || defaultPackagesRoot == config.packages.root) return; // Copy default packages to the packages folder var defaultPackages = fs.readdirSync(defaultPackagesRoot); From 760e0adb436c7ec37f86ed25666098b15447c20f Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 11:34:05 -0500 Subject: [PATCH 064/101] Move codebox-pkg inside codebox --- README.md | 17 +----- bin/codebox-pkg.js | 41 -------------- bin/codebox.js | 136 +++++++++++++++++++++++++++++---------------- gulpfile.js | 16 +++++- package.json | 3 +- 5 files changed, 104 insertions(+), 109 deletions(-) delete mode 100755 bin/codebox-pkg.js diff --git a/README.md b/README.md index 31ca68c6..0496d267 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ $ npm install -g codebox And start the IDE from the command line: ``` -$ codebox --root=./myworkspace --open +$ codebox run ./myworkspace --open ``` Use this command to run and open Codebox IDE. By default, Codebox uses GIT to identify you, you can use the option ```--email=john.doe@gmail.com``` to define the email you want to use during GIT operations. @@ -53,21 +53,6 @@ Others comand line options are available and can be list with: ```codebox --help -p, --port [port] HTTP port ``` -#### Developing and testing packages - -Download and build the source code: - -``` -$ git clone https://github.com/CodeboxIDE/codebox.git -$ cd ./codebox -$ npm install . -$ grunt -``` - -Then you can easily link packages for testing by creating a folder that will contains all your packages (each should start with the prefix `package-`), then run the command `grunt link --origin=../mypackages`. This command will create symlinks between all the packages in `../mypackages` and the folder where are stored packages used by codebox. - -Everytime you update the code of your package, simply run `grunt resetPkg --pkg=mypackage` in it and restart codebox. - #### Need help? The IDE's documentation can be found at [help.codebox.io](http://help.codebox.io). Feel free to ask any questions or signal problems by adding issues. diff --git a/bin/codebox-pkg.js b/bin/codebox-pkg.js deleted file mode 100755 index e1a81d6d..00000000 --- a/bin/codebox-pkg.js +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env node - -var _ = require("lodash"); -var path = require("path"); -var program = require('commander'); - -var pkg = require("../package.json"); -var codebox = require("../lib"); - -program -.version(pkg.version) -.option('-r, --root [path]', 'Root folder to store packages') -.option('-p, --packages ', 'Comma separated list of packages to install', function (val) { - return _.chain(val.split(",")) - .compact() - .map(function(pkgref) { - var parts = pkgref.split(":"); - var name = _.first(parts); - var url = parts.slice(1).join(":"); - if (!name || !url) throw "Packages need to be formatted as 'name:url'"; - - return [name,url]; - }) - .object() - .value() -}, []) -.parse(process.argv); - -codebox.prepare({ - packages: { - root: program.root? path.resolve(process.cwd(), program.root) : undefined, - install: program.packages, - defaults: null - } -}) -.then(function() { - process.exit(0); -}) -.fail(function(err) { - console.log(err.stack || err.message || err); -}); \ No newline at end of file diff --git a/bin/codebox.js b/bin/codebox.js index 1597f92c..b220643f 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -10,66 +10,106 @@ var codebox = require("../lib"); var gitconfig = require('../lib/utils/gitconfig'); +function printError(err) { + console.log(err.stack || err.message || err); +} + program .version(pkg.version) -.option('-r, --root [path]', 'Root folder for the workspace, default is current directory', "./") -.option('-t, --templates [list]', 'Configuration templates, separated by commas', "") -.option('-p, --port [port]', 'HTTP port', 3000) -.option('-o, --open', 'Open the IDE in your favorite browser') -.option('-e, --email [email address]', 'Email address to use as a default authentication') -.option('-u, --users [list users]', 'List of coma seperated users and password (formatted as "username:password")'); - - -program.on('--help', function(){ +.on('--help', function(){ console.log(' Examples:'); console.log(''); - console.log(' $ codebox --root=./myfolder'); + console.log(' $ codebox ./myfolder'); console.log(''); }); -program.parse(process.argv); +//// Run Codebox +//// +program +.command('run [root]') +.description('run codebox') +.option('-t, --templates [list]', 'Configuration templates, separated by commas', "") +.option('-p, --port [port]', 'HTTP port', 3000) +.option('-o, --open', 'Open the IDE in your favorite browser') +.option('-e, --email [email address]', 'Email address to use as a default authentication') +.option('-u, --users [list users]', 'List of coma seperated users and password (formatted as "username:password")', function (val) { + return _.object(_.map((val || "").split(','), function(x) { + // x === 'username:password' + return x.split(':', 2); + })); +}, {}) +.action(function(root, opts) { + // Generate configration + var options = { + root: path.resolve(process.cwd(), root || "./"), + port: opts.port, + auth: { + users: opts.users + } + }; -// Parse auth users -var users = !program.users ? {} : _.object(_.map(program.users.split(','), function(x) { - // x === 'username:password' - return x.split(':', 2); -})); + codebox.start(options) + .then(function() { + if (program.email) return program.email; -// Generate configration -var options = { - root: path.resolve(process.cwd(), program.root), - port: program.port, - auth: { - users: users - } -}; + // Path to user's .gitconfig file + var configPath = path.join( + process.env.HOME, + '.gitconfig' + ); -codebox.start(options) -.then(function() { - if (program.email) return program.email; + // Codebox git repo: use to identify the user + return gitconfig(configPath) + .get("user") + .get("email") + .fail(function() { + return ""; + }); + }) + .then(function(email) { + var token = users[email] || Math.random().toString(36).substring(7); + var url = "http://localhost:"+program.port; - // Path to user's .gitconfig file - var configPath = path.join( - process.env.HOME, - '.gitconfig' - ); + console.log("\nCodebox is running at", url); - // Codebox git repo: use to identify the user - return gitconfig(configPath) - .get("user") - .get("email") - .fail(function() { - return ""; - }); -}) -.then(function(email) { - var token = users[email] || Math.random().toString(36).substring(7); - var url = "http://localhost:"+program.port; + if (program.open) open(url+"/?email="+email+"&token="+token); + }) + .fail(printError); +}); - console.log("\nCodebox is running at", url); +//// Install packages +//// +program +.command('install') +.description('pre-install packages') +.option('-r, --root [path]', 'Root folder to store packages') +.option('-p, --packages ', 'Comma separated list of packages to install', function (val) { + return _.chain(val.split(",")) + .compact() + .map(function(pkgref) { + var parts = pkgref.split(":"); + var name = _.first(parts); + var url = parts.slice(1).join(":"); + if (!name || !url) throw "Packages need to be formatted as 'name:url'"; - if (program.open) open(url+"/?email="+email+"&token="+token); -}) -.fail(function(err) { - console.log(err.stack || err.message || err); + return [name,url]; + }) + .object() + .value() +}, []) +.action(function(opts) { + codebox.prepare({ + packages: { + root: opts.root? path.resolve(process.cwd(), opts.root) : undefined, + install: opts.packages, + defaults: null + } + }) + .then(function() { + process.exit(0); + }) + .fail(printError); }); + +program.parse(process.argv); + diff --git a/gulpfile.js b/gulpfile.js index 487bed1a..02f6066f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,7 +10,14 @@ var rename = require('gulp-rename'); var uglify = require('gulp-uglify'); var stringify = require('stringify'); var merge = require('merge-stream'); -var exec = require('child_process').exec; +var child_process = require('child_process'); + +function exec(cmd, cb) { + var c = child_process.exec.apply(child_process, arguments); + c.stdout.pipe(process.stdout); + c.stderr.pipe(process.stderr); +} + // Compile Javascript gulp.task('scripts', function() { @@ -79,6 +86,11 @@ gulp.task('build', function(cb) { runSequence('clean', 'dedupe', ['scripts', 'styles', 'html', 'assets'], cb); }); +// Dedupe modules +gulp.task('preinstall-addons', function (cb) { + exec('./bin/codebox.js install --root=./.tmp/packages', cb); +}); + // Copy everything to .tmp gulp.task('copy-tmp', function() { return gulp.src([ @@ -102,7 +114,7 @@ gulp.task('copy-tmp', function() { // Publish to NPM gulp.task('publish', function(cb) { - runSequence('clean', 'build', 'copy-tmp', cb); + runSequence('clean', 'build', 'copy-tmp', 'preinstall-addons', cb); }); gulp.task('default', function(cb) { diff --git a/package.json b/package.json index 45710417..79bd7ba5 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,7 @@ } ], "bin": { - "codebox": "./bin/codebox.js", - "codebox-pkg": "./bin/codebox-pkg.js" + "codebox": "./bin/codebox.js" }, "dependencies": { "q": "~1.2.0", From 889702edd625761efce63584651d1d17fd4eec26 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 11:34:37 -0500 Subject: [PATCH 065/101] Fix default folder for packages --- lib/configs/default.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configs/default.js b/lib/configs/default.js index 843d1037..8230ab9e 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -51,7 +51,7 @@ module.exports = function(options) { // Packages 'packages': { 'root': undefined, - 'defaults': path.resolve(__dirname, "../packages"), + 'defaults': path.resolve(__dirname, "../../packages"), 'install': {} } }, _.defaults); From bcf222cdd4e1eab6235426a7abab1aa7f412492a Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 11:36:12 -0500 Subject: [PATCH 066/101] By default print help --- bin/codebox.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/codebox.js b/bin/codebox.js index b220643f..6975305d 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -19,7 +19,7 @@ program .on('--help', function(){ console.log(' Examples:'); console.log(''); - console.log(' $ codebox ./myfolder'); + console.log(' $ codebox run ./myfolder'); console.log(''); }); @@ -113,3 +113,6 @@ program program.parse(process.argv); +if (!process.argv.slice(2).length) { + program.outputHelp(); +} From 7e2aba8527cf5382b933bf930c04e00958005fac Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 11:39:54 -0500 Subject: [PATCH 067/101] Fix open after running codebox --- bin/codebox.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/codebox.js b/bin/codebox.js index 6975305d..2669ba9c 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -67,8 +67,8 @@ program }); }) .then(function(email) { - var token = users[email] || Math.random().toString(36).substring(7); - var url = "http://localhost:"+program.port; + var token = opts.users[email] || Math.random().toString(36).substring(7); + var url = "http://localhost:"+opts.port; console.log("\nCodebox is running at", url); From 2c9c31d8ca2d861656f13c2f4215479b46033a28 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 11:44:32 -0500 Subject: [PATCH 068/101] Adapt tests to use tmp folder for packages --- test/helper.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/helper.js b/test/helper.js index 5fd4c82d..15db0a34 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,12 +1,16 @@ var Q = require("q"); var chai = require("chai"); var path = require("path"); +var os = require("os"); var codebox = require("../lib"); var users = require("../lib/users"); var config = { log: false, - root: path.resolve(__dirname, "workspace") + root: path.resolve(__dirname, "workspace"), + packages: { + root: path.resolve(os.tmpdir()) + } }; // Expose assert globally From 4ea9477d1bb0c2c03fd35c68f5e1fe8be832e239 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 11:54:54 -0500 Subject: [PATCH 069/101] Fix acces to packages --- gulpfile.js | 2 -- lib/index.js | 20 ++++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 02f6066f..8ecd2cfe 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -104,8 +104,6 @@ gulp.task('copy-tmp', function() { "!./tmp/**", "!./.git/**", "!./packages/**", - "!./editor/**", - '!./editor', "!./node_modules/**", '!./node_modules' ]) diff --git a/lib/index.js b/lib/index.js index 2f551c2b..945697ea 100644 --- a/lib/index.js +++ b/lib/index.js @@ -24,6 +24,14 @@ var logging = require('./utils/logger'); var logger = logging("main"); +var _middleware = function(fn) { + var __middleware; + return function(req, res, next) { + if (!__middleware) __middleware = fn(); + return __middleware.apply(this, arguments); + }; +} + var prepare = function(config) { return Q() .then(_.partial(logging.init, config)) @@ -134,7 +142,9 @@ var start = function(config) { // Static files app.use('/', express.static(path.resolve(__dirname, '../build'))); - app.use('/packages', express.static(path.resolve(__dirname, '../packages'))); + app.use('/packages', _middleware(function() { + return express.static(config.packages.root); + })); // Auth app.use(function(req, res, next) { @@ -163,11 +173,9 @@ var start = function(config) { app.use('/rpc', rpc.router); // Fs direct access - var _fsmiddleware; - app.use('/fs', function(req, res, next) { - if (!_fsmiddleware) _fsmiddleware = express.static(workspace.root()); - return _fsmiddleware.apply(this, arguments); - }); + app.use('/fs', _middleware(function() { + return express.static(workspace.root()); + })); // Error handling app.use(function(req, res, next) { From 73017a4dd55a9ad810fe77e49a7a2ecd0e6f2790 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 12:00:03 -0500 Subject: [PATCH 070/101] Fix tests by using local packages folder --- .gitignore | 3 +++ test/helper.js | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d1d04b1d..1171d6d8 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ build # Tmp directory .tmp + +# Packages for testing +test/packages \ No newline at end of file diff --git a/test/helper.js b/test/helper.js index 15db0a34..ffd4a3e5 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,15 +1,21 @@ var Q = require("q"); var chai = require("chai"); var path = require("path"); +var wrench = require("wrench"); var os = require("os"); var codebox = require("../lib"); var users = require("../lib/users"); +var packagesFolder = path.resolve(__dirname, "./packages"); +try { + wrench.mkdirSyncRecursive(packagesFolder, 0777); +} catch (e) {} + var config = { log: false, root: path.resolve(__dirname, "workspace"), packages: { - root: path.resolve(os.tmpdir()) + root: packagesFolder } }; From 017088d6fb1419b22b655a7b4c5f7388b186853d Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 12:01:31 -0500 Subject: [PATCH 071/101] Add command to npm publish --- gulpfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 8ecd2cfe..6fdcec9c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -111,8 +111,8 @@ gulp.task('copy-tmp', function() { }); // Publish to NPM -gulp.task('publish', function(cb) { - runSequence('clean', 'build', 'copy-tmp', 'preinstall-addons', cb); +gulp.task('publish', 'clean', 'build', 'copy-tmp', 'preinstall-addons', function(cb) { + exec('cd ./.tmp && npm publish', cb); }); gulp.task('default', function(cb) { From d41a3bdedcd5bd24f1aebfc412fd672fcb8a6c41 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 12:01:50 -0500 Subject: [PATCH 072/101] Bump version to 1.0.0-alpha.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79bd7ba5..c5c18fe6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codebox", "description": "Extensible hybrid IDE", - "version": "1.0.0", + "version": "1.0.0-alpha.1", "author": "FriendCode Inc. ", "license": "Apache 2", "preferGlobal": true, From 8491324d52354bb2fbf2dedd84041042e456f9c3 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 12:04:47 -0500 Subject: [PATCH 073/101] Fix publish task --- gulpfile.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 6fdcec9c..44374984 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -111,8 +111,11 @@ gulp.task('copy-tmp', function() { }); // Publish to NPM -gulp.task('publish', 'clean', 'build', 'copy-tmp', 'preinstall-addons', function(cb) { - exec('cd ./.tmp && npm publish', cb); +gulp.task('publish', function(cb) { + runSequence('clean', 'build', 'copy-tmp', 'preinstall-addons', function(err) { + if (err) return cb(err); + exec('cd ./.tmp && npm publish', cb); + }); }); gulp.task('default', function(cb) { From 5c08dd8a2d3e7bf16d8e93de6c38185aa0c3037a Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 12:12:32 -0500 Subject: [PATCH 074/101] Don't copy test folder in publish task --- gulpfile.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 44374984..0980920a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -105,7 +105,10 @@ gulp.task('copy-tmp', function() { "!./.git/**", "!./packages/**", "!./node_modules/**", - '!./node_modules' + '!./node_modules', + "!./test/**", + '!./test', + ]) .pipe(gulp.dest('.tmp')); }); From 01bac5d18ed0a0dcc66c6c06f9dba9073584f729 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 12:14:05 -0500 Subject: [PATCH 075/101] Exit process when command failed --- bin/codebox.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/codebox.js b/bin/codebox.js index 2669ba9c..6a8fb922 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -12,6 +12,7 @@ var gitconfig = require('../lib/utils/gitconfig'); function printError(err) { console.log(err.stack || err.message || err); + process.exit(1); } program From 17b2adf77cacec864a8feb2f65610cb680872cb6 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 13:08:34 -0500 Subject: [PATCH 076/101] Don't fix image package version --- README.md | 2 -- package.json | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 0496d267..52f8fab9 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ [![Build Status](https://travis-ci.org/CodeboxIDE/codebox.png?branch=master)](https://travis-ci.org/CodeboxIDE/codebox) [![NPM version](https://badge.fury.io/js/codebox.svg)](http://badge.fury.io/js/codebox) -#### :warning: Instructions are for the not yet published version 1.0.0 - Codebox is a complete and modular Cloud IDE. It can run on any unix-like machine (Linux, Mac OS X). It is an open source component of [codebox.io](https://www.codebox.io) (Cloud IDE as a Service). The IDE can run on your desktop (Linux or Mac), on your server or the cloud. You can use the [codebox.io](https://www.codebox.io) service to host and manage IDE instances. diff --git a/package.json b/package.json index c5c18fe6..f522ff59 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "git": "CodeboxIDE/package-git", "settings": "CodeboxIDE/package-settings", "menubar": "CodeboxIDE/package-menubar", - "image": "CodeboxIDE/package-image#1.0.0", + "image": "CodeboxIDE/package-image", "ctags": "CodeboxIDE/package-ctags" }, "scripts": { From ac54d4fb30f5659e01e004c89770a748227ebb14 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 13:29:07 -0500 Subject: [PATCH 077/101] Separate pre-publish task --- gulpfile.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 0980920a..e6e6a20d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -114,11 +114,11 @@ gulp.task('copy-tmp', function() { }); // Publish to NPM -gulp.task('publish', function(cb) { - runSequence('clean', 'build', 'copy-tmp', 'preinstall-addons', function(err) { - if (err) return cb(err); - exec('cd ./.tmp && npm publish', cb); - }); +gulp.task('pre-publish', function(cb) { + runSequence('clean', 'build', 'copy-tmp', 'preinstall-addons', cb); +}); +gulp.task('publish', ['pre-publish'], function(cb) { + exec('cd ./.tmp && npm publish', cb); }); gulp.task('default', function(cb) { From 9798a4504c39d2c52acc1540604a638f5de5d925 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 13:33:58 -0500 Subject: [PATCH 078/101] Accept env PORT --- lib/configs/default.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configs/default.js b/lib/configs/default.js index 8230ab9e..0a2a7f93 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -6,7 +6,7 @@ var path = require('path'); module.exports = function(options) { options = _.merge(options, { // Port for running the webserver - 'port': 3000, + 'port': process.env.PORT || 3000, // Root folder 'root': process.cwd(), From ad7875a9d60b047cc9e20964d8c030f00b5dc7a0 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 13:41:35 -0500 Subject: [PATCH 079/101] Send static files without auth --- lib/index.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/index.js b/lib/index.js index 945697ea..4f75bd85 100644 --- a/lib/index.js +++ b/lib/index.js @@ -127,6 +127,11 @@ var start = function(config) { next(); } }); + + // Static files + app.use('/', express.static(path.resolve(__dirname, '../build'))); + + // Auth app.use(function(req, res, next) { var doAuth = basicAuth(function(user, pass, fn){ users.auth(user, pass) @@ -139,14 +144,6 @@ var start = function(config) { if (req.session.userId || !config.auth.basic) return next(); doAuth(req, res, next); }); - - // Static files - app.use('/', express.static(path.resolve(__dirname, '../build'))); - app.use('/packages', _middleware(function() { - return express.static(config.packages.root); - })); - - // Auth app.use(function(req, res, next) { if (req.user) { req.session.userId = req.user.id; @@ -169,6 +166,11 @@ var start = function(config) { } }); + // Download packages + app.use('/packages', _middleware(function() { + return express.static(config.packages.root); + })); + // RPC services app.use('/rpc', rpc.router); From 91baf08c046e65a56cc8570d9152129dcf318f18 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 13:45:05 -0500 Subject: [PATCH 080/101] Mimify js for production --- gulpfile.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index e6e6a20d..f7e29320 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,22 +12,25 @@ var stringify = require('stringify'); var merge = require('merge-stream'); var child_process = require('child_process'); +var debug = !!process.env.DEBUG; + function exec(cmd, cb) { var c = child_process.exec.apply(child_process, arguments); c.stdout.pipe(process.stdout); c.stderr.pipe(process.stderr); } - // Compile Javascript gulp.task('scripts', function() { - return gulp.src('editor/main.js') + var out = gulp.src('editor/main.js') .pipe(browserify({ debug: false, transform: ['stringify', 'require-globify'] - })) - //.pipe(uglify()) - .pipe(rename('application.js')) + })); + + if (!debug) out = out.pipe(uglify()) + + return out.pipe(rename('application.js')) .pipe(gulp.dest('./build/static/js')); }); From d90ab70eb9cb7cef5559dd942a8ab6a4d27eeb11 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 13:59:20 -0500 Subject: [PATCH 081/101] Uglify packages if debug is false Update pkgm@3.2.0 --- lib/configs/default.js | 3 +++ lib/packages.js | 3 ++- package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/configs/default.js b/lib/configs/default.js index 0a2a7f93..ec9204a7 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -5,6 +5,9 @@ var path = require('path'); // Base structure for a configuration module.exports = function(options) { options = _.merge(options, { + // Debug + 'debug': !!process.env.DEBUG, + // Port for running the webserver 'port': process.env.PORT || 3000, diff --git a/lib/packages.js b/lib/packages.js index 46197c97..43cd3314 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -31,7 +31,8 @@ var init = function(config) { 'engine': "codebox", 'version': pkg.version, 'folder': config.packages.root, - 'lessInclude': path.resolve(__dirname, "../editor/resources/stylesheets/variables.less") + 'lessInclude': path.resolve(__dirname, "../editor/resources/stylesheets/variables.less"), + 'uglify': !config.debug }); manager.on("add", function(pkg) { diff --git a/package.json b/package.json index f522ff59..f8a41c52 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "dependencies": { "q": "~1.2.0", "lodash": "2.4.1", - "pkgm": "3.1.0", + "pkgm": "3.2.0", "express": "4.6.1", "express-session": "1.7.0", "wrench": "1.5.8", From 2bdadb987baf7b2295cce558cbfa0d653008b2b5 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 17:15:52 -0500 Subject: [PATCH 082/101] Catch uncaught exception in promises --- editor/main.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/editor/main.js b/editor/main.js index 461f2560..acc04faf 100644 --- a/editor/main.js +++ b/editor/main.js @@ -4,6 +4,10 @@ var Q = require("q"); var logger = require("hr.logger")("app"); +Q.onerror = function (error) { + logger.exception("Uncaught Error:", error); +}; + var app = require("./core/application"); var commands = require("./core/commands"); var packages = require("./core/packages"); From 39a2588d17355651c81a59f803f15797e819d379 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Fri, 17 Apr 2015 20:17:12 -0500 Subject: [PATCH 083/101] Fix issue on happyrhino by updating hr.class --- editor/core/application.js | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/editor/core/application.js b/editor/core/application.js index 08eb893f..752ecb85 100644 --- a/editor/core/application.js +++ b/editor/core/application.js @@ -14,7 +14,7 @@ var CodeboxApplication = Application.extend({ routes: {}, initialize: function() { - Application.__super__.initialize.apply(this, arguments); + CodeboxApplication.__super__.initialize.apply(this, arguments); this.grid = new GridView({ columns: 10 @@ -33,4 +33,4 @@ var CodeboxApplication = Application.extend({ }, }); -module.exports = new CodeboxApplication(); +module.exports = new CodeboxApplication(); diff --git a/package.json b/package.json index f8a41c52..283ea25b 100644 --- a/package.json +++ b/package.json @@ -67,9 +67,9 @@ "hr.list": "0.3.1", "hr.storage": "0.2.0", "hr.model": "0.2.0", - "hr.view": "1.0.0", + "hr.view": "1.0.1", "hr.collection": "0.2.0", - "hr.class": "1.2.2", + "hr.class": "1.2.3", "hr.dnd": "0.2.0", "hr.gridview": "0.3.0", "hr.logger": "0.3.0", From 3e5ba13e5a644d08b854149951618cb7098912f7 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Sun, 19 Apr 2015 18:59:57 -0500 Subject: [PATCH 084/101] Fix events reporting using hook --- lib/configs/default.js | 2 +- lib/configs/local.js | 4 ++-- lib/events.js | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/configs/default.js b/lib/configs/default.js index ec9204a7..9657a571 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -22,7 +22,7 @@ module.exports = function(options) { // Events reporting 'reporting': { - 'timeout': 180 * 1e3 + 'timeout': 180 }, // Authentication settings diff --git a/lib/configs/local.js b/lib/configs/local.js index 60e0fe6d..bfdeb40e 100644 --- a/lib/configs/local.js +++ b/lib/configs/local.js @@ -6,12 +6,12 @@ var wrench = require('wrench'); var logger = require("../utils/logger")("local"); -var LOCAL_SETTINGS_DIR = path.join( +var LOCAL_SETTINGS_DIR = process.env.WORKSPACE_CODEBOX_DIR || path.join( process.env.HOME, '.codebox' ); -var SETTINGS_FILE = process.env.WORKSPACE_CODEBOX_DIR || path.join(LOCAL_SETTINGS_DIR, 'settings.json') +var SETTINGS_FILE = path.join(LOCAL_SETTINGS_DIR, 'settings.json'); // Base structure for a local workspace // Store the workspace configuration in a file, ... diff --git a/lib/events.js b/lib/events.js index 5eb2e64c..fb9ed962 100644 --- a/lib/events.js +++ b/lib/events.js @@ -38,7 +38,7 @@ var init = function(config) { var eventQueue = []; // Send events and empty queue - var sendEvents = _.debounce(function sendEvents() { + var sendEvents = _.debounce(function() { logger.log("report", _.size(eventQueue), "events"); // Hit hook @@ -48,7 +48,7 @@ var init = function(config) { eventQueue = []; }, timeout); - var queueEvent = function queueEvent(eventData) { + var queueEvent = function(eventData) { eventQueue.push(eventData); }; @@ -74,7 +74,7 @@ var init = function(config) { sendEvents(); }); - logger.log("events are ready"); + logger.log("events are ready, reporting is debounced to", (timeout/1000).toFixed(0)+"s"); }; From 89b0f1feb006c039640ae612ae11ce346341d3fb Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 20 Apr 2015 07:33:35 -0500 Subject: [PATCH 085/101] Fix display of port where codebox is running --- bin/codebox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/codebox.js b/bin/codebox.js index 6a8fb922..4710a5d6 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -69,7 +69,7 @@ program }) .then(function(email) { var token = opts.users[email] || Math.random().toString(36).substring(7); - var url = "http://localhost:"+opts.port; + var url = "http://localhost:"+options.port; console.log("\nCodebox is running at", url); From 4b8fecb5643165b864d6c3be39b8d6b610c8d65b Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 20 Apr 2015 08:49:35 -0500 Subject: [PATCH 086/101] Use env mapping to extend configs --- lib/configs/default.js | 7 +++-- lib/configs/env.js | 50 ++++++++++++++++++++++++++++++ lib/configs/index.js | 5 +-- lib/configs/local.js | 69 +++++++++++++++++++----------------------- 4 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 lib/configs/env.js diff --git a/lib/configs/default.js b/lib/configs/default.js index 9657a571..3ce54b37 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -6,10 +6,10 @@ var path = require('path'); module.exports = function(options) { options = _.merge(options, { // Debug - 'debug': !!process.env.DEBUG, + 'debug': false, // Port for running the webserver - 'port': process.env.PORT || 3000, + 'port': 3000, // Root folder 'root': process.cwd(), @@ -49,6 +49,9 @@ module.exports = function(options) { 'email': data.email }; }, + 'events': undefined, + 'settings.get': undefined, + 'settings.set': undefined }, // Packages diff --git a/lib/configs/env.js b/lib/configs/env.js new file mode 100644 index 00000000..cd985051 --- /dev/null +++ b/lib/configs/env.js @@ -0,0 +1,50 @@ +var _ = require('lodash'); + +var alias = { + 'PORT': 'CODEBOX_PORT' +} + +var parseEnv = function (env, parent, prefix) { + var k, v, envVar, parsedEnv; + + if (!parent) { + parent = {}; + } + + for(k in parent) { + v = parent[k]; + + envVar = prefix? prefix + '_': ''; + envVar += k.toUpperCase(); + + if (_.isObject(v) && !_.isArray(v) && !_.isFunction(v)) { + parseEnv(env, v, envVar); + } + else { + if (envVar in env) { + if (_.isArray(v) && (v.length == 0 || _.isString(v[0]))) { + parent[k] = _.compact(env[envVar].split(",")); + } else if (_.isNumber(v)) { + parent[k] = parseInt(env[envVar]); + } else if (_.isBoolean(v)) { + parent[k] = (env[envVar] == "false"? false : true); + } else { + parent[k] = env[envVar] + } + } + } + } + + return parent; +}; + + +// Extend configuration with environment variables +module.exports = function(options) { + var env = _.clone(process.env); + _.each(alias, function(to, from) { + env[to] = env[from] || env[to]; + }) + + return parseEnv(env, options, 'CODEBOX'); +}; \ No newline at end of file diff --git a/lib/configs/index.js b/lib/configs/index.js index c6f23484..5574c50c 100644 --- a/lib/configs/index.js +++ b/lib/configs/index.js @@ -3,12 +3,13 @@ var Q = require('q'); var TEMPLATES = { 'default': require('./default'), - 'local': require('./local') + 'local': require('./local'), + 'env': require('./env') }; // Generate a complete config from templates module.exports = function(options) { - var templates = _.unique(["default"].concat((options.templates || "local").split(","))); + var templates = _.unique(["default"].concat((options.templates || "local,env").split(","))); return _.reduce(templates, function(prev, template) { return prev.then(function(_options) { diff --git a/lib/configs/local.js b/lib/configs/local.js index bfdeb40e..e36e1621 100644 --- a/lib/configs/local.js +++ b/lib/configs/local.js @@ -6,51 +6,44 @@ var wrench = require('wrench'); var logger = require("../utils/logger")("local"); -var LOCAL_SETTINGS_DIR = process.env.WORKSPACE_CODEBOX_DIR || path.join( - process.env.HOME, - '.codebox' -); - +var LOCAL_SETTINGS_DIR = process.env.CODEBOX_LOCAL_FOLDER || path.join(process.env.HOME,'.codebox') var SETTINGS_FILE = path.join(LOCAL_SETTINGS_DIR, 'settings.json'); // Base structure for a local workspace // Store the workspace configuration in a file, ... module.exports = function(options) { - options = _.defaults(options, { - - }); - - options.hooks = _.defaults(options.hooks, { - 'settings.get': function(args) { - return Q.nfcall(fs.readFile, SETTINGS_FILE, "utf-8") - .then(JSON.parse) - .fail(_.constant({})) - .then(function(config) { - if (!config[options.id]) config[options.id] = {}; - return config[options.id][args.user] || {}; - }); + options = _.merge(options, { + 'hooks': { + 'settings.get': function(args) { + return Q.nfcall(fs.readFile, SETTINGS_FILE, "utf-8") + .then(JSON.parse) + .fail(_.constant({})) + .then(function(config) { + if (!config[options.id]) config[options.id] = {}; + return config[options.id][args.user] || {}; + }); + }, + + 'settings.set': function(args) { + return Q.nfcall(fs.readFile, SETTINGS_FILE, "utf-8") + .then(JSON.parse) + .fail(_.constant({})) + .then(function(config) { + if (!config[options.id]) config[options.id] = {}; + config[options.id][args.user] = args.settings; + + return Q.nfcall(fs.writeFile, SETTINGS_FILE, JSON.stringify(config)) + .thenResolve(config); + }) + .then(function(config) { + return config[options.id][args.user] || {}; + }); + } }, - - 'settings.set': function(args) { - return Q.nfcall(fs.readFile, SETTINGS_FILE, "utf-8") - .then(JSON.parse) - .fail(_.constant({})) - .then(function(config) { - if (!config[options.id]) config[options.id] = {}; - config[options.id][args.user] = args.settings; - - return Q.nfcall(fs.writeFile, SETTINGS_FILE, JSON.stringify(config)) - .thenResolve(config); - }) - .then(function(config) { - return config[options.id][args.user] || {}; - }); + packages:{ + 'root': path.resolve(LOCAL_SETTINGS_DIR, 'packages') } - }); - - options.packages = _.defaults(options.packages, { - 'root': process.env.WORKSPACE_ADDONS_DIR || path.resolve(LOCAL_SETTINGS_DIR, 'packages') - }); + }, _.defaults); // Create .codebox folder logger.log("Creating", LOCAL_SETTINGS_DIR); From d912082bc4fe22d5b817fb3cbb863823daeb27d8 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 20 Apr 2015 09:06:27 -0500 Subject: [PATCH 087/101] Force debug to false when publishing --- gulpfile.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index f7e29320..59401bf9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -91,7 +91,11 @@ gulp.task('build', function(cb) { // Dedupe modules gulp.task('preinstall-addons', function (cb) { - exec('./bin/codebox.js install --root=./.tmp/packages', cb); + exec('./bin/codebox.js install --root=./.tmp/packages', { + env: _.extend({}, process.env, { + CODEBOX_DEBUG: debug + }) + }, cb); }); // Copy everything to .tmp @@ -118,6 +122,7 @@ gulp.task('copy-tmp', function() { // Publish to NPM gulp.task('pre-publish', function(cb) { + debug = false; runSequence('clean', 'build', 'copy-tmp', 'preinstall-addons', cb); }); gulp.task('publish', ['pre-publish'], function(cb) { From ab40da2a9887eb18a1088d211a74645de90f0123 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 20 Apr 2015 09:22:58 -0500 Subject: [PATCH 088/101] Bump version to 1.0.0-alpha.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 283ea25b..2b37f4f5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codebox", "description": "Extensible hybrid IDE", - "version": "1.0.0-alpha.1", + "version": "1.0.0-alpha.2", "author": "FriendCode Inc. ", "license": "Apache 2", "preferGlobal": true, From fdb08f59ad098bb46cfcef3d0cacc4d9172c3b46 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 20 Apr 2015 09:51:41 -0500 Subject: [PATCH 089/101] Fix default port --- lib/configs/env.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/configs/env.js b/lib/configs/env.js index cd985051..361aa312 100644 --- a/lib/configs/env.js +++ b/lib/configs/env.js @@ -43,8 +43,8 @@ var parseEnv = function (env, parent, prefix) { module.exports = function(options) { var env = _.clone(process.env); _.each(alias, function(to, from) { - env[to] = env[from] || env[to]; - }) + if (env[from]) env[to] = env[from] || env[to]; + }); return parseEnv(env, options, 'CODEBOX'); }; \ No newline at end of file From dff8f7b1345a8c557d43111189316ba6b8a95572 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 20 Apr 2015 11:26:12 -0500 Subject: [PATCH 090/101] Send boxid to webhook --- lib/hooks.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/hooks.js b/lib/hooks.js index 2a0cf127..540e739a 100644 --- a/lib/hooks.js +++ b/lib/hooks.js @@ -4,6 +4,7 @@ var request = require('request'); var logger = require("./utils/logger")("hooks"); +var BOXID = null; var HOOKS = {}; var POSTHOOKS = { 'users.auth': function(data) { @@ -36,6 +37,7 @@ var use = function(hook, data) { // Do http requests request.post(handler, { 'body': { + 'id': BOXID, 'data': data, 'hook': hook }, @@ -74,6 +76,7 @@ var use = function(hook, data) { var init = function(options) { logger.log("init hooks"); + BOXID = options.id; HOOKS = options.hooks; SECRET_TOKEN = options.secret; }; From a0377d7bafc16da35a06256f55f080b37d53c1cd Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Mon, 20 Apr 2015 15:52:03 -0500 Subject: [PATCH 091/101] Use 0777 when creating .codebox folder --- lib/configs/local.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configs/local.js b/lib/configs/local.js index e36e1621..7da404b8 100644 --- a/lib/configs/local.js +++ b/lib/configs/local.js @@ -47,7 +47,7 @@ module.exports = function(options) { // Create .codebox folder logger.log("Creating", LOCAL_SETTINGS_DIR); - wrench.mkdirSyncRecursive(LOCAL_SETTINGS_DIR); + wrench.mkdirSyncRecursive(LOCAL_SETTINGS_DIR, 0777); return options; }; From 99f53a6c66e04c96457a23eab2adce52ebaa7e6c Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Tue, 21 Apr 2015 16:39:43 -0500 Subject: [PATCH 092/101] Bump version to 1.0.0-alpha.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b37f4f5..0c033f19 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codebox", "description": "Extensible hybrid IDE", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "author": "FriendCode Inc. ", "license": "Apache 2", "preferGlobal": true, From 0fb4129af8d15f7dd0d60cae73ac1a7c3fc97764 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Wed, 22 Apr 2015 09:06:44 -0500 Subject: [PATCH 093/101] Add title for workspace --- editor/core/application.js | 3 +++ editor/core/workspace.js | 3 +++ editor/main.js | 15 +++++++++++---- editor/models/workspace.js | 26 ++++++++++++++++++++++++++ lib/configs/default.js | 3 +++ lib/services/codebox.js | 4 ++++ lib/workspace.js | 9 ++++++++- 7 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 editor/core/workspace.js create mode 100644 editor/models/workspace.js diff --git a/editor/core/application.js b/editor/core/application.js index 752ecb85..a96663df 100644 --- a/editor/core/application.js +++ b/editor/core/application.js @@ -5,6 +5,8 @@ var Q = require("q"); var Application = require("hr.app"); var GridView = require("hr.gridview"); +var workspace = require("./workspace"); + // Define base application var CodeboxApplication = Application.extend({ el: null, @@ -24,6 +26,7 @@ var CodeboxApplication = Application.extend({ }, render: function() { + this.head.title(workspace.get('title')); return this.ready(); }, diff --git a/editor/core/workspace.js b/editor/core/workspace.js new file mode 100644 index 00000000..6621de79 --- /dev/null +++ b/editor/core/workspace.js @@ -0,0 +1,3 @@ +var Workspace = require("../models/workspace"); + +module.exports = new Workspace(); diff --git a/editor/main.js b/editor/main.js index acc04faf..416df13a 100644 --- a/editor/main.js +++ b/editor/main.js @@ -12,6 +12,7 @@ var app = require("./core/application"); var commands = require("./core/commands"); var packages = require("./core/packages"); var user = require("./core/user"); +var workspace = require("./core/workspace"); var users = require("./core/users"); var settings = require("./core/settings"); var dialogs = require("./utils/dialogs"); @@ -27,6 +28,7 @@ window.codebox = { require: codeboxRequire, app: app, user: user, + workspace: workspace, root: new File(), settings: settings }; @@ -48,10 +50,15 @@ commands.register({ // Start running the applications logger.log("start application"); Q.delay(500) -.then(codebox.user.whoami.bind(codebox.user)) -.then(codebox.root.stat.bind(codebox.root, "./")) -.then(settings.load.bind(settings)) -.then(users.listAll.bind(users)) +.then(function() { + return Q.all([ + codebox.user.whoami(), + codebox.root.stat('./'), + codebox.workspace.about(), + settings.load(), + users.listAll() + ]); +}) .then(function() { return packages.loadAll() .fail(function(err) { diff --git a/editor/models/workspace.js b/editor/models/workspace.js new file mode 100644 index 00000000..ba1483c5 --- /dev/null +++ b/editor/models/workspace.js @@ -0,0 +1,26 @@ +var Q = require("q"); +var _ = require("hr.utils"); +var Model = require("hr.model"); +var logger = require("hr.logger")("workspace"); + +var rpc = require("../core/rpc"); + +var Workspace = Model.extend({ + defaults: { + id: "", + title: "" + }, + + // Identify the workspace + about: function() { + var that = this; + + return rpc.execute("codebox/about") + .then(function(data) { + return that.set(data); + }) + .thenResolve(that); + }, +}); + +module.exports = Workspace; diff --git a/lib/configs/default.js b/lib/configs/default.js index 3ce54b37..113c4003 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -14,6 +14,9 @@ module.exports = function(options) { // Root folder 'root': process.cwd(), + // Workspace title + 'title': "Codebox", + // Workspace id 'id': null, diff --git a/lib/services/codebox.js b/lib/services/codebox.js index ee6a03c2..4ba1bd8d 100644 --- a/lib/services/codebox.js +++ b/lib/services/codebox.js @@ -2,9 +2,13 @@ var fs = require("fs"); var path = require("path"); var pkg = require("../../package.json"); +var workspace = require('../workspace'); + // About this current version var about = function(args) { return { + 'id': workspace.config('id'), + 'title': workspace.config('title'), 'version': pkg.version }; }; diff --git a/lib/workspace.js b/lib/workspace.js index 612d004c..3dbe93fb 100644 --- a/lib/workspace.js +++ b/lib/workspace.js @@ -6,9 +6,11 @@ var events = require('./events'); var logger = require('./utils/logger')("workspace"); var root = null; +var _config = {}; // Init the workspace var init = function(config) { + _config = config; root = path.resolve(config.root); logger.log("Working on ", root); @@ -38,5 +40,10 @@ module.exports = { init: init, path: getPath, relative: relativePath, - root: function() { return root; } + root: function() { return root; }, + config: function(str) { + return str.split('.').reduce(function(obj, i) { + return obj[i]; + }, _config); + } }; From 297520b015772e89a905210299ee80200724fe60 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Wed, 22 Apr 2015 13:29:55 -0500 Subject: [PATCH 094/101] Use __ as dots in envs --- lib/configs/env.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configs/env.js b/lib/configs/env.js index 361aa312..8828fcb0 100644 --- a/lib/configs/env.js +++ b/lib/configs/env.js @@ -21,6 +21,7 @@ var parseEnv = function (env, parent, prefix) { parseEnv(env, v, envVar); } else { + envVar = envVar.replace(/\./g, '__'); if (envVar in env) { if (_.isArray(v) && (v.length == 0 || _.isString(v[0]))) { parent[k] = _.compact(env[envVar].split(",")); @@ -45,6 +46,5 @@ module.exports = function(options) { _.each(alias, function(to, from) { if (env[from]) env[to] = env[from] || env[to]; }); - return parseEnv(env, options, 'CODEBOX'); }; \ No newline at end of file From 370bfa2c562be3afd00f6408a8d7b843daaca6fa Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Wed, 22 Apr 2015 13:44:28 -0500 Subject: [PATCH 095/101] Bump version to 1.0.0-alpha.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c033f19..a88ca1d1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codebox", "description": "Extensible hybrid IDE", - "version": "1.0.0-alpha.3", + "version": "1.0.0-alpha.4", "author": "FriendCode Inc. ", "license": "Apache 2", "preferGlobal": true, From 3dd0877716f2483050735205840e423bac1af42f Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 23 Apr 2015 08:41:38 -0500 Subject: [PATCH 096/101] Improve authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support option “redirect” --- lib/configs/default.js | 10 +++++++++- lib/index.js | 39 +++++++++++++++++++++++++++------------ package.json | 2 +- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lib/configs/default.js b/lib/configs/default.js index 113c4003..fc425013 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -30,10 +30,13 @@ module.exports = function(options) { // Authentication settings 'auth': { - 'basic': true + // Redirect user to this url for auth + 'redirect': undefined, }, // Hooks + // If value is string: POST to the url + // If function: executed 'hooks': { 'users.auth': function(data) { if (!data.email || !data.token) throw "Need 'token' and 'email' for auth hook"; @@ -59,8 +62,13 @@ module.exports = function(options) { // Packages 'packages': { + // Path to store all packages for the user 'root': undefined, + + // Path to default packages 'defaults': path.resolve(__dirname, "../../packages"), + + // Packages to install when booting 'install': {} } }, _.defaults); diff --git a/lib/index.js b/lib/index.js index 4f75bd85..59de7139 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,7 +7,7 @@ var fs = require('fs'); var http = require('http'); var express = require('express'); var bodyParser = require('body-parser'); -var basicAuth = require('basic-auth-connect'); +var basicAuth = require('basic-auth'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var Busboy = require('busboy'); @@ -115,6 +115,8 @@ var start = function(config) { resave: false, saveUninitialized: true })); + + // Auth by query strings app.use("/", function(req, res, next) { var args = _.extend({}, req.query, req.body); if (args.email && args.token) { @@ -128,21 +130,30 @@ var start = function(config) { } }); - // Static files - app.use('/', express.static(path.resolve(__dirname, '../build'))); - // Auth app.use(function(req, res, next) { - var doAuth = basicAuth(function(user, pass, fn){ - users.auth(user, pass) + if (req.session.userId) return next(); + + var auth = basicAuth(req); + + // Do basic auth + if (auth && auth.name && auth.pass) { + users.auth(auth.name, auth.pass, req) .then(function(user) { - fn(null, user) + req.user = user; + next(); }) - .fail(fn); - }); - - if (req.session.userId || !config.auth.basic) return next(); - doAuth(req, res, next); + .fail(next); + } else { + if (config.auth.redirect) { + console.log('no auth, redirect to', config.auth.redirect); + res.redirect(config.auth.redirect); + } else { + res.header('WWW-Authenticate', 'Basic realm="codebox"'); + res.status(401); + res.end(); + } + } }); app.use(function(req, res, next) { if (req.user) { @@ -166,6 +177,10 @@ var start = function(config) { } }); + // Static files + app.use('/', express.static(path.resolve(__dirname, '../build'))); + + // Download packages app.use('/packages', _middleware(function() { return express.static(config.packages.root); diff --git a/package.json b/package.json index a88ca1d1..52fb7351 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "commander": "2.8.0", "open": "0.0.5", "ini": "1.2.1", - "basic-auth-connect": "1.0.0", + "basic-auth": "1.0.0", "mime": "1.3.4", "busboy": "0.2.9", "uuid": "2.0.1", From 799b17198f7c3548e5437454320f704aa2505d3f Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 23 Apr 2015 09:29:52 -0500 Subject: [PATCH 097/101] For production, use a bundle of all packages --- editor/collections/packages.js | 61 ++++++++++++++++++---------------- editor/main.js | 2 +- lib/index.js | 7 ++++ lib/packages.js | 28 ++++++++++++++-- lib/services/codebox.js | 3 +- package.json | 2 +- 6 files changed, 70 insertions(+), 33 deletions(-) diff --git a/editor/collections/packages.js b/editor/collections/packages.js index a356dfd6..f461ceeb 100644 --- a/editor/collections/packages.js +++ b/editor/collections/packages.js @@ -1,5 +1,6 @@ var Q = require("q"); var _ = require("hr.utils"); +var $ = require("jquery"); var Collection = require("hr.collection"); var logger = require("hr.logger")("packages"); @@ -16,38 +17,42 @@ var Packages = Collection.extend({ .then(this.reset.bind(this)); }, - // Load all plugins from backend - loadAll: function() { + // Load all plugins from backend (using bundle) + loadAll: function(bundle) { var that = this; var errors = []; - return this.listAll() - .then(function() { - return that.reduce(function(prev, pkg) { - errors = errors.concat(_.map(pkg.get("errors"), function(e) { - return { - 'name': pkg.get("name"), - 'error': e - }; - })); - - return prev.then(pkg.load.bind(pkg)) - .fail(function(err) { - errors.push({ - 'name': pkg.get("name"), - 'error': err + if (bundle) { + return Q($.getScript("/packages.js")); + } else { + return this.listAll() + .then(function() { + return that.reduce(function(prev, pkg) { + errors = errors.concat(_.map(pkg.get("errors"), function(e) { + return { + 'name': pkg.get("name"), + 'error': e + }; + })); + + return prev.then(pkg.load.bind(pkg)) + .fail(function(err) { + errors.push({ + 'name': pkg.get("name"), + 'error': err + }); + return Q(); }); - return Q(); - }); - }, Q()); - }) - .then(function() { - if (errors.length > 0) { - var e = new Error("Error loading packages"); - e.errors = errors; - return Q.reject(e); - } - }); + }, Q()); + }) + .then(function() { + if (errors.length > 0) { + var e = new Error("Error loading packages"); + e.errors = errors; + return Q.reject(e); + } + }); + } } }); diff --git a/editor/main.js b/editor/main.js index 416df13a..a173d424 100644 --- a/editor/main.js +++ b/editor/main.js @@ -60,7 +60,7 @@ Q.delay(500) ]); }) .then(function() { - return packages.loadAll() + return packages.loadAll(!codebox.workspace.get('debug')) .fail(function(err) { var message = "

"+err.message+"

"; if (err.errors) { diff --git a/lib/index.js b/lib/index.js index 59de7139..c89e973f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -185,6 +185,13 @@ var start = function(config) { app.use('/packages', _middleware(function() { return express.static(config.packages.root); })); + app.get('/packages.js', function(req, res, next) { + return packages.bundle() + .then(function(fp) { + fs.createReadStream(fp).pipe(res); + }) + .fail(next); + }); // RPC services app.use('/rpc', rpc.router); diff --git a/lib/packages.js b/lib/packages.js index 43cd3314..43ab16fc 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -1,6 +1,7 @@ var Q = require("q"); var _ = require("lodash"); var fs = require("fs"); +var os = require("os"); var path = require("path"); var wrench = require("wrench"); var Packager = require("pkgm"); @@ -107,6 +108,9 @@ var init = function(config) { .then(function() { if (!config.run) return; return manager.runAll(context); + }) + .then(function() { + return bundle(true); }); }; @@ -115,6 +119,9 @@ var install = function(url) { .then(function(pkg) { return pkg.run(context) .thenResolve(pkg); + }) + .then(function() { + return bundle(true); }); }; @@ -126,10 +133,27 @@ var list = function() { return manager.orderedPackages(); }; +var bundle = function(force) { + var pkgBundle = path.resolve(os.tmpdir(), 'codebox-bundle.js'); + + return Q() + .then(function() { + if (fs.existsSync(pkgBundle) && force != true) return; + console.log("bundle into", pkgBundle); + return manager.bundleAll(pkgBundle); + }) + .then(function() { + return pkgBundle; + }); +}; + module.exports = { init: init, - manager: manager, + manager: function() { + return manager; + }, install: install, uninstall: uninstall, - list: list + list: list, + bundle: bundle }; diff --git a/lib/services/codebox.js b/lib/services/codebox.js index 4ba1bd8d..1d211a59 100644 --- a/lib/services/codebox.js +++ b/lib/services/codebox.js @@ -9,7 +9,8 @@ var about = function(args) { return { 'id': workspace.config('id'), 'title': workspace.config('title'), - 'version': pkg.version + 'version': pkg.version, + 'debug': workspace.config('debug') }; }; diff --git a/package.json b/package.json index 52fb7351..9a7b6d1b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "dependencies": { "q": "~1.2.0", "lodash": "2.4.1", - "pkgm": "3.2.0", + "pkgm": "3.3.0", "express": "4.6.1", "express-session": "1.7.0", "wrench": "1.5.8", From d33619be9be8e956159cf47ce7771a5e8f10c439 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 23 Apr 2015 09:32:04 -0500 Subject: [PATCH 098/101] Remove useless log --- lib/packages.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/packages.js b/lib/packages.js index 43ab16fc..39ad01d4 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -139,7 +139,6 @@ var bundle = function(force) { return Q() .then(function() { if (fs.existsSync(pkgBundle) && force != true) return; - console.log("bundle into", pkgBundle); return manager.bundleAll(pkgBundle); }) .then(function() { From 9e668877b7b5fbdd3f578bd0d83a7a624655ec89 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 23 Apr 2015 09:48:48 -0500 Subject: [PATCH 099/101] Add dialogs for offline events --- editor/core/application.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/editor/core/application.js b/editor/core/application.js index a96663df..8b94f87d 100644 --- a/editor/core/application.js +++ b/editor/core/application.js @@ -1,10 +1,11 @@ var _ = require("hr.utils"); var $ = require("jquery"); var Q = require("q"); - +var logger = require("hr.logger")("app"); var Application = require("hr.app"); var GridView = require("hr.gridview"); +var dialogs = require("../utils/dialogs"); var workspace = require("./workspace"); // Define base application @@ -23,6 +24,18 @@ var CodeboxApplication = Application.extend({ }, this); this.grid.$el.addClass("main-grid"); this.grid.appendTo(this); + + // Signal offline + function updateOnlineStatus(event) { + logger.log("connection changed", navigator.onLine); + if (!navigator.onLine) { + dialogs.alert("It looks like you lost your internet connection. The IDE requires an internet connection."); + } else { + dialogs.alert("Your internet connection is up again. Restart your navigator tab to ensure that codebox works perfectly."); + } + } + window.addEventListener('online', updateOnlineStatus); + window.addEventListener('offline', updateOnlineStatus); }, render: function() { From f65aa280bdd24af973a7514cfb00855bd51ec521 Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 23 Apr 2015 09:58:57 -0500 Subject: [PATCH 100/101] Use node-tmp to get a temp file for the bundle --- lib/packages.js | 18 +++++++++++++----- package.json | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/packages.js b/lib/packages.js index 39ad01d4..846bf9ae 100644 --- a/lib/packages.js +++ b/lib/packages.js @@ -4,13 +4,14 @@ var fs = require("fs"); var os = require("os"); var path = require("path"); var wrench = require("wrench"); +var tmp = require("tmp"); var Packager = require("pkgm"); var pkg = require("../package.json"); var events = require("./events"); var logger = require("./utils/logger")("packages"); -var context, manager; +var context, manager, _bundle; // Remove output if folder or symlink function cleanFolder(outPath) { @@ -134,15 +135,22 @@ var list = function() { }; var bundle = function(force) { - var pkgBundle = path.resolve(os.tmpdir(), 'codebox-bundle.js'); + var exists = true; return Q() .then(function() { - if (fs.existsSync(pkgBundle) && force != true) return; - return manager.bundleAll(pkgBundle); + if (_bundle) return _bundle ; + exists = false; + + return Q.nfcall(tmp.file).get(0); + }) + .then(function(b) { + _bundle = b; + if (exists && force != true) return; + return manager.bundleAll(_bundle ); }) .then(function() { - return pkgBundle; + return _bundle ; }); }; diff --git a/package.json b/package.json index 9a7b6d1b..64f32c2e 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "q": "~1.2.0", "lodash": "2.4.1", "pkgm": "3.3.0", + "tmp": "0.0.25", "express": "4.6.1", "express-session": "1.7.0", "wrench": "1.5.8", From 98b4710f1bdba615dddbab56011a86cd5f16897b Mon Sep 17 00:00:00 2001 From: Samy Pesse Date: Thu, 23 Apr 2015 13:26:47 -0500 Subject: [PATCH 101/101] Bump version to 1.0.0-alpha.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 64f32c2e..f091b217 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codebox", "description": "Extensible hybrid IDE", - "version": "1.0.0-alpha.4", + "version": "1.0.0-alpha.5", "author": "FriendCode Inc. ", "license": "Apache 2", "preferGlobal": true,