From ba2b7ef02f6276f8b7eefabc54be72150109ab84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 25 Feb 2014 12:40:33 +0100 Subject: [PATCH 001/646] Use r.js programmatically --- core/cb.addons/addon.js | 26 ++++++++++++++------------ package.json | 4 ++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/core/cb.addons/addon.js b/core/cb.addons/addon.js index e7f160b3..595db572 100644 --- a/core/cb.addons/addon.js +++ b/core/cb.addons/addon.js @@ -5,6 +5,8 @@ var wrench = require('wrench'); var child_process = require('child_process'); var Q = require("q"); +var requirejs = require("requirejs"); + var exec = function(command, options) { var deferred = Q.defer(); @@ -111,9 +113,6 @@ var Addon = function(_rootPath, options) { return Q(this); } - // R.js bin - var rjs = path.resolve(__dirname, "../../node_modules/.bin/r.js"); - // Base directory for the addon var addonPath = this.root; @@ -126,7 +125,7 @@ var Addon = function(_rootPath, options) { // Output file var output = path.resolve(addonPath, "addon-built.js"); - // Build config (todo: use this config for the command) + // Build config var optconfig = { 'baseUrl': addonPath, 'name': main, @@ -134,7 +133,7 @@ var Addon = function(_rootPath, options) { 'paths': { 'require-tools': requiretoolsPath }, - 'optimize': "none", + 'optimize': "uglify", 'map': { '*': { 'css': "require-tools/css/css", @@ -143,23 +142,26 @@ var Addon = function(_rootPath, options) { } } }; - - var command = rjs+" -o baseUrl="+addonPath+" paths.require-tools="+requiretoolsPath+" name="+main+" map.*.css=require-tools/css/css map.*.less=require-tools/less/less map.*.text=require-tools/text/text out="+output; - // Run optimization logger.log("Optimizing", this.infos.name); return Q.nfcall(fs.unlink, output).fail(function() { return Q(); }).then(function() { - return exec(command) + var d = Q.defer(); + + requirejs.optimize(optconfig, function(resp, err) { + if (err) return d.reject(err); + d.resolve(resp); + }); + + return d.promise; }).then(function() { logger.log("Finished", that.infos.name, "optimization"); return Q(that); }, function(err) { logger.error("error for optimization of", that.infos.name); - logger.error("command=",command); - logger.error(err.stdout); - logger.exception(err, false); + logger.error("options=", optconfig); + logger.error(err); return Q.reject(err); }); }; diff --git a/package.json b/package.json index 528677dd..f736b4fb 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "express": "3.3.5", "socket.io": "0.9.16", "loadfire": "1.3.4", - "shux": "0.2.0", + "shux": "git+https://github.com/SamyPesse/shux.git#3abe4c6074013791eac0d055c63ed70b3197e7f4", "vfs-http-adapter": "git+https://github.com/AaronO/vfs-http-adapter.git#6c9934e4f2da7886310397170b6ebaf745833936", "vfs-local": "git+https://github.com/FriendCode/vfs-local.git#af6bafede0ccb9fb362a0c280e708648f7363f33", "watchr": "2.4.11", @@ -29,7 +29,7 @@ "glob": "3.2.6", "temp": "0.6.0", "wrench": "1.5.7", - "requirejs": "2.1.0", + "requirejs": "2.1.11", "less": "1.5.0", "csso": "1.3.9", "fstream": "0.1.25", From 3c94ae07b29a5926b589e1514ad23036c35e3bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 25 Feb 2014 12:48:55 +0100 Subject: [PATCH 002/646] Fix error handling for requirejs --- core/cb.addons/addon.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/cb.addons/addon.js b/core/cb.addons/addon.js index 595db572..82163076 100644 --- a/core/cb.addons/addon.js +++ b/core/cb.addons/addon.js @@ -149,9 +149,10 @@ var Addon = function(_rootPath, options) { }).then(function() { var d = Q.defer(); - requirejs.optimize(optconfig, function(resp, err) { - if (err) return d.reject(err); + requirejs.optimize(optconfig, function(resp) { d.resolve(resp); + }, function(err) { + d.reject(err); }); return d.promise; From b1cdc642cc67ca8117ed66f393c429a74df40e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 26 Feb 2014 23:06:37 +0100 Subject: [PATCH 003/646] Add base for a grid system --- client/resources/stylesheets/main.less | 1 + .../resources/stylesheets/tabs/manager.less | 1 + client/resources/stylesheets/ui/grid.less | 31 +++++ client/views/tabs/grid.js | 116 ++++++++++++++++++ client/views/tabs/manager.js | 27 ++-- 5 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 client/resources/stylesheets/ui/grid.less create mode 100644 client/views/tabs/grid.js diff --git a/client/resources/stylesheets/main.less b/client/resources/stylesheets/main.less index 9b36008c..9b0a603f 100644 --- a/client/resources/stylesheets/main.less +++ b/client/resources/stylesheets/main.less @@ -19,6 +19,7 @@ @import "ui/body.less"; @import "ui/login.less"; @import "ui/toolbar.less"; +@import "ui/grid.less"; * { -webkit-overflow-scrolling: touch; diff --git a/client/resources/stylesheets/tabs/manager.less b/client/resources/stylesheets/tabs/manager.less index ca228997..bba22e57 100644 --- a/client/resources/stylesheets/tabs/manager.less +++ b/client/resources/stylesheets/tabs/manager.less @@ -2,6 +2,7 @@ position: absolute; width: 100%; height: 100%; + overflow: hidden; .section { position: absolute; diff --git a/client/resources/stylesheets/ui/grid.less b/client/resources/stylesheets/ui/grid.less new file mode 100644 index 00000000..ef6adcc4 --- /dev/null +++ b/client/resources/stylesheets/ui/grid.less @@ -0,0 +1,31 @@ +.component-grid { + position: absolute; + top: -1px; + left: -1px; + right: -1px; + bottom: -1px; + + .grid-section { + position: absolute; + + .grid-resize-bar-v { + position: absolute; + right: 0px; + height: 3px; + left: 0px; + bottom: 0px; + background: blue; + cursor: row-resize; + } + + .grid-resize-bar-h { + position: absolute; + right: 0px; + width: 3px; + top: 0px; + bottom: 0px; + background: red; + cursor: col-resize; + } + } +} \ No newline at end of file diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js new file mode 100644 index 00000000..751394f5 --- /dev/null +++ b/client/views/tabs/grid.js @@ -0,0 +1,116 @@ +define([ + "hr/utils", + "hr/dom", + "hr/hr" +], function(_, $, hr) { + 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 = []; + + return this; + }, + + /* + * Add a view + */ + addView: function(view) { + this.views.push(view); + return this.render(); + }, + + /* + * Remove a view + */ + removeView: function(view) { + if (!_.isString(view)) view = view.cid; + + this.views = _.filter(this.views, function(_v) { + return _v.cid != view; + }); + return this.render(); + }, + + /* + * Change layout by defining + */ + setLayout: function(n) { + this.columns = n; + return this.render(); + }, + + /* + * Re-render the complete layout + */ + render: function() { + var x, y; + this.$el.empty(); + + // Calcul layout if auto + 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); + + var sectionWidth = (100/nColumns).toFixed(3); + var sectionHeight = (100/nLines).toFixed(3); + + // Add grid content + x = 0; y = 0; + _.each(this.views, function(view, i) { + // Container object + var section = $("
", { + 'class': 'grid-section', + 'css': { + 'left': (x * sectionWidth)+"%", + 'top': (y * sectionHeight)+"%", + 'width': sectionWidth+"%", + 'height': sectionHeight+"%" + } + }); + section.appendTo(this.$el); + + // Resize bar + if (x < (nColumns - 1)) { + // Horrizontal + var hBar = $("
", { + 'class': "grid-resize-bar-h" + }); + hBar.appendTo(section); + } + + if (y < (nLines - 1)) { + // Vertical + var vBar = $("
", { + 'class': "grid-resize-bar-v" + }); + vBar.appendTo(section); + } + + + // Calcul next position + x = x + 1; + if (x >= nColumns) { + x = 0; + y = y + 1; + } + }, this); + + return this.ready(); + }, + + + }); + + return GridView; +}); \ No newline at end of file diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index be42e04f..2991495d 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -3,12 +3,15 @@ define([ "hr/dom", "hr/hr", "models/command", + "utils/dragdrop", "utils/keyboard", "utils/contextmenu", + "views/tabs/tab", - "views/tabs/base" -], function(_, $, hr, Command, DragDrop, Keyboard, ContextMenu, TabView, TabPanelView) { + "views/tabs/base", + "views/tabs/grid" +], function(_, $, hr, Command, DragDrop, Keyboard, ContextMenu, TabView, TabPanelView, GridView) { // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", @@ -16,7 +19,8 @@ define([ "dblclick .tabs-header": "openDefaultNew" }, layouts: { - "Auto Grid": null, + "Auto Grid": 0, + "Columns: 1": 1, "Columns: 2": 2, "Columns: 3": 3, "Columns: 4": 4 @@ -29,6 +33,11 @@ define([ // Current layout this.layout = null; // null: mode auto + this.grid = new GridView(); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); // Commands this.layoutCommand = new Command({}, { @@ -65,8 +74,12 @@ define([ // Empty view this.empty(); + // Add grid + this.grid.$el.appendTo(this.$el); + this.grid.render(); + // Check tabs - this.checkTabs(); + /*this.checkTabs(); // Calcul number of sections var sections = this.getSections(); @@ -154,7 +167,7 @@ define([ sectionTop = sectionTop + sectionHeight; } }, this); - this.delegateEvents(); + this.delegateEvents();*/ return this.ready(); }, @@ -398,8 +411,8 @@ define([ // Define tabs layout setLayout: function(l) { - this.layout = l; - this.trigger("layout", this.layout); + this.grid.setLayout(l); + this.trigger("layout", l); this.update(); } }, { From 931256654e3dbeb8d8de9031f9d46d34ce2ad93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 26 Feb 2014 23:21:49 +0100 Subject: [PATCH 004/646] Base handling for grid resizing using mouse --- client/views/tabs/grid.js | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js index 751394f5..143b81e6 100644 --- a/client/views/tabs/grid.js +++ b/client/views/tabs/grid.js @@ -68,8 +68,10 @@ define([ // Add grid content x = 0; y = 0; _.each(this.views, function(view, i) { + var section + // Container object - var section = $("
", { + section = $("
", { 'class': 'grid-section', 'css': { 'left': (x * sectionWidth)+"%", @@ -84,7 +86,8 @@ define([ if (x < (nColumns - 1)) { // Horrizontal var hBar = $("
", { - 'class': "grid-resize-bar-h" + 'class': "grid-resize-bar-h", + 'mousedown': this.resizerHandler(x, y, "h") }); hBar.appendTo(section); } @@ -92,7 +95,8 @@ define([ if (y < (nLines - 1)) { // Vertical var vBar = $("
", { - 'class': "grid-resize-bar-v" + 'class': "grid-resize-bar-v", + 'mousedown': this.resizerHandler(x, y, "v") }); vBar.appendTo(section); } @@ -109,7 +113,32 @@ define([ return this.ready(); }, - + // Create a resizer handler + resizerHandler: function(x, y, type) { + var $document = $(document); + var oX, oY, dX, dY; + return function(e) { + e.preventDefault(); + oX = e.pageX; + oY = e.pageY; + + var f = function(e) { + dx = oX - e.pageX; + dy = oY - e.pageY; + + if (type == "h") { + console.log("move h", dx); + } else { + console.log("move v", dy); + } + }; + + $document.mousemove(f); + $document.mouseup(function(e) { + $document.unbind('mousemove', f); + }); + }; + } }); return GridView; From ac8ffdcd4f775427da3a06f044d9110c9ea1be49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 26 Feb 2014 23:40:17 +0100 Subject: [PATCH 005/646] Add base resize for sections --- client/views/tabs/grid.js | 77 +++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js index 143b81e6..391c4c6f 100644 --- a/client/views/tabs/grid.js +++ b/client/views/tabs/grid.js @@ -49,6 +49,21 @@ define([ return this.render(); }, + /* + * 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 + }; + }, + /* * Re-render the complete layout */ @@ -56,14 +71,11 @@ define([ var x, y; this.$el.empty(); - // Calcul layout if auto - 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); + // Calcul layout + var layout = this.getLayout(); - var sectionWidth = (100/nColumns).toFixed(3); - var sectionHeight = (100/nLines).toFixed(3); + var sectionWidth = (100/layout.columns).toFixed(3); + var sectionHeight = (100/layout.lines).toFixed(3); // Add grid content x = 0; y = 0; @@ -83,7 +95,7 @@ define([ section.appendTo(this.$el); // Resize bar - if (x < (nColumns - 1)) { + if (x < (layout.columns - 1)) { // Horrizontal var hBar = $("
", { 'class': "grid-resize-bar-h", @@ -92,7 +104,7 @@ define([ hBar.appendTo(section); } - if (y < (nLines - 1)) { + if (y < (layout.lines - 1)) { // Vertical var vBar = $("
", { 'class': "grid-resize-bar-v", @@ -102,9 +114,9 @@ define([ } - // Calcul next position + // Calcul next position x = x + 1; - if (x >= nColumns) { + if (x >= layout.columns) { x = 0; y = y + 1; } @@ -115,6 +127,7 @@ define([ // Create a resizer handler resizerHandler: function(x, y, type) { + var that = this; var $document = $(document); var oX, oY, dX, dY; return function(e) { @@ -128,9 +141,14 @@ define([ if (type == "h") { console.log("move h", dx); + that.resizeColumn(y, -dx); } else { console.log("move v", dy); + that.resizeLine(x, -dy); } + + oX = e.pageX; + oY = e.pageY; }; $document.mousemove(f); @@ -138,6 +156,43 @@ define([ $document.unbind('mousemove', f); }); }; + }, + + 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; + }); + }, + + resizeLine: function(i, d) { + var $sections = this.getSection(null, i); + var h = $sections.height(); + + $sections.height(h+d); + }, + + resizeColumn: function(i, d) { + var $sections = this.getSection(i, null); + var w = $sections.width(); + + $sections.width(w+d); } }); From c6de09e46dc2aece55c0cfd1ce2c19e6c2d91222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 26 Feb 2014 23:43:42 +0100 Subject: [PATCH 006/646] Use percent instead of pixels for size --- client/views/tabs/grid.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js index 391c4c6f..9fb95ca5 100644 --- a/client/views/tabs/grid.js +++ b/client/views/tabs/grid.js @@ -185,14 +185,19 @@ define([ var $sections = this.getSection(null, i); var h = $sections.height(); - $sections.height(h+d); + $sections.height(this.pixelToPercent(null, h+d)+"%"); }, resizeColumn: function(i, d) { var $sections = this.getSection(i, null); var w = $sections.width(); - $sections.width(w+d); + $sections.width(this.pixelToPercent(w+d, null)+"%"); + }, + + pixelToPercent: function(x, y) { + if (x != null) return ((x*100) / this.$el.width()); + if (y != null) return ((y*100) / this.$el.height()); } }); From 56b330b006eb89467fb19f1ddee546dc57979475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 00:29:22 +0100 Subject: [PATCH 007/646] Complete first grid resizable --- client/views/tabs/grid.js | 57 ++++++++++++++++++++++++++++-------- client/views/tabs/manager.js | 4 +++ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js index 9fb95ca5..9536ea3e 100644 --- a/client/views/tabs/grid.js +++ b/client/views/tabs/grid.js @@ -140,11 +140,9 @@ define([ dy = oY - e.pageY; if (type == "h") { - console.log("move h", dx); - that.resizeColumn(y, -dx); + that.resizeColumn(x, -dx); } else { - console.log("move v", dy); - that.resizeLine(x, -dy); + that.resizeLine(y, -dy); } oX = e.pageX; @@ -181,18 +179,53 @@ define([ }); }, - resizeLine: function(i, d) { - var $sections = this.getSection(null, i); - var h = $sections.height(); + _resize: function(type, i, d) { + var getSection = _.bind(_.partialRight(this.getSection, null), this); + var pixelToPercent = _.bind(_.partialRight(this.pixelToPercent, null), this); + var size = function(el, s) { + if (!s) return el.width(); + return el.width(s); + }; + var position = "left"; + + if (type == "h") { + getSection = _.bind(_.partial(this.getSection, null), this); + pixelToPercent = _.bind(_.partial(this.pixelToPercent, null), this); + position = "top"; + size = function(el, s) { + if (!s) return el.height(); + return el.height(s); + } + } + + var $sections = getSection(i); + var $sectionsAfter = getSection(i+1); + + // New size for next sections + var sAfterN = pixelToPercent(size($sectionsAfter)-d); + + // New size for current sections + var sCurrentN = pixelToPercent(size($sections)+d); + + // Limited size + if (sCurrentN < 10 || sAfterN < 10) return false; - $sections.height(this.pixelToPercent(null, h+d)+"%"); + // Resize next line + $sectionsAfter.css(_.object([position], [pixelToPercent($sectionsAfter.position()[position]+d)+"%"])); + size($sectionsAfter, sAfterN+"%"); + + // Resize current line + size($sections, sCurrentN+"%"); + + return true; }, - resizeColumn: function(i, d) { - var $sections = this.getSection(i, null); - var w = $sections.width(); + resizeLine: function(i, d) { + return this._resize("h", i, d); + }, - $sections.width(this.pixelToPercent(w+d, null)+"%"); + resizeColumn: function(i, d) { + return this._resize("w", i, d); }, pixelToPercent: function(x, y) { diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 2991495d..1ca2720c 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -38,6 +38,10 @@ define([ this.grid.addView(new hr.View()); this.grid.addView(new hr.View()); this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); // Commands this.layoutCommand = new Command({}, { From 354452e529ec5d2138ff8c4d60f9781b7b8d5dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 12:19:22 +0100 Subject: [PATCH 008/646] Start base refactoring for tabs manager --- client/collections/tabs.js | 18 ++ client/core/files.js | 43 ++--- client/models/tab.js | 15 ++ client/views/tabs/base.js | 35 ++-- client/views/tabs/manager.js | 357 ++++++++--------------------------- client/views/tabs/section.js | 74 ++++++++ 6 files changed, 218 insertions(+), 324 deletions(-) create mode 100644 client/collections/tabs.js create mode 100644 client/models/tab.js create mode 100644 client/views/tabs/section.js diff --git a/client/collections/tabs.js b/client/collections/tabs.js new file mode 100644 index 00000000..591f0b79 --- /dev/null +++ b/client/collections/tabs.js @@ -0,0 +1,18 @@ +define([ + "hr/utils", + "hr/hr", + "models/tab" +], function(_, hr, Tab) { + var Tabs = hr.Collection.extend({ + model: Tab, + + // Return a tab by its id + getById: function(id) { + return this.find(function(tab) { + return tab.id == id; + }); + } + }); + + return Tabs; +}); \ No newline at end of file diff --git a/client/core/files.js b/client/core/files.js index e473def6..a3042c2a 100644 --- a/client/core/files.js +++ b/client/core/files.js @@ -60,31 +60,24 @@ define([ // Add files as open if (handler.setActive) activeFiles.add(file); - var tab = tabs.getActiveTabByType("directory"); - if (tab != null && !tabs.checkTabExists(uniqueId) && !file.isNewfile()) { - // Change current tab to open the file - tab.view.load(path, handler); - } else { - - // Add new tab - var tab = tabs.add(FileTab, { - "model": file, - "handler": handler - }, { - "uniqueId": uniqueId, - "type": "file", - }); - - // Focus tab -> set active file - tab.on("tab:state", function(state) { - if (state) box.setActiveFile(file); - }); - - // Close tab -> close active file - tab.on("tab:close", function(state) { - if (handler.setActive) activeFiles.remove(file); - }); - } + // Add new tab + var tab = tabs.add(FileTab, { + "model": file, + "handler": handler + }, { + "uniqueId": uniqueId, + "type": "file", + }); + + // Focus tab -> set active file + tab.on("tab:state", function(state) { + if (state) box.setActiveFile(file); + }); + + // Close tab -> close active file + tab.on("tab:close", function(state) { + if (handler.setActive) activeFiles.remove(file); + }); }; } diff --git a/client/models/tab.js b/client/models/tab.js new file mode 100644 index 00000000..3d681791 --- /dev/null +++ b/client/models/tab.js @@ -0,0 +1,15 @@ +define([ + "hr/utils", + "hr/hr" +], function(_, hr) { + var Tab = hr.Model.extend({ + defaults: { + "id": null, + "title": "", + "state": null, + "active": false + } + }); + + return Tab; +}); \ No newline at end of file diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index 79bc317d..9a4ebc18 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -30,8 +30,8 @@ define([ TabPanelView.__super__.initialize.apply(this, arguments); var menu = require("core/commands/menu"); - this.tabid = this.options.tabid; this.tabs = this.parent; + this.tab = this.options.tab; // Create tab menu this.menu = new Command({}, { @@ -77,24 +77,23 @@ define([ // Close the tab closeTab: function(e, force) { if (e != null) e.preventDefault(); - this.tabs.close(this.tabid, force); + //this.tabs.close(this.tabid, force); }, // Open the tab openTab: function(e) { - this.tabs.open(this.tabid); + //this.tabs.open(this.tabid); }, // Set tab title setTabTitle: function(t) { - this.tabs.tabs[this.tabid].title = t; - this.tabs.tabs[this.tabid].tab.update(); + //this.tab.set("title", t); return this; }, // Set tab state setTabState: function(state, value) { - var states = (this.tabs.tabs[this.tabid].state || "").split(" "); + var states = (this.tab.get("state") || "").split(" "); if (value == null) state = !_.contains(states, state); if (value) { @@ -102,27 +101,21 @@ define([ } else { states = _.without(states, state); } - this.tabs.tabs[this.tabid].state = _.uniq(states).join(" "); - this.tabs.tabs[this.tabid].tab.update(); + this.tab.set("state", _.uniq(states).join(" ")); return this; }, // Set tab title setTabId: function(t) { - this.tabs.tabs[this.tabid].uniqueId = t; - return this; - }, - - // Set tab type - setTabType: function(t) { - this.tabs.tabs[this.tabid].type = t; + //this.tabs.tabs[this.tabid].uniqueId = t; return this; }, // Return if is active isActiveTab: function() { - var active = this.tabs.getCurrentTab(); - return !(active == null || active.tabid != this.tabid); + /*var active = this.tabs.getCurrentTab(); + return !(active == null || active.tabid != this.tabid);*/ + return false; }, // Check that tab can be closed @@ -133,17 +126,17 @@ define([ // Goto tabGotoPrevious: function(e) { if (e) e.preventDefault(); - var that = this; + /*var that = this; setTimeout(function() { that.tabs.open(that.tabs.getPreviousTab(that.tabid)); - }, 0); + }, 0);*/ }, tabGotoNext: function(e) { if (e) e.preventDefault(); - var that = this; + /*var that = this; setTimeout(function() { that.tabs.open(that.tabs.getNextTab(that.tabid)); - }, 0); + }, 0);*/ } }); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 1ca2720c..1f63d804 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -4,19 +4,22 @@ define([ "hr/hr", "models/command", + "collections/tabs", + "utils/dragdrop", "utils/keyboard", "utils/contextmenu", "views/tabs/tab", "views/tabs/base", - "views/tabs/grid" -], function(_, $, hr, Command, DragDrop, Keyboard, ContextMenu, TabView, TabPanelView, GridView) { + "views/tabs/grid", + "views/tabs/section" +], function(_, $, hr, Command, Tabs, DragDrop, Keyboard, ContextMenu, TabView, TabPanelView, GridView, TabsSectionView) { // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", events: { - "dblclick .tabs-header": "openDefaultNew" + }, layouts: { "Auto Grid": 0, @@ -31,17 +34,13 @@ define([ var that = this; TabsView.__super__.initialize.apply(this, arguments); + // Sections + this.sections = {}; + // Current layout this.layout = null; // null: mode auto this.grid = new GridView(); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); + this.grid.$el.appendTo(this.$el); // Commands this.layoutCommand = new Command({}, { @@ -57,122 +56,40 @@ define([ } }); this.layoutCommand.menu.add(command); - this.on("layout", function() { - command.toggleFlag("active", layout == that.layout); + this.on("layout", function(_layout) { + command.toggleFlag("active", layout == _layout); }); }, this); - // Tabs map - this.tabs = {}; - this.activeTab = null; + // Tabs collection + this.tabs = new Tabs(); // Set base layout this.setLayout(null); return this; }, - // Render all tabs - render: function() { - var that = this; - - // Empty view - this.empty(); - - // Add grid - this.grid.$el.appendTo(this.$el); - this.grid.render(); - - // Check tabs - /*this.checkTabs(); - - // Calcul number of sections - var sections = this.getSections(); - var sections_n = _.max([1, _.size(sections)]); - - - var layout = this.layout || Math.floor(Math.sqrt(sections_n)); // Number of columns - - var nColumns = Math.min(layout, sections_n); - var nLines = Math.ceil(sections_n/layout); - - var sectionWidth = Math.floor(100/nColumns); - var sectionHeight = Math.floor(100/nLines); - - var sectionLeft = 0; - var sectionTop = 0; - - // Add different section - _.each(sections, function(section, sectionIndex) { - var section_el, section_tabs, section_tabs_content, css; - - section_el = $("
", { - "class": "section", - "css": { - "width": sectionWidth+"%", - "height": sectionHeight+"%", - "top": sectionTop+"%", - "left": sectionLeft+"%" - }, - }).appendTo(this.$el); - - // Context menu - ContextMenu.add(section_el, [ - { - 'type': "action", - 'title': "New Tab", - 'action': function() { - that.openDefaultNew(); - } - }, - { 'type': "divider" }, - that.layoutCommand - ]); - - // Drag and drop tabs - section_el.on('dragleave', function(e) { - if (DragDrop.checkDrop(e, "tab")) { - section_el.removeClass("dragover"); - } - }); - section_el.on('dragover', function(e) { - if (DragDrop.checkDrop(e, "tab")) { - DragDrop.dragover(e, 'move'); - section_el.addClass("dragover"); - } - }); - section_el.on('drop', _.bind(function(e) { - if (DragDrop.checkDrop(e, "tab")) { - section_el.removeClass("dragover"); - DragDrop.drop(e); - this.tabs[DragDrop.getData(e, "tab")].tab.setSection(section); - } - }, this)); - - section_tabs = $("
", { - "class": "tabs-header" - }).appendTo(section_el); - - section_tabs_content = $("
", { - "class": "tabs-content" - }).appendTo(section_el); - - _.each(this.tabs, function(tab) { - if (tab.tab.section != section) return; + // Return a section by its id + getSection: function(id) { + if (!this.sections[id]) { + this.sections[id] = new TabsSectionView(); + this.sections[id].sectionId = id; + this.grid.addView(this.sections[id]); + } - tab.tab.$el.appendTo(section_tabs); - tab.view.$el.appendTo(section_tabs_content); - tab.view.trigger("tab:layout"); - }, this); + return this.sections[id]; + }, - // Calcul next position - sectionLeft = sectionLeft + sectionWidth; - if (Math.ceil(sectionLeft/sectionWidth) >= nColumns) { - sectionLeft = 0; - sectionTop = sectionTop + sectionHeight; - } - }, this); - this.delegateEvents();*/ + // Remove a section + removeSection: function(id) { + var s = this.getSection(id); + this.grid.removeView(s); + delete this.sections[id]; + return this; + }, + // Render all tabs + render: function() { return this.ready(); }, @@ -183,7 +100,50 @@ define([ * @options : options */ add: function(V, construct, options) { - var tabid, tabinfos; + var tab = null; + + options = _.defaults(options || {}, { + // Don't trigger event + silent: false, + + // Open after creation + open: true, + + // Base title + title: "untitled", + + // Unique id for this tab + uniqueId: null, + + // Base section id + section: 0 + }); + + if (options.uniqueId) { + tab = this.tabs.getById(options.uniqueId) + } else { + options.uniqueId = _.uniqueId("tab"); + } + + if (!tab) { + // Create tab object + tab = this.tabs.add({ + 'id': options.uniqueId, + 'title': options.title + }); + + // Create content view + tab.view = new V(_.extend(construct || {}, { + "tab": tab, + }), this); + + // Add to section + this.getSection(options.section).addTab(tab); + } + + return tab.view; + + /*var tabid, tabinfos; options = _.defaults(options || {}, { silent: false, render: true, @@ -235,177 +195,18 @@ define([ if (options.render) this.update(); } if (this.activeTab == null || options.open) this.open(tabid); - return this.tabs[tabid].view; - }, - - // Return list of differents sections names - getSections: function() { - return _.uniq(_.map(this.tabs, function(tab) { - return tab.tab.section; - })); - }, - - // Return list of tabs for a section - getSectionTabs: function(section) { - return _.filter(this.tabs, function(tab) { - return tab.tab.section == section; - }); - }, - - // Get the 'n'th tab relative to tab - getNthTab: function(tab, n) { - if (_.isString(tab)) tab = this.tabs[tab]; - if (!tab) tab = this.tabs[this.activeTab]; - - var selectTab = function(tabs) { - var length = tabs.length; - if(length === 1) return null; - - // Index of current tab - var index = tabs.indexOf(tab); - - // Normalize with a modulo - var m = (index + n) % length; - var newIndex = m < 0 ? length + m : m; - - return tabs[newIndex].tabid; - }; - - var tabs = this.getSectionTabs(tab.tab.section); - - return selectTab(tabs) || selectTab(_.values(this.tabs)); - }, - - getPreviousTab: function(tab) { - return this.getNthTab(tab, -1); - }, - - getNextTab: function(tab) { - return this.getNthTab(tab, 1); - }, - - // Return active tab for a section - getActiveTab: function(section) { - section = section || 0; - return _.reduce(this.tabs, function(state, tab) { - if (tab.tab.section != section) return state; - if (tab.tab.isActive()) return tab; - return state; - }, null); - }, - - // Return current (last) active tab - getCurrentTab: function() { - if (this.activeTab == null || this.tabs[this.activeTab] == null) return null; - return this.tabs[this.activeTab]; - }, - - // Return active tab by type - getActiveTabByType: function(type) { - return _.reduce(this.tabs, function(state, tab) { - if (tab.type != type) return state; - if (tab.tab.isActive()) return tab; - return state; - }, null); - }, - - // Check if a tab with a uniqueid exists - checkTabExists: function(uniqueId) { - return _.reduce(this.tabs, function(state, tab) { - return state || (uniqueId == tab.uniqueId ? tab.tabid : null); - }, null) - }, - - // Check all tabs - checkTabs: function() { - var sections = this.getSections(); - _.each(sections, function(section) { - var active = this.getActiveTab(section); - if (active != null) return; - this.getSectionTabs(section)[0].tab.open(); - }, this); + return this.tabs[tabid].view;*/ }, // Open a tab by tabid open: function(tabid) { - var active ,section; - this.activeTab = tabid; - - section = _.reduce(this.tabs, function(s, tab) { - if (tab.tabid == tabid) { - return tab.tab.section; - } - return s; - }, 0); - - _.each(this.tabs, function(tab) { - active = (tab.tabid == this.activeTab); - tab.view.trigger("tab:state", active); - - if (tab.tab.section != section) return; - tab.tab.$el.toggleClass("active", active); - tab.view.$el.toggleClass("active", active); - }, this); - - this.checkTabs(); + return this; }, // Close a tab by tabid close: function(tabid, force) { - var previousTabId, that = this; - - if (this.tabs[tabid] == null) return this; - - var handleClose = function(state) { - if (state == false && !force) return; - - // Get previous tab (will set as active) - // We're not closing the active tab - // So keep the current tab as active - if(tabid !== that.activeTab) { - previousTabId = that.activeTab; - } else { - previousTabId = that.getPreviousTab(that.tabs[tabid]); - } - - // Triger in tab - that.tabs[tabid].view.trigger("tab:close"); - that.tabs[tabid].view.off(); - - delete that.tabs[tabid].view; - delete that.tabs[tabid]; - - // Trigger global - that.trigger("tab:"+tabid+":close"); - that.trigger("tabs:close", tabid); - - if (!_.size(that.tabs) || !previousTabId) { - that.trigger("tabs:default"); - } else { - that.open(previousTabId); - } - - that.update(); - - return Q(tabid); - }; - - // Check that we can close the tab - return Q(that.tabs[tabid].view.tabCanBeClosed()).then(handleClose, handleClose); - }, - - // Close others tabs - closeOthers: function(tabid) { - var that = this; - - return _.reduce(this.tabs, function(prev, tab, oTabId) { - if (oTabId == tabid) return prev; - - return prev.then(function() { - return that.close(oTabId); - }); - }, Q()); + return Q(); }, // Open default new tab diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js new file mode 100644 index 00000000..f9c0a4e4 --- /dev/null +++ b/client/views/tabs/section.js @@ -0,0 +1,74 @@ +define([ + "hr/utils", + "hr/dom", + "hr/hr", + "collections/tabs" +], function(_, $, hr, Tabs) { + var TabItem = hr.List.Item.extend({ + + }); + var TabsList = hr.List.extend({ + Collection: Tabs, + Item: TabItem + }); + + + var TabsSectionContent = TabsList.extend({ + Item: TabItem + }); + + var TabsSectionHeader = TabsList.extend({ + Item: TabItem + }); + + + var TabsSectionView = hr.View.extend({ + className: "tabs-section", + defaults: { + + }, + events: { + + }, + + initialize: function() { + TabsSectionView.__super__.initialize.apply(this, arguments); + + this.tabs = new Tabs(); + + this.header = new TabsSectionHeader({ + collection: this.tabs + }); + this.content = new TabsSectionContent({ + collection: this.tabs + }); + + this.header.$el.appendTo(this.$el); + this.content.$el.appendTo(this.$el); + + return this; + }, + + /* + * Add a tab to this section + */ + addTab: function(tab) { + this.tabs.add(tab); + return this; + }, + + /* + * Remove a tab from this section + */ + removeTab: function(tab) { + this.tabs.remove(tab); + return this; + }, + + render: function() { + return this.ready(); + } + }); + + return TabsSectionView; +}); \ No newline at end of file From e17c608e20139c9938b96e5683212f33f503b031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 12:26:48 +0100 Subject: [PATCH 009/646] Add content in grid sections --- client/resources/stylesheets/ui/grid.less | 15 +++++++++++++++ client/views/tabs/grid.js | 20 ++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/client/resources/stylesheets/ui/grid.less b/client/resources/stylesheets/ui/grid.less index ef6adcc4..5b350506 100644 --- a/client/resources/stylesheets/ui/grid.less +++ b/client/resources/stylesheets/ui/grid.less @@ -8,6 +8,21 @@ .grid-section { position: absolute; + .grid-section-content { + position: absolute; + top: 0px; + right: 0px; + left: 0px; + bottom: 0px; + + &.with-bar-h { + right: 3px; + } + &.with-bar-v { + bottom: 3px; + } + } + .grid-resize-bar-v { position: absolute; right: 0px; diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js index 9536ea3e..7ed7132f 100644 --- a/client/views/tabs/grid.js +++ b/client/views/tabs/grid.js @@ -80,10 +80,10 @@ define([ // Add grid content x = 0; y = 0; _.each(this.views, function(view, i) { - var section + var $section, $content; // Container object - section = $("
", { + $section = $("
", { 'class': 'grid-section', 'css': { 'left': (x * sectionWidth)+"%", @@ -92,7 +92,14 @@ define([ 'height': sectionHeight+"%" } }); - section.appendTo(this.$el); + $section.appendTo(this.$el); + + // Content + $content = $("
", { + 'class': 'grid-section-content' + }); + $content.append(view.$el); + $content.appendTo($section); // Resize bar if (x < (layout.columns - 1)) { @@ -101,7 +108,8 @@ define([ 'class': "grid-resize-bar-h", 'mousedown': this.resizerHandler(x, y, "h") }); - hBar.appendTo(section); + hBar.appendTo($section); + $content.addClass("with-bar-h"); } if (y < (layout.lines - 1)) { @@ -110,10 +118,10 @@ define([ 'class': "grid-resize-bar-v", 'mousedown': this.resizerHandler(x, y, "v") }); - vBar.appendTo(section); + vBar.appendTo($section); + $content.addClass("with-bar-v"); } - // Calcul next position x = x + 1; if (x >= layout.columns) { From 5415535409e8a6545a7abf02480c074f940a8ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 15:38:27 +0100 Subject: [PATCH 010/646] Add base theming for new tabs --- addons/cb.theme.dark/main.js | 20 +++------ client/core/themes.js | 7 +++- client/models/tab.js | 2 +- client/resources/stylesheets/main.less | 1 + .../resources/stylesheets/tabs/manager.less | 33 --------------- .../resources/stylesheets/tabs/section.less | 36 ++++++++++++++++ client/resources/stylesheets/ui/grid.less | 4 +- client/views/tabs/manager.js | 11 +++-- client/views/tabs/section.js | 15 ++++--- client/views/tabs/tab.js | 41 ++++++++----------- 10 files changed, 84 insertions(+), 86 deletions(-) create mode 100644 client/resources/stylesheets/tabs/section.less diff --git a/addons/cb.theme.dark/main.js b/addons/cb.theme.dark/main.js index 807df313..dc2c898d 100644 --- a/addons/cb.theme.dark/main.js +++ b/addons/cb.theme.dark/main.js @@ -20,21 +20,6 @@ define([ 'theme': aceTheme }, styles: { - // Scrollbar - /*scrollbar: { - 'background': "transparent", - 'width': "10px", - - corner: { - - }, - - thumb: { - 'background': "#4b505b" - } - },*/ - - // Top menubar menubar: { 'background': bgNormal, @@ -60,6 +45,11 @@ define([ } }, + // Body + body: { + 'background': bgDark + }, + // Tabs tabs: { section: { diff --git a/client/core/themes.js b/client/core/themes.js index 43d3443d..d5a05be5 100644 --- a/client/core/themes.js +++ b/client/core/themes.js @@ -88,10 +88,13 @@ define([ 'lateralbar commands': ".cb-lateralbar .lateral-commands", 'lateralbar body': ".cb-lateralbar .lateral-body", + // body + 'body': ".cb-body", + // tabs 'tabs section': ".cb-tabs .section", - 'tabs header': ".cb-tabs .section .tabs-header", - 'tabs content': ".cb-tabs .section .tabs-content", + 'tabs header': ".cb-tabs .tabs-section .tabs-section-header", + 'tabs content': ".cb-tabs .tabs-section .tabs-section-content", 'tabs tab': ".component-tab", // operations diff --git a/client/models/tab.js b/client/models/tab.js index 3d681791..99f04c3b 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -6,7 +6,7 @@ define([ defaults: { "id": null, "title": "", - "state": null, + "state": "", "active": false } }); diff --git a/client/resources/stylesheets/main.less b/client/resources/stylesheets/main.less index 9b0a603f..031cda31 100644 --- a/client/resources/stylesheets/main.less +++ b/client/resources/stylesheets/main.less @@ -6,6 +6,7 @@ @import "tabs/tab.less"; @import "tabs/base.less"; +@import "tabs/section.less"; @import "tabs/manager.less"; @import "panels/list.less"; diff --git a/client/resources/stylesheets/tabs/manager.less b/client/resources/stylesheets/tabs/manager.less index bba22e57..fecb8205 100644 --- a/client/resources/stylesheets/tabs/manager.less +++ b/client/resources/stylesheets/tabs/manager.less @@ -3,37 +3,4 @@ width: 100%; height: 100%; overflow: hidden; - - .section { - position: absolute; - top: 0px; - bottom: 0px; - left: 0px; - border-bottom: 1px solid @tabSeparationColor; - border-right: 1px solid @tabSeparationColor; - - &.dragover { - opacity: 0.6; - } - - .tabs-header { - position: absolute; - top: 0px; - height: @tabHeight; - right: 0px; - left: 0px; - background: @tabBackground; - overflow: hidden; - } - - .tabs-content { - position: absolute; - top: @tabHeight - 1px; - bottom: 0px; - right: 0px; - left: 0px; - z-index: 1; - background: @tabActiveColor; - } - } } \ No newline at end of file diff --git a/client/resources/stylesheets/tabs/section.less b/client/resources/stylesheets/tabs/section.less new file mode 100644 index 00000000..f0c19a96 --- /dev/null +++ b/client/resources/stylesheets/tabs/section.less @@ -0,0 +1,36 @@ +.tabs-section { + position: absolute; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + + &.dragover { + opacity: 0.6; + } + + .tabs-section-header { + position: absolute; + top: 0px; + height: @tabHeight; + right: 0px; + left: 0px; + background: @tabBackground; + overflow: hidden; + list-style: none; + margin: 0px; + padding: 0px; + } + + .tabs-section-content { + position: absolute; + top: @tabHeight - 1px; + bottom: 0px; + right: 0px; + left: 0px; + z-index: 1; + background: @tabActiveColor; + margin: 0px; + padding: 0px; + } +} \ No newline at end of file diff --git a/client/resources/stylesheets/ui/grid.less b/client/resources/stylesheets/ui/grid.less index 5b350506..b5fac366 100644 --- a/client/resources/stylesheets/ui/grid.less +++ b/client/resources/stylesheets/ui/grid.less @@ -29,7 +29,7 @@ height: 3px; left: 0px; bottom: 0px; - background: blue; + background: transparent; cursor: row-resize; } @@ -39,7 +39,7 @@ width: 3px; top: 0px; bottom: 0px; - background: red; + background: transparent; cursor: col-resize; } } diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 1f63d804..6a5ebb1f 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -2,7 +2,9 @@ define([ "hr/utils", "hr/dom", "hr/hr", + "models/command", + "models/tab", "collections/tabs", @@ -14,7 +16,7 @@ define([ "views/tabs/base", "views/tabs/grid", "views/tabs/section" -], function(_, $, hr, Command, Tabs, DragDrop, Keyboard, ContextMenu, TabView, TabPanelView, GridView, TabsSectionView) { +], function(_, $, hr, Command, Tab , Tabs, DragDrop, Keyboard, ContextMenu, TabView, TabPanelView, GridView, TabsSectionView) { // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", @@ -126,11 +128,14 @@ define([ } if (!tab) { - // Create tab object - tab = this.tabs.add({ + tab = new Tab({}, { 'id': options.uniqueId, 'title': options.title }); + tab.manager = this; + + // Create tab object + this.tabs.add(tab); // Create content view tab.view = new V(_.extend(construct || {}, { diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index f9c0a4e4..ab9584d8 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -2,23 +2,26 @@ define([ "hr/utils", "hr/dom", "hr/hr", - "collections/tabs" -], function(_, $, hr, Tabs) { + "collections/tabs", + "views/tabs/tab" +], function(_, $, hr, Tabs, TabHeaderItem) { var TabItem = hr.List.Item.extend({ }); var TabsList = hr.List.extend({ - Collection: Tabs, - Item: TabItem + Collection: Tabs }); var TabsSectionContent = TabsList.extend({ - Item: TabItem + className: "tabs-section-content", + Item: TabItem, + }); var TabsSectionHeader = TabsList.extend({ - Item: TabItem + className: "tabs-section-header", + Item: TabHeaderItem }); diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index 57ceb41a..3f67040c 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -8,7 +8,7 @@ define([ "utils/contextmenu" ], function(_, $, hr, Command, DragDrop, Keyboard, ContextMenu) { // Tab header - var TabView = hr.View.extend({ + var TabView = hr.List.extend({ className: "component-tab", defaults: { title: "", @@ -36,9 +36,6 @@ define([ var that = this; this.$el.attr("draggable", true); - this.tabid = this.options.tabid; - this.tabs = this.parent; - this.section = this.options.section || 0; // Context menu ContextMenu.add(this.$el, [ @@ -47,7 +44,7 @@ define([ 'type': "action", 'title': "New Tab", 'action': function() { - that.tabs.openDefaultNew(); + //that.tabs.openDefaultNew(); } }, { 'type': "divider" }, @@ -77,7 +74,7 @@ define([ } }, { 'type': "divider" }, - that.tabs.layoutCommand + that.model.manager.layoutCommand ]); return this; @@ -87,14 +84,12 @@ define([ render: function() { this.$el.empty(); - var tab = this.tabs.tabs[this.tabid]; - var inner = $("
", { "class": "inner", - "html": tab.title + "html": this.model.get("title") }).appendTo(this.$el); - var states = (tab.state || "").split(" "); + var states = this.model.get("state", "").split(" "); _.each(states, function(state) { if (state && this.states[state]) { $("", { @@ -102,15 +97,13 @@ define([ }).prependTo(inner); } }, this); - - if (this.options.close) { - $("", { - "class": "close", - "href": "#", - "html": "×" - }).prependTo(inner); - } + $("", { + "class": "close", + "href": "#", + "html": "×" + }).prependTo(inner); + return this.ready(); }, @@ -121,9 +114,9 @@ define([ // Set section setSection: function(section) { - this.section = section; + /*this.section = section; this.open(); - this.tabs.update(); + this.tabs.update();*/ }, // Create section @@ -135,13 +128,13 @@ define([ // (event) open open: function(e) { if (e != null) e.preventDefault(); - this.tabs.open(this.tabid); + //this.tabs.open(this.tabid); }, // (event) Drag start dragStart: function(e) { DragDrop.drag(e, "move"); - DragDrop.setData(e, "tab", this.tabid); + DragDrop.setData(e, "tab", this.model.id); }, // (event) close @@ -150,12 +143,12 @@ define([ e.preventDefault(); e.stopPropagation(); } - this.tabs.close(this.tabid, force); + //this.tabs.close(this.tabid, force); }, // (event) close others tabs closeOthers: function(e) { - this.tabs.closeOthers(this.tabid); + //this.tabs.closeOthers(this.tabid); } }); From 29cb2d7b716daf6c9f79a9425b80e98c3f7019e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 16:08:45 +0100 Subject: [PATCH 011/646] Add base for active/desactive tabs --- client/models/tab.js | 63 ++++++++++++++++++++ client/resources/stylesheets/tabs/base.less | 13 ++++- client/views/tabs/base.js | 24 ++++---- client/views/tabs/manager.js | 65 +++------------------ client/views/tabs/section.js | 10 +++- client/views/tabs/tab.js | 8 ++- 6 files changed, 110 insertions(+), 73 deletions(-) diff --git a/client/models/tab.js b/client/models/tab.js index 99f04c3b..0f6a3f83 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -8,6 +8,69 @@ define([ "title": "", "state": "", "active": false + }, + + initialize: function() { + Tab.__super__.initialize.apply(this, arguments); + this.manager = this.options.manager; + + if (!this.manager) { + throw "Need a manager to create a tab"; + } + }, + + // Return true if this tab is active + isActive: function() { + return this.get("active"); + }, + + // Active this tab + active: function() { + this.collection.each(function(tab) { + tab.set("active", tab.id == this.id); + }, this); + }, + + // Disactive + disactive: function() { + this.model.set("active", false); + }, + + // Close this tab + close: function(force) { + var that = this, tabid = this.id; + + var handleClose = function(state) { + if (!state && !force) return; + + if (!that.isActive()) { + // Change active tab + } + + // Triger in tab + that.view.trigger("tab:close"); + that.view.off(); + + delete that.view; + + // Trigger global + that.manager.trigger("tab:"+tabid+":close"); + that.manager.trigger("tabs:close", tabid); + + /*if (!_.size(that.tabs) || !previousTabId) { + that.trigger("tabs:default"); + } else { + that.open(previousTabId); + }*/ + + that.destroy(); + + return Q(tabid); + }; + + // Check that we can close the tab + return Q(this.view.tabCanBeClosed()) + .then(handleClose, handleClose); } }); diff --git a/client/resources/stylesheets/tabs/base.less b/client/resources/stylesheets/tabs/base.less index 7f9901cb..4a25a225 100644 --- a/client/resources/stylesheets/tabs/base.less +++ b/client/resources/stylesheets/tabs/base.less @@ -1,4 +1,4 @@ -.component-tab-panel { +.component-tab-content { position: absolute; width: 100%; height: 100%; @@ -12,6 +12,17 @@ &.active { display: block; } +} + +.component-tab-panel { + position: absolute; + width: 100%; + height: 100%; + position: absolute; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; .tab-panel-lateralopener { position: absolute; diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index 9a4ebc18..6815c999 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -39,13 +39,17 @@ define([ 'title': this.menuTitle, 'position': 1 }); - this.on("tab:state", function(active) { - this.menu.toggleFlag("hidden", !active); - }, this); - this.on("tab:close", function() { + menu.collection.add(this.menu); + + // Bind tab event + this.tab.on("destroy", function() { this.menu.destroy(); + this.trigger("tab:close"); + }, this); + this.tab.on("change:active", function() { + this.trigger("tab:state", this.tab.isActive()); + this.menu.toggleFlag("hidden", !this.tab.isActive()); }, this); - menu.collection.add(this.menu); // Keyboard shortcuts this.setShortcuts(this.shortcuts || {}); @@ -77,17 +81,17 @@ define([ // Close the tab closeTab: function(e, force) { if (e != null) e.preventDefault(); - //this.tabs.close(this.tabid, force); + this.tab.close(force); }, // Open the tab openTab: function(e) { - //this.tabs.open(this.tabid); + this.tab.active(); }, // Set tab title setTabTitle: function(t) { - //this.tab.set("title", t); + this.tab.set("title", t); return this; }, @@ -113,9 +117,7 @@ define([ // Return if is active isActiveTab: function() { - /*var active = this.tabs.getCurrentTab(); - return !(active == null || active.tabid != this.tabid);*/ - return false; + return this.tab.isActive(); }, // Check that tab can be closed diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 6a5ebb1f..8cf46a8e 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -128,11 +128,12 @@ define([ } if (!tab) { - tab = new Tab({}, { + tab = new Tab({ + 'manager': this + }, { 'id': options.uniqueId, 'title': options.title }); - tab.manager = this; // Create tab object this.tabs.add(tab); @@ -146,61 +147,9 @@ define([ this.getSection(options.section).addTab(tab); } - return tab.view; - - /*var tabid, tabinfos; - options = _.defaults(options || {}, { - silent: false, - render: true, - - title: "untitled", // Base title for the tab - type: "default", - uniqueId: null, // Unique id for unicity of the tab - close: true, // Enable/disable close button - open: true, // Open after creation - section: 0, // Section to open in - parentId: null // Parent tabid - }); - tabid = options.uniqueId != null ? this.checkTabExists(options.uniqueId) : null; - if (tabid == null) { - tabid = _.uniqueId("tab"); - tabinfos = { - "tabid": tabid, - "title": options.title, - "state": null, - "uniqueId": options.uniqueId, - "type": this.options.type, - "view": null, - "tab": null - }; - this.tabs[tabid] = tabinfos; - - this.tabs[tabid].tab = new TabView({ - "tabid": tabid, - "close": options.close, - "section": options.section - }, this); - - this.tabs[tabid].view = new V(_.extend(construct || {}, { - "tabid": tabid, - }), this); + if (options.open) tab.active(); - if (options.parentId != null) { - this.on("tab:"+options.parentId+":close", _.partial(this.tabs[tabid].tab.close, null, true), this.tabs[tabid].tab); - } - - this.tabs[tabid].view.trigger("tab:ready"); - - this.tabs[tabid].tab.update(); - this.tabs[tabid].view.update(); - - this.addComponent("tabs_tabs", this.tabs[tabid].tab); - this.addComponent("tabs_content", this.tabs[tabid].view); - - if (options.render) this.update(); - } - if (this.activeTab == null || options.open) this.open(tabid); - return this.tabs[tabid].view;*/ + return tab.view; }, // Open a tab by tabid @@ -211,7 +160,9 @@ define([ // Close a tab by tabid close: function(tabid, force) { - return Q(); + var tab = this.tabs.getById(tabid); + if (!tab) return Q.reject(new Error("Invalid tab")); + return tab.close(force); }, // Open default new tab diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index ab9584d8..6cedcde6 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -6,7 +6,15 @@ define([ "views/tabs/tab" ], function(_, $, hr, Tabs, TabHeaderItem) { var TabItem = hr.List.Item.extend({ - + className: "component-tab-content", + initialize: function() { + TabItem.__super__.initialize.apply(this, arguments); + this.$el.append(this.model.view.$el); + }, + render: function() { + this.$el.toggleClass("active", this.model.isActive()); + return this.ready(); + } }); var TabsList = hr.List.extend({ Collection: Tabs diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index 3f67040c..96792379 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -8,7 +8,7 @@ define([ "utils/contextmenu" ], function(_, $, hr, Command, DragDrop, Keyboard, ContextMenu) { // Tab header - var TabView = hr.List.extend({ + var TabView = hr.List.Item.extend({ className: "component-tab", defaults: { title: "", @@ -103,6 +103,8 @@ define([ "href": "#", "html": "×" }).prependTo(inner); + + this.$el.toggleClass("active", this.model.isActive()); return this.ready(); }, @@ -128,7 +130,7 @@ define([ // (event) open open: function(e) { if (e != null) e.preventDefault(); - //this.tabs.open(this.tabid); + this.model.active(); }, // (event) Drag start @@ -143,7 +145,7 @@ define([ e.preventDefault(); e.stopPropagation(); } - //this.tabs.close(this.tabid, force); + this.model.close(force); }, // (event) close others tabs From aac8d72a94c9d039edb362a216eca7dad30d9df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 17:26:41 +0100 Subject: [PATCH 012/646] Improve tab states --- client/models/tab.js | 55 ++++++++++++++++++++++++++++-------- client/views/tabs/file.js | 1 - client/views/tabs/manager.js | 1 + client/views/tabs/section.js | 1 + 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/client/models/tab.js b/client/models/tab.js index 0f6a3f83..222e2026 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -12,21 +12,49 @@ define([ initialize: function() { Tab.__super__.initialize.apply(this, arguments); + this.manager = this.options.manager; + this._closed = false; if (!this.manager) { throw "Need a manager to create a tab"; } }, + // Get previous tab + getNthTab: function(collection, n) { + var prev = null; + collection = collection || this.section; + + var i = collection.indexOf(this); + + // tab not found + if (i < 0) return; + i = i + n; + + if (i < 0 || i >= collection.size()) return null; + + return collection.models[i]; + }, + + // Get previous tab + prevTab: function(collection) { + return this.getNthTab(collection, -1); + }, + + // Get next tab + nextTab: function(collection) { + return this.getNthTab(collection, +1); + }, + // Return true if this tab is active isActive: function() { return this.get("active"); }, - // Active this tab + // Active this tab on the current section active: function() { - this.collection.each(function(tab) { + this.section.each(function(tab) { tab.set("active", tab.id == this.id); }, this); }, @@ -38,13 +66,19 @@ define([ // Close this tab close: function(force) { - var that = this, tabid = this.id; + var that = this, tabid = this.id, prev = null; + + if (this._closed) return Q.reject(new Error("Tab already been closed")); + var handleClose = function(state) { - if (!state && !force) return; + if ((!state && !force) || this._closed) return; + + that._closed = true; - if (!that.isActive()) { - // Change active tab + if (that.isActive()) { + // Change active tab to an another tab with priority order: + prev = that.prevTab() || that.nextTab() || that.prevTab(that.manager.tabs) || that.nextTab(that.manager.tabs); } // Triger in tab @@ -57,14 +91,13 @@ define([ that.manager.trigger("tab:"+tabid+":close"); that.manager.trigger("tabs:close", tabid); - /*if (!_.size(that.tabs) || !previousTabId) { - that.trigger("tabs:default"); - } else { - that.open(previousTabId); - }*/ + if (prev) prev.active(); that.destroy(); + // No tab -> open default + if (that.manager.tabs.size() == 0) that.manager.trigger("tabs:default"); + return Q(tabid); }; diff --git a/client/views/tabs/file.js b/client/views/tabs/file.js index 62243b95..4ad84616 100644 --- a/client/views/tabs/file.js +++ b/client/views/tabs/file.js @@ -63,7 +63,6 @@ define([ /* Adapt the tab to the file (title, ...) */ adaptFile: function() { this.setTabTitle(this.model.get("name", "loading...")); - this.setTabType(this.model.isDirectory() ? "directory" : "file"); this.setTabId(this.fileHandler.id+":"+this.model.syncEnvId()); return this; }, diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 8cf46a8e..d335c575 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -142,6 +142,7 @@ define([ tab.view = new V(_.extend(construct || {}, { "tab": tab, }), this); + tab.view.update(); // Add to section this.getSection(options.section).addTab(tab); diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index 6cedcde6..7cd44b1b 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -64,6 +64,7 @@ define([ * Add a tab to this section */ addTab: function(tab) { + tab.section = this.tabs; this.tabs.add(tab); return this; }, From 78dba2e1c8c3367b79d3f7cfd664b46bd7185b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 17:46:48 +0100 Subject: [PATCH 013/646] Remove empty tabs sections and add action to lose others tabs --- client/models/tab.js | 17 ++++++++++++-- client/views/tabs/manager.js | 43 +++++++++++++++++------------------- client/views/tabs/tab.js | 4 ++-- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/client/models/tab.js b/client/models/tab.js index 222e2026..ac6c68d5 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -95,15 +95,28 @@ define([ that.destroy(); + that.manager.checkSections(); + // No tab -> open default - if (that.manager.tabs.size() == 0) that.manager.trigger("tabs:default"); + if (that.manager.tabs.size() == 0) that.manager.openDefault(); return Q(tabid); }; // Check that we can close the tab return Q(this.view.tabCanBeClosed()) - .then(handleClose, handleClose); + .then(handleClose, handleClose) + .fail(function(err) { + console.log("error !!! ", err); + }); + }, + + // Close all other tabs in the section + closeOthers: function() { + this.section.each(function(tab) { + if (tab.id != this.id) tab.close(); + }, this); + this.active(); } }); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index d335c575..32584e3f 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -36,9 +36,6 @@ define([ var that = this; TabsView.__super__.initialize.apply(this, arguments); - // Sections - this.sections = {}; - // Current layout this.layout = null; // null: mode auto this.grid = new GridView(); @@ -73,20 +70,23 @@ define([ // Return a section by its id getSection: function(id) { - if (!this.sections[id]) { - this.sections[id] = new TabsSectionView(); - this.sections[id].sectionId = id; - this.grid.addView(this.sections[id]); + var s = _.find(this.grid.views, function(section) { + return section.sectionId == id; + }); + + if (!s) { + s = new TabsSectionView(); + s.sectionId = id; + this.grid.addView(s); } - return this.sections[id]; + return s; }, // Remove a section removeSection: function(id) { var s = this.getSection(id); this.grid.removeView(s); - delete this.sections[id]; return this; }, @@ -153,21 +153,8 @@ define([ return tab.view; }, - // Open a tab by tabid - open: function(tabid) { - - return this; - }, - - // Close a tab by tabid - close: function(tabid, force) { - var tab = this.tabs.getById(tabid); - if (!tab) return Q.reject(new Error("Invalid tab")); - return tab.close(force); - }, - // Open default new tab - openDefaultNew: function(e) { + openDefault: function(e) { this.trigger("tabs:opennew"); }, @@ -176,6 +163,16 @@ define([ this.grid.setLayout(l); this.trigger("layout", l); this.update(); + }, + + // Check sections + // -> check that there is no empty sections + checkSections: function() { + _.each(this.grid.views, function(section) { + if (section.tabs.size() > 0) return; + + this.grid.removeView(section); + }, this); } }, { Panel: TabPanelView diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index 96792379..b2de25f9 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -44,7 +44,7 @@ define([ 'type': "action", 'title': "New Tab", 'action': function() { - //that.tabs.openDefaultNew(); + that.model.manager.openDefault(); } }, { 'type': "divider" }, @@ -150,7 +150,7 @@ define([ // (event) close others tabs closeOthers: function(e) { - //this.tabs.closeOthers(this.tabid); + this.model.closeOthers(); } }); From 5731b22dd13fa2abf5e762f2ef2c18749a83017b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 27 Feb 2014 18:11:50 +0100 Subject: [PATCH 014/646] Base for tab ordering --- client/models/tab.js | 10 ++++--- client/resources/stylesheets/tabs/tab.less | 4 +++ client/views/tabs/base.js | 16 ++++++----- client/views/tabs/manager.js | 8 ++++++ client/views/tabs/tab.js | 32 ++++++++++++++++++++-- 5 files changed, 56 insertions(+), 14 deletions(-) diff --git a/client/models/tab.js b/client/models/tab.js index ac6c68d5..aff7be38 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -64,6 +64,11 @@ define([ this.model.set("active", false); }, + // Change tab section + changeSection: function(section) { + return this.manager.changeTabSection(this, section); + }, + // Close this tab close: function(force) { var that = this, tabid = this.id, prev = null; @@ -105,10 +110,7 @@ define([ // Check that we can close the tab return Q(this.view.tabCanBeClosed()) - .then(handleClose, handleClose) - .fail(function(err) { - console.log("error !!! ", err); - }); + .then(handleClose, handleClose); }, // Close all other tabs in the section diff --git a/client/resources/stylesheets/tabs/tab.less b/client/resources/stylesheets/tabs/tab.less index c5789afc..851b59f0 100644 --- a/client/resources/stylesheets/tabs/tab.less +++ b/client/resources/stylesheets/tabs/tab.less @@ -7,6 +7,10 @@ margin: 0px; background: @tabColor; + &.move { + position: absolute; + } + .inner { .box-sizing(border-box); diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index 6815c999..84ad072f 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -111,7 +111,7 @@ define([ // Set tab title setTabId: function(t) { - //this.tabs.tabs[this.tabid].uniqueId = t; + //this.model.set("id", t); return this; }, @@ -128,17 +128,19 @@ define([ // Goto tabGotoPrevious: function(e) { if (e) e.preventDefault(); - /*var that = this; + var that = this; setTimeout(function() { - that.tabs.open(that.tabs.getPreviousTab(that.tabid)); - }, 0);*/ + var p = that.tab.prevTab(); + if (p) p.active(); + }, 0); }, tabGotoNext: function(e) { if (e) e.preventDefault(); - /*var that = this; + var that = this; setTimeout(function() { - that.tabs.open(that.tabs.getNextTab(that.tabid)); - }, 0);*/ + var p = that.tab.nextTab(); + if (p) p.active(); + }, 0); } }); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 32584e3f..6fc756df 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -173,6 +173,14 @@ define([ this.grid.removeView(section); }, this); + }, + + // Change tab section + changeTabSection: function(tab, section) { + section = this.getSection(section); + + tab.section.remove(tab); + section.addTab(tab); } }, { Panel: TabPanelView diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index b2de25f9..fb42da5b 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -34,9 +34,36 @@ define([ TabView.__super__.initialize.apply(this, arguments); var that = this; + var $document = $(document); this.$el.attr("draggable", true); + this.$el.mousedown(function(e) { + e.preventDefault(); + var oX = e.pageX; + var poX = that.$el.position().left; + + var f = function(e) { + dx = oX - e.pageX; + + that.$el.addClass("move"); + that.$el.css({ + 'left': poX-dx + }); + + console.log("movement ", dx); + }; + + $document.mousemove(f); + $document.mouseup(function(e) { + that.$el.removeClass("move"); + that.$el.css({ + 'left': null + }); + $document.unbind('mousemove', f); + }); + }); + // Context menu ContextMenu.add(this.$el, [ { @@ -116,9 +143,8 @@ define([ // Set section setSection: function(section) { - /*this.section = section; - this.open(); - this.tabs.update();*/ + this.model.changeSection(section); + this.model.active(); }, // Create section From 614f525c7cbdfcf04428010755e83098f0774461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 28 Feb 2014 12:16:27 +0100 Subject: [PATCH 015/646] Detach views before update grid layout --- client/views/tabs/grid.js | 7 +++++++ client/views/tabs/tab.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js index 7ed7132f..f1ab4762 100644 --- a/client/views/tabs/grid.js +++ b/client/views/tabs/grid.js @@ -69,6 +69,13 @@ define([ */ render: function() { var x, y; + + // Detach view + _.each(this.views, function(view) { + view.$el.detach(); + }); + + // Clear the view this.$el.empty(); // Calcul layout diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index fb42da5b..94019e3e 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -58,7 +58,7 @@ define([ $document.mouseup(function(e) { that.$el.removeClass("move"); that.$el.css({ - 'left': null + 'left': "auto" }); $document.unbind('mousemove', f); }); From 106965c53119b09bfc22efe355aa3810805a0ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 28 Feb 2014 14:10:00 +0100 Subject: [PATCH 016/646] Add base for drag and drop tabs between sections --- client/collections/tabs.js | 9 +++- .../resources/stylesheets/tabs/section.less | 10 ++++ client/resources/stylesheets/tabs/tab.less | 3 +- client/utils/dragdrop.js | 5 +- client/views/tabs/manager.js | 31 ++++++++++-- client/views/tabs/section.js | 49 ++++++++++++++++++- client/views/tabs/tab.js | 23 ++++++--- 7 files changed, 113 insertions(+), 17 deletions(-) diff --git a/client/collections/tabs.js b/client/collections/tabs.js index 591f0b79..20f6b6f4 100644 --- a/client/collections/tabs.js +++ b/client/collections/tabs.js @@ -11,7 +11,14 @@ define([ return this.find(function(tab) { return tab.id == id; }); - } + }, + + // Return current active tab + getActive: function() { + return this.find(function(tab) { + return tab.get("active"); + }); + }, }); return Tabs; diff --git a/client/resources/stylesheets/tabs/section.less b/client/resources/stylesheets/tabs/section.less index f0c19a96..d9d203b4 100644 --- a/client/resources/stylesheets/tabs/section.less +++ b/client/resources/stylesheets/tabs/section.less @@ -7,6 +7,16 @@ &.dragover { opacity: 0.6; + + &:after { + content:''; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 1; + } } .tabs-section-header { diff --git a/client/resources/stylesheets/tabs/tab.less b/client/resources/stylesheets/tabs/tab.less index 851b59f0..18d05d11 100644 --- a/client/resources/stylesheets/tabs/tab.less +++ b/client/resources/stylesheets/tabs/tab.less @@ -8,7 +8,8 @@ background: @tabColor; &.move { - position: absolute; + position: fixed; + z-index: 100000; } diff --git a/client/utils/dragdrop.js b/client/utils/dragdrop.js index 88b1f018..f16974ed 100644 --- a/client/utils/dragdrop.js +++ b/client/utils/dragdrop.js @@ -34,7 +34,7 @@ define([ dragover: function(e, effect) { if (e != null) { var ev = e.originalEvent; - if (ev.preventDefault) ev.preventDefault(); + e.preventDefault(); ev.dataTransfer.dropEffect = effect; return true; } else { @@ -48,8 +48,7 @@ define([ */ drop: function(e, effect) { if (e != null) { - var ev = e.originalEvent; - if (ev.stopPropagation) ev.stopPropagation(); + e.stopPropagation(); return true; } else { return false; diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 6fc756df..498fa9f2 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -38,7 +38,7 @@ define([ // Current layout this.layout = null; // null: mode auto - this.grid = new GridView(); + this.grid = new GridView({}, this); this.grid.$el.appendTo(this.$el); // Commands @@ -75,7 +75,7 @@ define([ }); if (!s) { - s = new TabsSectionView(); + s = new TabsSectionView({}, this); s.sectionId = id; this.grid.addView(s); } @@ -169,18 +169,39 @@ define([ // -> check that there is no empty sections checkSections: function() { _.each(this.grid.views, function(section) { - if (section.tabs.size() > 0) return; - - this.grid.removeView(section); + // If empty remove it + if (section.tabs.size() == 0) { + this.grid.removeView(section); + return; + } + + // If no active tab + if (section.tabs.getActive() == null) { + section.tabs.first().active(); + } + }, this); }, // Change tab section changeTabSection: function(tab, section) { + if (_.isString(tab)) tab = this.tabs.getById(tab); + if (!tab) return false; section = this.getSection(section); + // Remove from old section tab.section.remove(tab); + + // Add to new section section.addTab(tab); + + // Active + tab.active(); + + // Check sections to remove empty one + this.checkSections(); + + return true; } }, { Panel: TabPanelView diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index 7cd44b1b..5a070952 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -2,9 +2,10 @@ define([ "hr/utils", "hr/dom", "hr/hr", + "utils/dragdrop", "collections/tabs", "views/tabs/tab" -], function(_, $, hr, Tabs, TabHeaderItem) { +], function(_, $, hr, DragDrop, Tabs, TabHeaderItem) { var TabItem = hr.List.Item.extend({ className: "component-tab-content", initialize: function() { @@ -45,6 +46,8 @@ define([ initialize: function() { TabsSectionView.__super__.initialize.apply(this, arguments); + var that = this; + this.tabs = new Tabs(); this.header = new TabsSectionHeader({ @@ -57,6 +60,50 @@ define([ this.header.$el.appendTo(this.$el); this.content.$el.appendTo(this.$el); + var ignoreNextLeave = false; + + this.$el.on('dragenter', function(e) { + if (e.target !== this) { + ignoreNextLeave = true; + } + + console.log("drag enter", that.cid, e); + if (DragDrop.checkDrop(e, "tab")) { + that.$el.addClass("dragover"); + } + }); + + this.$el.on('dragover', function(e) { + if (DragDrop.checkDrop(e, "tab")) { + e.preventDefault(); + DragDrop.dragover(e, 'move'); + } + }); + + this.$el.on('dragleave', function(e) { + if (ignoreNextLeave) { + ignoreNextLeave = false; + return; + } + + console.log("drag leave", that.cid, e); + if (DragDrop.checkDrop(e, "tab")) { + that.$el.removeClass("dragover"); + } + }); + + this.$el.on('drop', function(e) { + console.log("drop ", e); + if (DragDrop.checkDrop(e, "tab")) { + that.$el.removeClass("dragover"); + DragDrop.drop(e); + console.log("Drop ", DragDrop.getData(e, "tab"), that); + + that.parent.changeTabSection(DragDrop.getData(e, "tab"), that.sectionId); + //this.tabs[DragDrop.getData(e, "tab")].tab.setSection(section); + } + }); + return this; }, diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index 94019e3e..ea6e240c 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -7,6 +7,7 @@ define([ "utils/keyboard", "utils/contextmenu" ], function(_, $, hr, Command, DragDrop, Keyboard, ContextMenu) { + // Tab header var TabView = hr.List.Item.extend({ className: "component-tab", @@ -38,31 +39,41 @@ define([ this.$el.attr("draggable", true); - this.$el.mousedown(function(e) { + /*this.$el.mousedown(function(e) { e.preventDefault(); + var dx, dy; var oX = e.pageX; - var poX = that.$el.position().left; + var oY = e.pageY; + var poX = that.$el.offset().left; + var poY = that.$el.offset().top; var f = function(e) { dx = oX - e.pageX; + dy = oY - e.pageY; that.$el.addClass("move"); that.$el.css({ - 'left': poX-dx + 'left': poX-dx, + 'top': poY }); - console.log("movement ", dx); + if (Math.abs(dy) > 50) { + that.$el.css({ + 'top': poY-dy + }); + } }; $document.mousemove(f); $document.mouseup(function(e) { that.$el.removeClass("move"); that.$el.css({ - 'left': "auto" + 'left': "auto", + 'top': "auto" }); $document.unbind('mousemove', f); }); - }); + });*/ // Context menu ContextMenu.add(this.$el, [ From e1728a45e07e2ea2d7f98f21da8fc1900351cc1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 28 Feb 2014 16:04:52 +0100 Subject: [PATCH 017/646] Improve drag and drop stability for tab sections --- client/resources/stylesheets/tabs/section.less | 6 +++++- client/views/tabs/manager.js | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/client/resources/stylesheets/tabs/section.less b/client/resources/stylesheets/tabs/section.less index d9d203b4..1e5ca18b 100644 --- a/client/resources/stylesheets/tabs/section.less +++ b/client/resources/stylesheets/tabs/section.less @@ -6,7 +6,7 @@ right: 0px; &.dragover { - opacity: 0.6; + opacity: 0.4; &:after { content:''; @@ -17,6 +17,10 @@ right: 0; z-index: 1; } + + * { + pointer-events:none; + } } .tabs-section-header { diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 498fa9f2..19c1b5ff 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -187,6 +187,7 @@ define([ changeTabSection: function(tab, section) { if (_.isString(tab)) tab = this.tabs.getById(tab); if (!tab) return false; + section = this.getSection(section); // Remove from old section From 274d8fe76883a9d7d84b16e8ee003920848a3bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 28 Feb 2014 16:23:51 +0100 Subject: [PATCH 018/646] Trigger event about change in grid layout --- addons/cb.terminal/views/tab.js | 3 +++ client/views/tabs/grid.js | 12 +++++++++++ client/views/tabs/section.js | 8 ++++++-- client/views/tabs/tab.js | 36 --------------------------------- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/addons/cb.terminal/views/tab.js b/addons/cb.terminal/views/tab.js index a366f66d..2854ff31 100644 --- a/addons/cb.terminal/views/tab.js +++ b/addons/cb.terminal/views/tab.js @@ -72,6 +72,9 @@ define([ this.focus(); } }, this); + this.on("tab:layout", function() { + that.resize(); + }, this); this.setTabTitle("Terminal - "+this.sessionId); diff --git a/client/views/tabs/grid.js b/client/views/tabs/grid.js index f1ab4762..5bafa1e3 100644 --- a/client/views/tabs/grid.js +++ b/client/views/tabs/grid.js @@ -64,6 +64,15 @@ define([ }; }, + /* + * 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 */ @@ -107,6 +116,7 @@ define([ }); $content.append(view.$el); $content.appendTo($section); + view.trigger("grid:layout"); // Resize bar if (x < (layout.columns - 1)) { @@ -232,6 +242,8 @@ define([ // Resize current line size($sections, sCurrentN+"%"); + this.signalLayout(); + return true; }, diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index 5a070952..e9769980 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -97,13 +97,17 @@ define([ if (DragDrop.checkDrop(e, "tab")) { that.$el.removeClass("dragover"); DragDrop.drop(e); - console.log("Drop ", DragDrop.getData(e, "tab"), that); that.parent.changeTabSection(DragDrop.getData(e, "tab"), that.sectionId); - //this.tabs[DragDrop.getData(e, "tab")].tab.setSection(section); } }); + this.on("grid:layout", function() { + this.tabs.each(function(tab) { + tab.view.trigger("tab:layout"); + }); + }, this); + return this; }, diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index ea6e240c..c4350944 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -39,42 +39,6 @@ define([ this.$el.attr("draggable", true); - /*this.$el.mousedown(function(e) { - e.preventDefault(); - var dx, dy; - var oX = e.pageX; - var oY = e.pageY; - var poX = that.$el.offset().left; - var poY = that.$el.offset().top; - - var f = function(e) { - dx = oX - e.pageX; - dy = oY - e.pageY; - - that.$el.addClass("move"); - that.$el.css({ - 'left': poX-dx, - 'top': poY - }); - - if (Math.abs(dy) > 50) { - that.$el.css({ - 'top': poY-dy - }); - } - }; - - $document.mousemove(f); - $document.mouseup(function(e) { - that.$el.removeClass("move"); - that.$el.css({ - 'left': "auto", - 'top': "auto" - }); - $document.unbind('mousemove', f); - }); - });*/ - // Context menu ContextMenu.add(this.$el, [ { From 13bf874684aaa06803d9aa22caa134a2182ee9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 28 Feb 2014 18:54:50 +0100 Subject: [PATCH 019/646] Fix menu of current tab --- addons/cb.terminal/views/tab.js | 1 + client/models/tab.js | 1 + client/views/{tabs => }/grid.js | 0 client/views/tabs/base.js | 8 +++++--- client/views/tabs/manager.js | 4 ++-- 5 files changed, 9 insertions(+), 5 deletions(-) rename client/views/{tabs => }/grid.js (100%) diff --git a/addons/cb.terminal/views/tab.js b/addons/cb.terminal/views/tab.js index 2854ff31..efeed5a0 100644 --- a/addons/cb.terminal/views/tab.js +++ b/addons/cb.terminal/views/tab.js @@ -163,6 +163,7 @@ define([ // Block propagation of clicks to sublevel clickTerm: function(e) { e.stopPropagation(); + this.openTab(); } }); diff --git a/client/models/tab.js b/client/models/tab.js index aff7be38..4cf596fc 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -57,6 +57,7 @@ define([ this.section.each(function(tab) { tab.set("active", tab.id == this.id); }, this); + this.manager.trigger("active", this); }, // Disactive diff --git a/client/views/tabs/grid.js b/client/views/grid.js similarity index 100% rename from client/views/tabs/grid.js rename to client/views/grid.js diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index 84ad072f..06e0d349 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -46,9 +46,11 @@ define([ this.menu.destroy(); this.trigger("tab:close"); }, this); - this.tab.on("change:active", function() { - this.trigger("tab:state", this.tab.isActive()); - this.menu.toggleFlag("hidden", !this.tab.isActive()); + this.tab.manager.on("active", function(tab) { + var state = tab.id == this.tab.id; + + this.trigger("tab:state", state); + this.menu.toggleFlag("hidden", !state); }, this); // Keyboard shortcuts diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 19c1b5ff..53f1e4a1 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -12,11 +12,11 @@ define([ "utils/keyboard", "utils/contextmenu", + "views/grid", "views/tabs/tab", "views/tabs/base", - "views/tabs/grid", "views/tabs/section" -], function(_, $, hr, Command, Tab , Tabs, DragDrop, Keyboard, ContextMenu, TabView, TabPanelView, GridView, TabsSectionView) { +], function(_, $, hr, Command, Tab , Tabs, DragDrop, Keyboard, ContextMenu, GridView, TabView, TabPanelView, TabsSectionView) { // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", From 91eea2893b9e11d8bf78cc620a99038ecaa35de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 11:29:07 +0100 Subject: [PATCH 020/646] Fix tab id changement --- addons/cb.terminal/views/tab.js | 2 ++ client/models/file.js | 3 ++- client/views/tabs/base.js | 2 +- client/views/tabs/section.js | 9 +++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/addons/cb.terminal/views/tab.js b/addons/cb.terminal/views/tab.js index efeed5a0..92f55369 100644 --- a/addons/cb.terminal/views/tab.js +++ b/addons/cb.terminal/views/tab.js @@ -163,6 +163,8 @@ define([ // Block propagation of clicks to sublevel clickTerm: function(e) { e.stopPropagation(); + + // We stop propagation so we need to active the tab manually this.openTab(); } }); diff --git a/client/models/file.js b/client/models/file.js index 9e52dfcb..882739a1 100644 --- a/client/models/file.js +++ b/client/models/file.js @@ -42,6 +42,7 @@ define([ this.content = null; this.modified = false; this._loading = false; + this._uniqueId = Date.now()+_.uniqueId("file"); this.read = this.download; // Change in codebox : file deleted @@ -304,7 +305,7 @@ define([ */ syncEnvId: function() { if (this.isNewfile()) { - return "temporary://"+Date.now()+_.uniqueId("file"); + return "temporary://"+this._uniqueId; } return "file://"+this.path(); }, diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index 06e0d349..fd545e39 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -113,7 +113,7 @@ define([ // Set tab title setTabId: function(t) { - //this.model.set("id", t); + this.tab.set("id", t); return this; }, diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index e9769980..d735234a 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -8,6 +8,9 @@ define([ ], function(_, $, hr, DragDrop, Tabs, TabHeaderItem) { var TabItem = hr.List.Item.extend({ className: "component-tab-content", + events: { + "click": "click" + }, initialize: function() { TabItem.__super__.initialize.apply(this, arguments); this.$el.append(this.model.view.$el); @@ -15,8 +18,14 @@ define([ render: function() { this.$el.toggleClass("active", this.model.isActive()); return this.ready(); + }, + + // On click focus the tab + click: function() { + this.model.active(); } }); + var TabsList = hr.List.extend({ Collection: Tabs }); From adc3b5eaaf07f3ebf759e1d66e5cbbe9bf8e9088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 12:34:32 +0100 Subject: [PATCH 021/646] Use new drag and drop module instead of html5 --- client/resources/stylesheets/tabs/tab.less | 5 + client/utils/dragdrop.js | 213 +++++++++++++-------- client/views/tabs/manager.js | 3 +- client/views/tabs/section.js | 45 +---- client/views/tabs/tab.js | 18 +- 5 files changed, 151 insertions(+), 133 deletions(-) diff --git a/client/resources/stylesheets/tabs/tab.less b/client/resources/stylesheets/tabs/tab.less index 18d05d11..5a933ed3 100644 --- a/client/resources/stylesheets/tabs/tab.less +++ b/client/resources/stylesheets/tabs/tab.less @@ -10,6 +10,11 @@ &.move { position: fixed; z-index: 100000; + pointer-events:none; + + * { + pointer-events:none; + } } diff --git a/client/utils/dragdrop.js b/client/utils/dragdrop.js index f16974ed..05e0e4a2 100644 --- a/client/utils/dragdrop.js +++ b/client/utils/dragdrop.js @@ -1,98 +1,143 @@ define([ + 'hr/hr', 'hr/dom' -], function ($) { - var DragDrop = { - /* - * Init drag on a event - * @e : dom event - * @effect : effect allowed : copy, ... - */ - drag: function(e, effect) { - if (e != null) { - var ev = e.originalEvent; - ev.dataTransfer.effectAllowed = effect; - return true; - } else { - return false; - } - }, +], function (hr, $) { + var DropArea = hr.Class.extend({ + defaults: { + // View for this area + view: null, + + // Class when drop data + className: "dragover", - /* - * Check drag is gogod for droping - * @e : dom event - * @attr : data require - */ - checkDrop: function(e, attr) { - return _.contains(e.originalEvent.dataTransfer.types, attr); + // Draggable type + dragType: null, + + // Handler for drop + handler: null }, - /* - * Init dragover on a event - * @e : dom event - * @effect : effect allowed : copy, ... - */ - dragover: function(e, effect) { - if (e != null) { - var ev = e.originalEvent; - e.preventDefault(); - ev.dataTransfer.dropEffect = effect; - return true; - } else { - return false; - } + 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('mouseenter', function(e) { + if (that.dragType.isDragging()) { + that.dragType.enterDropArea(that); + that.$el.addClass("dragover"); + } + }); + + this.$el.on('mouseleave', function(e) { + that.$el.removeClass("dragover"); + }); + + 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; + + // Drop handler + this.drop = null; }, - /* - * Init drop event - * @e : dom event - */ - drop: function(e, effect) { - if (e != null) { - e.stopPropagation(); - return true; - } else { - return false; - } + // Is currently dragging data + isDragging: function() { + return this.data != null; }, - /* - * Define drag data - * @e : dom event - * @attr : name of the data - * @data : data - */ - setData: function(e, attr, data) { - if (e != null) { - var ev = e.originalEvent; - ev.dataTransfer.setData(attr, JSON.stringify(data)); - return true; - } else { - return false; - } + // Enter drop area + enterDropArea: function(area) { + this.drop = area; }, - /* - * Get drag data - * @e : dom event - * @attr : name of the data - */ - getData: function(e, attr) { - if (e != null) { - var ev = e.originalEvent; - var o = ev.dataTransfer.getData(attr); - if (o == null) { - return null; - } - try { - var obj = JSON.parse(o); - return obj; - } catch(err) { - return null; - } - } else { - return null; - } + // Exit drop area + exitDropArea: function() { + this.drop = null; }, + + // 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 + }); + if (options.el) $el = $(options.el); + if (options.view) $el = options.view.$el, data = options.view; + if (options.data) data = options.data; + + $el.mousedown(function(e) { + e.preventDefault(); + var dx, dy; + var oX = e.pageX; + var oY = e.pageY; + var poX = $el.offset().left; + var poY = $el.offset().top; + + that.data = data; + + var f = function(e) { + dx = oX - e.pageX; + dy = oY - e.pageY; + + $el.addClass("move"); + $el.css({ + 'left': poX-dx, + 'top': poY + }); + + if (Math.abs(dy) > 50) { + $el.css({ + 'top': poY-dy + }); + } + }; + + $document.mousemove(f); + $document.mouseup(function(e) { + if (that.drop) { + that.drop.trigger("drop", that.data); + } + + that.data = null; + that.drop = null; + + $el.removeClass("move"); + $el.css({ + 'left': "auto", + 'top': "auto" + }); + $document.unbind('mousemove', f); + }); + }); + } + }); + + return { + DropArea: DropArea, + DraggableType: DraggableType }; - return DragDrop; }); \ No newline at end of file diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 53f1e4a1..21617945 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -8,7 +8,6 @@ define([ "collections/tabs", - "utils/dragdrop", "utils/keyboard", "utils/contextmenu", @@ -16,7 +15,7 @@ define([ "views/tabs/tab", "views/tabs/base", "views/tabs/section" -], function(_, $, hr, Command, Tab , Tabs, DragDrop, Keyboard, ContextMenu, GridView, TabView, TabPanelView, TabsSectionView) { +], function(_, $, hr, Command, Tab , Tabs, Keyboard, ContextMenu, GridView, TabView, TabPanelView, TabsSectionView) { // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index d735234a..99c1d9d2 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -5,7 +5,7 @@ define([ "utils/dragdrop", "collections/tabs", "views/tabs/tab" -], function(_, $, hr, DragDrop, Tabs, TabHeaderItem) { +], function(_, $, hr, dnd, Tabs, TabHeaderItem) { var TabItem = hr.List.Item.extend({ className: "component-tab-content", events: { @@ -71,43 +71,12 @@ define([ var ignoreNextLeave = false; - this.$el.on('dragenter', function(e) { - if (e.target !== this) { - ignoreNextLeave = true; - } - - console.log("drag enter", that.cid, e); - if (DragDrop.checkDrop(e, "tab")) { - that.$el.addClass("dragover"); - } - }); - - this.$el.on('dragover', function(e) { - if (DragDrop.checkDrop(e, "tab")) { - e.preventDefault(); - DragDrop.dragover(e, 'move'); - } - }); - - this.$el.on('dragleave', function(e) { - if (ignoreNextLeave) { - ignoreNextLeave = false; - return; - } - - console.log("drag leave", that.cid, e); - if (DragDrop.checkDrop(e, "tab")) { - that.$el.removeClass("dragover"); - } - }); - - this.$el.on('drop', function(e) { - console.log("drop ", e); - if (DragDrop.checkDrop(e, "tab")) { - that.$el.removeClass("dragover"); - DragDrop.drop(e); - - that.parent.changeTabSection(DragDrop.getData(e, "tab"), that.sectionId); + // Drop tabs + this.dropArea = new dnd.DropArea({ + view: this, + dragType: TabHeaderItem.drag, + handler: function(tab) { + tab.changeSection(that.sectionId); } }); diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index c4350944..6a144e54 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -6,7 +6,7 @@ define([ "utils/dragdrop", "utils/keyboard", "utils/contextmenu" -], function(_, $, hr, Command, DragDrop, Keyboard, ContextMenu) { +], function(_, $, hr, Command, dnd, Keyboard, ContextMenu) { // Tab header var TabView = hr.List.Item.extend({ @@ -19,8 +19,7 @@ define([ events: { "click": "open", "click .close": "close", - "dblclick": "split", - "dragstart": "dragStart" + "dblclick": "split" }, states: { 'modified': "fa-asterisk", @@ -39,6 +38,11 @@ define([ this.$el.attr("draggable", true); + TabView.drag.enableDrag({ + view: this, + data: this.model + }); + // Context menu ContextMenu.add(this.$el, [ { @@ -134,12 +138,6 @@ define([ this.model.active(); }, - // (event) Drag start - dragStart: function(e) { - DragDrop.drag(e, "move"); - DragDrop.setData(e, "tab", this.model.id); - }, - // (event) close close: function(e, force) { if (e != null) { @@ -153,6 +151,8 @@ define([ closeOthers: function(e) { this.model.closeOthers(); } + }, { + 'drag': new dnd.DraggableType() }); return TabView; From 18d0bab64fd990489290f6bdb28f43a185a680e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 15:14:13 +0100 Subject: [PATCH 022/646] Improve drag and drop of tabs --- client/models/tab.js | 5 ++ .../resources/stylesheets/tabs/section.less | 4 +- client/utils/dragdrop.js | 69 ++++++++++++++----- client/views/tabs/section.js | 38 ++++++---- client/views/tabs/tab.js | 26 +++---- 5 files changed, 93 insertions(+), 49 deletions(-) diff --git a/client/models/tab.js b/client/models/tab.js index 4cf596fc..b0b3dbad 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -70,6 +70,11 @@ define([ return this.manager.changeTabSection(this, section); }, + // Split section + splitSection: function() { + this.changeSection(_.uniqueId("section")); + }, + // Close this tab close: function(force) { var that = this, tabid = this.id, prev = null; diff --git a/client/resources/stylesheets/tabs/section.less b/client/resources/stylesheets/tabs/section.less index 1e5ca18b..aee00669 100644 --- a/client/resources/stylesheets/tabs/section.less +++ b/client/resources/stylesheets/tabs/section.less @@ -6,7 +6,7 @@ right: 0px; &.dragover { - opacity: 0.4; + /*opacity: 0.4; &:after { content:''; @@ -20,7 +20,7 @@ * { pointer-events:none; - } + }*/ } .tabs-section-header { diff --git a/client/utils/dragdrop.js b/client/utils/dragdrop.js index 05e0e4a2..3629daaa 100644 --- a/client/utils/dragdrop.js +++ b/client/utils/dragdrop.js @@ -14,7 +14,10 @@ define([ dragType: null, // Handler for drop - handler: null + handler: null, + + // Contrain elastic + constrain: null }, initialize: function() { @@ -35,6 +38,7 @@ define([ this.$el.on('mouseleave', function(e) { that.$el.removeClass("dragover"); + that.dragType.exitDropArea(); }); this.on("drop", function() { @@ -83,7 +87,10 @@ define([ el: null, // Data to transfer - data: null + data: null, + + // Base drop area + baseDropArea: null }); if (options.el) $el = $(options.el); if (options.view) $el = options.view.$el, data = options.view; @@ -91,35 +98,66 @@ define([ $el.mousedown(function(e) { e.preventDefault(); - var dx, dy; + 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(); + + // Contrain element + var cw, ch, cx, cy; + + that.drop = options.baseDropArea; that.data = data; var f = function(e) { dx = oX - e.pageX; dy = oY - e.pageY; - $el.addClass("move"); - $el.css({ - 'left': poX-dx, - 'top': poY - }); + if (Math.abs(dx) > 20 || Math.abs(dy) > 20) { + if (!hasMove) $el.addClass("move"); + hasMove = true; + } + + ex = poX - dx; + ey = poY - dy; - if (Math.abs(dy) > 50) { - $el.css({ - 'top': poY-dy - }); + if (that.drop && that.drop.options.constrain) { + cw = that.drop.$el.width(); + ch = that.drop.$el.height(); + cx = that.drop.$el.offset().left; + cy = that.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.mousemove(f); - $document.mouseup(function(e) { - if (that.drop) { - that.drop.trigger("drop", that.data); + $document.one("mouseup", function(e) { + $document.unbind('mousemove', f); + + if (hasMove && (!options.baseDropArea || !that.drop || (options.baseDropArea.cid != that.drop.cid))) { + if (that.drop) { + that.drop.trigger("drop", that.data); + } + that.trigger("drop", that.drop, that.data); } that.data = null; @@ -130,7 +168,6 @@ define([ 'left': "auto", 'top': "auto" }); - $document.unbind('mousemove', f); }); }); } diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index 99c1d9d2..5b723fb5 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -39,7 +39,28 @@ define([ var TabsSectionHeader = TabsList.extend({ className: "tabs-section-header", - Item: TabHeaderItem + Item: TabHeaderItem, + + initialize: function() { + TabsSectionHeader.__super__.initialize.apply(this, arguments); + + var that = this; + + // Drop tabs + this.dropArea = new dnd.DropArea({ + view: this, + dragType: TabHeaderItem.drag, + constrain: { + x: 20, + y: 20 + }, + handler: function(tab) { + tab.changeSection(that.parent.sectionId); + } + }); + + return this; + }, }); @@ -61,25 +82,14 @@ define([ this.header = new TabsSectionHeader({ collection: this.tabs - }); + }, this); this.content = new TabsSectionContent({ collection: this.tabs - }); + }, this); this.header.$el.appendTo(this.$el); this.content.$el.appendTo(this.$el); - var ignoreNextLeave = false; - - // Drop tabs - this.dropArea = new dnd.DropArea({ - view: this, - dragType: TabHeaderItem.drag, - handler: function(tab) { - tab.changeSection(that.sectionId); - } - }); - this.on("grid:layout", function() { this.tabs.each(function(tab) { tab.view.trigger("tab:layout"); diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index 6a144e54..b0232f25 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -18,8 +18,7 @@ define([ }, events: { "click": "open", - "click .close": "close", - "dblclick": "split" + "click .close": "close" }, states: { 'modified': "fa-asterisk", @@ -37,10 +36,11 @@ define([ var $document = $(document); this.$el.attr("draggable", true); - + TabView.drag.enableDrag({ view: this, - data: this.model + data: this.model, + baseDropArea: this.list.dropArea }); // Context menu @@ -76,7 +76,7 @@ define([ 'type': "action", 'title': "New Group", 'action': function() { - that.split(); + that.model.splitSection(); } }, { 'type': "divider" }, @@ -120,18 +120,6 @@ define([ return this.$el.hasClass("active"); }, - // Set section - setSection: function(section) { - this.model.changeSection(section); - this.model.active(); - }, - - // Create section - split: function(e) { - if (e) e.stopPropagation(); - this.setSection(_.uniqueId("section")); - }, - // (event) open open: function(e) { if (e != null) e.preventDefault(); @@ -155,5 +143,9 @@ define([ 'drag': new dnd.DraggableType() }); + TabView.drag.on("drop", function(section, tab) { + if (!section && tab) tab.splitSection(); + }) + return TabView; }); \ No newline at end of file From 7f8cff4fc64546f5677e1ace4a96f0752a3ea1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 15:58:19 +0100 Subject: [PATCH 023/646] Double click on header to open a new tab --- client/views/tabs/section.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index 5b723fb5..26927192 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -40,6 +40,9 @@ define([ var TabsSectionHeader = TabsList.extend({ className: "tabs-section-header", Item: TabHeaderItem, + events: { + 'dblclick': "openNewtab" + }, initialize: function() { TabsSectionHeader.__super__.initialize.apply(this, arguments); @@ -61,6 +64,10 @@ define([ return this; }, + + openNewtab: function() { + this.parent.manager.openDefault(); + } }); @@ -77,6 +84,7 @@ define([ TabsSectionView.__super__.initialize.apply(this, arguments); var that = this; + this.manager = this.parent; this.tabs = new Tabs(); From 0ebe6fb2b835abcd0782303577e345c4a40be8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 17:23:13 +0100 Subject: [PATCH 024/646] Add reorder for tabs (Fix #182) --- client/resources/stylesheets/tabs/tab.less | 6 +++ client/utils/dragdrop.js | 49 +++++++++++++++------- client/views/tabs/manager.js | 9 ++-- client/views/tabs/section.js | 7 +++- client/views/tabs/tab.js | 25 ++++++++++- 5 files changed, 72 insertions(+), 24 deletions(-) diff --git a/client/resources/stylesheets/tabs/tab.less b/client/resources/stylesheets/tabs/tab.less index 5a933ed3..f7ec3d78 100644 --- a/client/resources/stylesheets/tabs/tab.less +++ b/client/resources/stylesheets/tabs/tab.less @@ -5,7 +5,9 @@ z-index: 0; cursor: default; margin: 0px; + padding: 0px; background: @tabColor; + .transition(padding-left ease 0.25s); &.move { position: fixed; @@ -17,6 +19,10 @@ } } + &.dragover { + padding-left: 150px; + } + .inner { .box-sizing(border-box); diff --git a/client/utils/dragdrop.js b/client/utils/dragdrop.js index 3629daaa..a31e2e6a 100644 --- a/client/utils/dragdrop.js +++ b/client/utils/dragdrop.js @@ -31,6 +31,7 @@ define([ this.$el.on('mouseenter', function(e) { if (that.dragType.isDragging()) { + e.stopPropagation(); that.dragType.enterDropArea(that); that.$el.addClass("dragover"); } @@ -57,7 +58,7 @@ define([ this.data = null; // Drop handler - this.drop = null; + this.drop = []; }, // Is currently dragging data @@ -65,14 +66,21 @@ define([ 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 = area; + //console.log("enter drop", this.drop.length, area.$el.get(0)); + this.drop.push(area); }, // Exit drop area exitDropArea: function() { - this.drop = null; + this.drop.pop(); + //console.log("exit drop", this.drop.length); }, // Enable drag and drop in a object @@ -90,7 +98,10 @@ define([ data: null, // Base drop area - baseDropArea: null + baseDropArea: null, + + // Before dragging + start: null }); if (options.el) $el = $(options.el); if (options.view) $el = options.view.$el, data = options.view; @@ -116,10 +127,15 @@ define([ // Contrain element var cw, ch, cx, cy; - that.drop = options.baseDropArea; + that.drop = []; + if (options.baseDropArea) that.enterDropArea(options.baseDropArea); that.data = data; + if (options.start) options.start(); + var f = function(e) { + var _drop = that.getDrop(); + dx = oX - e.pageX; dy = oY - e.pageY; @@ -131,11 +147,11 @@ define([ ex = poX - dx; ey = poY - dy; - if (that.drop && that.drop.options.constrain) { - cw = that.drop.$el.width(); - ch = that.drop.$el.height(); - cx = that.drop.$el.offset().left; - cy = that.drop.$el.offset().top; + 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; @@ -152,16 +168,17 @@ define([ $document.mousemove(f); $document.one("mouseup", function(e) { $document.unbind('mousemove', f); - - if (hasMove && (!options.baseDropArea || !that.drop || (options.baseDropArea.cid != that.drop.cid))) { - if (that.drop) { - that.drop.trigger("drop", that.data); + var _drop = that.getDrop(); + + if (hasMove && (!options.baseDropArea || !_drop || (options.baseDropArea.cid != _drop.cid))) { + if (_drop) { + _drop.trigger("drop", that.data); } - that.trigger("drop", that.drop, that.data); + that.trigger("drop", _drop, that.data); } that.data = null; - that.drop = null; + that.drop = []; $el.removeClass("move"); $el.css({ diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 21617945..bb54d384 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -74,8 +74,9 @@ define([ }); if (!s) { - s = new TabsSectionView({}, this); - s.sectionId = id; + s = new TabsSectionView({ + sectionId: id + }, this); this.grid.addView(s); } @@ -183,7 +184,7 @@ define([ }, // Change tab section - changeTabSection: function(tab, section) { + changeTabSection: function(tab, section, options) { if (_.isString(tab)) tab = this.tabs.getById(tab); if (!tab) return false; @@ -193,7 +194,7 @@ define([ tab.section.remove(tab); // Add to new section - section.addTab(tab); + section.addTab(tab, options); // Active tab.active(); diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index 26927192..6b1317fc 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -58,6 +58,7 @@ define([ y: 20 }, handler: function(tab) { + console.log("drop tab at end"); tab.changeSection(that.parent.sectionId); } }); @@ -85,8 +86,10 @@ define([ var that = this; this.manager = this.parent; + this.sectionId = this.options.sectionId; this.tabs = new Tabs(); + this.tabs.sectionId = this.sectionId; this.header = new TabsSectionHeader({ collection: this.tabs @@ -110,9 +113,9 @@ define([ /* * Add a tab to this section */ - addTab: function(tab) { + addTab: function(tab, options) { tab.section = this.tabs; - this.tabs.add(tab); + this.tabs.add(tab, options); return this; }, diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index b0232f25..aacdb57e 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -36,11 +36,32 @@ define([ var $document = $(document); this.$el.attr("draggable", true); - + + // Drop tabs to order + this.dropArea = new dnd.DropArea({ + view: this, + dragType: TabView.drag, + handler: function(tab) { + var i = that.list.collection.indexOf(that.model); + var ib = that.list.collection.indexOf(tab); + + if (ib >= 0 && ib < i) { + i = i - 1; + } + console.log("drop tab at position", i); + that.model.manager.changeTabSection(tab, that.list.collection.sectionId, { + at: i + }); + } + }); + TabView.drag.enableDrag({ view: this, data: this.model, - baseDropArea: this.list.dropArea + baseDropArea: this.list.dropArea, + start: function() { + that.open(); + } }); // Context menu From 8e54593088d421c8779aa8d6bf98953d577c0c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 20:42:22 +0100 Subject: [PATCH 025/646] Fix #162: Restore previous tabs when open codebox --- addons/cb.terminal/client.js | 11 +++++ client/core/app.js | 7 ++- client/core/files.js | 17 ++++++++ client/models/tab.js | 10 +++++ client/views/tabs/manager.js | 84 ++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 2 deletions(-) diff --git a/addons/cb.terminal/client.js b/addons/cb.terminal/client.js index c8b26f2c..5c14c21b 100644 --- a/addons/cb.terminal/client.js +++ b/addons/cb.terminal/client.js @@ -77,6 +77,7 @@ define([ var tab = tabs.add(TerminalTab, { 'shellId': shellId }, { + 'type': "terminal", 'section': "terminals" }); @@ -84,6 +85,16 @@ define([ return tab; }); + // Restorer for tabs + tabs.addRestorer("terminal", function(tabInfos) { + var tab = tabs.add(TerminalTab, {}, { + 'type': "terminal", + 'section': "terminals" + }); + + return tab; + }); + // List terminals menu var terminalsList = Command.register("terminal.list", { title: "Open Terminals", diff --git a/client/core/app.js b/client/core/app.js index c2b66bbe..ea10fabe 100644 --- a/client/core/app.js +++ b/client/core/app.js @@ -154,8 +154,11 @@ box, session, addons, box, files, commands, menu, tabs, panels, operations, loca // Trigger event that app is ready that.trigger("ready"); - // Open new file if not files opened by addons - if (files.active.size() == 0) files.openNew(); + // Open new file if not files opened by addons and no restored tabs + tabs.restoreTabs() + .then(function(_n) { + if (files.active.size() == 0 && _n == 0) files.openNew(); + }); }); } return Application.__super__.finish.apply(this, arguments); diff --git a/client/core/files.js b/client/core/files.js index a3042c2a..68bf7627 100644 --- a/client/core/files.js +++ b/client/core/files.js @@ -36,6 +36,23 @@ define([ // Files handlers map var handlers = {}; + // Restorer for tabs + tabs.addRestorer("file", function(tabInfos) { + var parts = tabInfos.id.split(":"); + var handlerId = parts[0]; + var path = parts.slice(1).join(":"); + + var newFile = path.indexOf("temporary") == 0; + if (!newFile) { + return openFile(path.replace("file://", "")) + .then(function() { + return tabs.getById(tabInfos.id); + }); + } + + return null; + }); + // Add handler var addHandler = function(handlerId, handler) { if (!handler diff --git a/client/models/tab.js b/client/models/tab.js index b0b3dbad..2dc82a12 100644 --- a/client/models/tab.js +++ b/client/models/tab.js @@ -125,6 +125,16 @@ define([ if (tab.id != this.id) tab.close(); }, this); this.active(); + }, + + // Return a snapshot of current tab + snapshot: function() { + return { + 'id': this.id, + 'type': this.get("type"), + 'section': this.section.sectionId, + 'active': this.isActive() + }; } }); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index bb54d384..cd5e0019 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -62,11 +62,19 @@ define([ // Tabs collection this.tabs = new Tabs(); + // Restorer + this.restorer = {}; + // Set base layout this.setLayout(null); return this; }, + // Return a tab by its id + getById: function(id) { + return this.tabs.getById(id); + }, + // Return a section by its id getSection: function(id) { var s = _.find(this.grid.views, function(section) { @@ -105,6 +113,9 @@ define([ var tab = null; options = _.defaults(options || {}, { + // Tab type + type: "unknown", + // Don't trigger event silent: false, @@ -131,6 +142,7 @@ define([ tab = new Tab({ 'manager': this }, { + 'type': options.type, 'id': options.uniqueId, 'title': options.title }); @@ -149,6 +161,7 @@ define([ } if (options.open) tab.active(); + this.saveTabs(); return tab.view; }, @@ -181,6 +194,8 @@ define([ } }, this); + + this.saveTabs(); }, // Change tab section @@ -203,6 +218,75 @@ define([ this.checkSections(); return true; + }, + + // Save tabs + saveTabs: function() { + var state = {}; + + // Snapshot sections and tabs + state.sections = _.map(this.grid.views, function(section) { + return { + 'id': section.sectionId, + 'tabs':section.tabs.map(function(tab) { + return tab.snapshot(); + }) + }; + }); + + // Snapshot layout + state.layout = this.grid.columns; + + hr.Storage.set("tabs", state); + }, + + // Add a restorer for tabs + addRestorer: function(type, handler) { + this.restorer[type] = handler; + return this; + }, + + // Load tabs saved in last session (return number of tabs restored) + restoreTabs: function(state) { + var n = 0, that = this; + + state = state || hr.Storage.get("tabs"); + + // Set layout + this.setLayout(state.layout); + + // Restore tabs + return Q.all( + _.chain(state.sections || []) + .map(function(section) { + that.getSection(section.id); + return section.tabs; + }) + .flatten() + .map(function(tab) { + // Restore tab + return Q() + .then(function() { + if (!that.restorer[tab.type]) return; + + return Q(that.restorer[tab.type](tab)); + }) + .then(function(_tab) { + if (!_tab) return; + + // restore in right section + _tab.changeSection(tab.section); + n = n + 1; + }) + .fin(function() { + return Q(); + }) + }) + .value() + ) + .then(function() { + return n; + }); } }, { Panel: TabPanelView From b54e8666df925e88d14683599b672f3036a5b801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 20:50:21 +0100 Subject: [PATCH 026/646] Fix ace editor rendering layout when focus --- addons/cb.files.editor/views/file.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/cb.files.editor/views/file.js b/addons/cb.files.editor/views/file.js index d3b68a20..12b34a87 100644 --- a/addons/cb.files.editor/views/file.js +++ b/addons/cb.files.editor/views/file.js @@ -313,6 +313,7 @@ define([ // Parent tab this.tab.on("tab:layout", function() { this.editor.resize(); + this.editor.renderer.updateFull(); }, this); this.tab.on("tab:state", function(state) { if (state) this.focus(); @@ -376,6 +377,8 @@ define([ }, focus: function() { + this.editor.resize(); + this.editor.renderer.updateFull(); this.editor.focus(); return this; }, From 053100e1db80affaeb7a8daa0326dd9e0ef652de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 20:57:41 +0100 Subject: [PATCH 027/646] Don't restore empty sections --- client/views/tabs/manager.js | 2 +- client/views/tabs/tab.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index cd5e0019..4daaeec5 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -259,7 +259,7 @@ define([ return Q.all( _.chain(state.sections || []) .map(function(section) { - that.getSection(section.id); + if (section.tabs.length > 0) that.getSection(section.id); return section.tabs; }) .flatten() diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index aacdb57e..e0e31f5c 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -148,12 +148,12 @@ define([ }, // (event) close - close: function(e, force) { + close: function(e) { if (e != null) { e.preventDefault(); e.stopPropagation(); } - this.model.close(force); + this.model.close(); }, // (event) close others tabs From e35293bcfce4f2d326831626f9bcb5c7ba4e44d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 1 Mar 2014 22:39:34 +0100 Subject: [PATCH 028/646] Fix close of tab stability --- client/views/tabs/tab.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index e0e31f5c..6505fefe 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -17,8 +17,9 @@ define([ close: true }, events: { - "click": "open", - "click .close": "close" + "mousedown .close": "close", + "click .close": "close", + "click": "open", }, states: { 'modified': "fa-asterisk", From f7d46080f1d6b16e90fc7a567a384b84fa75ae5d Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Sun, 2 Mar 2014 11:51:02 +0100 Subject: [PATCH 029/646] Fix minor z-index bug when dragging active tabs Previously would be hidden when being dragged to a new section due to drag z-index having a lower priority to the .active z-index --- client/resources/stylesheets/tabs/tab.less | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/resources/stylesheets/tabs/tab.less b/client/resources/stylesheets/tabs/tab.less index f7ec3d78..6e79674d 100644 --- a/client/resources/stylesheets/tabs/tab.less +++ b/client/resources/stylesheets/tabs/tab.less @@ -9,7 +9,8 @@ background: @tabColor; .transition(padding-left ease 0.25s); - &.move { + &.move, + &.active.move { position: fixed; z-index: 100000; pointer-events:none; @@ -49,7 +50,7 @@ } &.state-sync { color: #27ae60; - } + } } .close { @@ -86,7 +87,7 @@ z-index: 10; border-color: @tabActiveBorder; background: @tabActiveColor; - + .inner { .close { color: rgba(0,0,0,0.5); From d3272c0816f0b1e0059a6f0de5b1f8b660a9a599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 2 Mar 2014 20:41:16 +0100 Subject: [PATCH 030/646] Base for using grid and tabs for lateral panels --- client/views/panels/manager.js | 48 +++++++++++++--------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index e08ad040..329890bd 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -3,8 +3,10 @@ define([ 'hr/dom', 'hr/hr', 'models/command', - 'views/panels/base' -], function(_, $, hr, Command, PanelBaseView) { + 'views/panels/base', + 'views/tabs/manager' +], function(_, $, hr, Command, PanelBaseView, TabsManager) { + var PanelsView = hr.View.extend({ className: "cb-panels-list", defaults: {}, @@ -15,6 +17,10 @@ define([ var that = this; PanelsView.__super__.initialize.apply(this, arguments); + // Tabs + this.tabs = new TabsManager({}, this); + this.tabs.$el.appendTo(this.$el); + // Active panel this.activePanel = null; this.previousPanel = null; @@ -54,9 +60,6 @@ define([ }); this.panels[panelId] = new panelView(constructor, this); - - this.panels[panelId].$el.appendTo(this.$el); - this.panels[panelId].$el.hide(); this.panels[panelId].render(); return this.panels[panelId]; @@ -64,37 +67,27 @@ define([ // Render render: function() { - this.$el.empty(); - _.each(this.panels, function(panel, panelId) { - panel.$el.appendTo(this.$el); - panel.render(); - }, this); - return this.ready(); }, // Open a panel open: function(pId) { var opened = false; - _.each(this.panels, function(panel, panelId) { - opened = opened || (panelId == pId); - - // Change visibility of the panel - panel.$el.toggle(panelId == pId); - // Trigger panel event - if (panelId != pId && panelId == this.activePanel) { - panel.trigger("panel:close"); - } else if (panelId == pId) { - panel.trigger("panel:open"); + if (pId && this.panels[pId]) { + opened = true; + var tab = this.tabs.add(TabsManager.Panel, {}, { + 'title': pId, + 'uniqueId': pId + }); + if (tab.$el.is(':empty')) { + this.panels[pId].$el.appendTo(tab.$el); } - }, this); + } - // Change active panel this.previousPanel = this.activePanel || this.previousPanel; this.activePanel = pId; - - // Trigger global event + if (opened) { this.trigger("open", pId); } else { @@ -110,11 +103,6 @@ define([ return this.open(null); }, - // Is active - isActive: function(pId) { - return this.activePanel == pId; - }, - // Show panels show: function() { return this.open(this.activePanel || this.previousPanel || _.first(_.keys(this.panels))); From 33da892db20179d534b1994554171dc684fee6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 11:45:15 +0100 Subject: [PATCH 031/646] Separate tabs drag and drop context --- client/views/panels/manager.js | 6 ++++++ client/views/tabs/manager.js | 9 ++++++++- client/views/tabs/section.js | 3 +-- client/views/tabs/tab.js | 10 ++-------- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index 329890bd..08013f9f 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -98,6 +98,12 @@ define([ return this; }, + // Check if a panel is active + isActive: function(pId) { + var t = this.tabs.getById(pId); + return !(t == null || !t.isActive()); + }, + // Close panel close: function() { return this.open(null); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 4daaeec5..c98a60ea 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -8,6 +8,7 @@ define([ "collections/tabs", + "utils/dragdrop", "utils/keyboard", "utils/contextmenu", @@ -15,7 +16,7 @@ define([ "views/tabs/tab", "views/tabs/base", "views/tabs/section" -], function(_, $, hr, Command, Tab , Tabs, Keyboard, ContextMenu, GridView, TabView, TabPanelView, TabsSectionView) { +], function(_, $, hr, Command, Tab , Tabs, dnd, Keyboard, ContextMenu, GridView, TabView, TabPanelView, TabsSectionView) { // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", @@ -40,6 +41,12 @@ define([ this.grid = new GridView({}, this); this.grid.$el.appendTo(this.$el); + // Drag and drop of tabs + this.drag = new dnd.DraggableType(); + this.drag.on("drop", function(section, tab) { + if (!section && tab) tab.splitSection(); + }) + // Commands this.layoutCommand = new Command({}, { 'type': "menu", diff --git a/client/views/tabs/section.js b/client/views/tabs/section.js index 6b1317fc..7f7eadbb 100644 --- a/client/views/tabs/section.js +++ b/client/views/tabs/section.js @@ -52,13 +52,12 @@ define([ // Drop tabs this.dropArea = new dnd.DropArea({ view: this, - dragType: TabHeaderItem.drag, + dragType: this.parent.manager.drag, constrain: { x: 20, y: 20 }, handler: function(tab) { - console.log("drop tab at end"); tab.changeSection(that.parent.sectionId); } }); diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index 6505fefe..e5062c4e 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -41,7 +41,7 @@ define([ // Drop tabs to order this.dropArea = new dnd.DropArea({ view: this, - dragType: TabView.drag, + dragType: this.model.manager.drag, handler: function(tab) { var i = that.list.collection.indexOf(that.model); var ib = that.list.collection.indexOf(tab); @@ -56,7 +56,7 @@ define([ } }); - TabView.drag.enableDrag({ + this.model.manager.drag.enableDrag({ view: this, data: this.model, baseDropArea: this.list.dropArea, @@ -161,13 +161,7 @@ define([ closeOthers: function(e) { this.model.closeOthers(); } - }, { - 'drag': new dnd.DraggableType() }); - TabView.drag.on("drop", function(section, tab) { - if (!section && tab) tab.splitSection(); - }) - return TabView; }); \ No newline at end of file From c5d00e4e9d8ccc4b6eb707e436609cb3dab3f84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 12:00:31 +0100 Subject: [PATCH 032/646] Disable menu for panels tabs --- client/views/panels/manager.js | 5 ++++- client/views/tabs/base.js | 2 +- client/views/tabs/manager.js | 11 ++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index 08013f9f..fe1d5a33 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -18,7 +18,10 @@ define([ PanelsView.__super__.initialize.apply(this, arguments); // Tabs - this.tabs = new TabsManager({}, this); + this.tabs = new TabsManager({ + layout: 1, + tabMenu: false + }, this); this.tabs.$el.appendTo(this.$el); // Active panel diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index fd545e39..37e39bbf 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -39,7 +39,7 @@ define([ 'title': this.menuTitle, 'position': 1 }); - menu.collection.add(this.menu); + if (this.tab.manager.options.tabMenu) menu.collection.add(this.menu); // Bind tab event this.tab.on("destroy", function() { diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index c98a60ea..8df4d50c 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -20,9 +20,14 @@ define([ // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", - events: { - + defaults: { + // BAse layout + layout: null, + + // Enable tab menu + tabMenu: true }, + events: {}, layouts: { "Auto Grid": 0, "Columns: 1": 1, @@ -73,7 +78,7 @@ define([ this.restorer = {}; // Set base layout - this.setLayout(null); + this.setLayout(this.options.layout); return this; }, From d63ff8690801ae9f3081d66578431b4da18dae7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 12:27:42 +0100 Subject: [PATCH 033/646] Limit options of panels tabs: layouts, new tab, .. --- client/views/panels/manager.js | 6 +++++- client/views/tabs/manager.js | 31 +++++++++++++++++++------------ client/views/tabs/tab.js | 10 +++++----- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index fe1d5a33..a14a2c76 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -20,7 +20,11 @@ define([ // Tabs this.tabs = new TabsManager({ layout: 1, - tabMenu: false + layouts: { + "Columns: 1": 1 + }, + tabMenu: false, + newTab: false }, this); this.tabs.$el.appendTo(this.$el); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 8df4d50c..1dd06ea8 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -21,20 +21,25 @@ define([ var TabsView = hr.View.extend({ className: "cb-tabs", defaults: { - // BAse layout + // Base layout layout: null, + // Available layouts + layouts: { + "Auto Grid": 0, + "Columns: 1": 1, + "Columns: 2": 2, + "Columns: 3": 3, + "Columns: 4": 4 + }, + // Enable tab menu - tabMenu: true + tabMenu: true, + + // Enable open new tab + newTab: true }, events: {}, - layouts: { - "Auto Grid": 0, - "Columns: 1": 1, - "Columns: 2": 2, - "Columns: 3": 3, - "Columns: 4": 4 - }, // Constructor initialize: function(options) { @@ -42,7 +47,7 @@ define([ TabsView.__super__.initialize.apply(this, arguments); // Current layout - this.layout = null; // null: mode auto + this.layout = this.options.layout; // null: mode auto this.grid = new GridView({}, this); this.grid.$el.appendTo(this.$el); @@ -57,7 +62,7 @@ define([ 'type': "menu", 'title': "Layout" }); - _.each(this.layouts, function(layout, layoutName) { + _.each(this.options.layouts, function(layout, layoutName) { var command = new Command({}, { 'type': "action", 'title': layoutName, @@ -78,7 +83,7 @@ define([ this.restorer = {}; // Set base layout - this.setLayout(this.options.layout); + this.setLayout(this.layout); return this; }, @@ -185,6 +190,8 @@ define([ // Define tabs layout setLayout: function(l) { + if (!_.contains(_.values(this.options.layouts), l)) return; + this.grid.setLayout(l); this.trigger("layout", l); this.update(); diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index e5062c4e..495a5733 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -66,16 +66,16 @@ define([ }); // Context menu - ContextMenu.add(this.$el, [ - { + ContextMenu.add(this.$el, _.compact([ + (this.model.manager.options.newTab ? { 'id': "tab.new", 'type': "action", 'title': "New Tab", 'action': function() { that.model.manager.openDefault(); } - }, - { 'type': "divider" }, + } : null), + (this.model.manager.options.newTab ? { 'type': "divider" } : null), { 'id': "tab.close", 'type': "action", @@ -103,7 +103,7 @@ define([ }, { 'type': "divider" }, that.model.manager.layoutCommand - ]); + ])); return this; }, From 046464095c219af5b8a1699a03f641115757ca4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 14:39:04 +0100 Subject: [PATCH 034/646] Add an optional limit on number of tabs per section --- client/views/panels/manager.js | 3 ++- client/views/tabs/manager.js | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index a14a2c76..d5bcd16b 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -24,7 +24,8 @@ define([ "Columns: 1": 1 }, tabMenu: false, - newTab: false + newTab: false, + maxTabsPerSection: 1 }, this); this.tabs.$el.appendTo(this.$el); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 1dd06ea8..8f0e45cb 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -37,7 +37,10 @@ define([ tabMenu: true, // Enable open new tab - newTab: true + newTab: true, + + // Max number of tabs per sections (-1 for unlimited) + maxTabsPerSection: -1 }, events: {}, @@ -174,7 +177,18 @@ define([ tab.view.update(); // Add to section - this.getSection(options.section).addTab(tab); + var sectionId = options.section; + for (;;) { + var section = this.getSection(sectionId); + console.log("test section ", sectionId) + if (this.options.maxTabsPerSection > 0 && section.tabs.size() >= this.options.maxTabsPerSection) { + sectionId = _.uniqueId("tabSection"); + } else { + console.log("add to section ", sectionId); + section.addTab(tab); + break; + } + } } if (options.open) tab.active(); @@ -224,6 +238,9 @@ define([ section = this.getSection(section); + // Check limit + if (this.options.maxTabsPerSection > 0 && section.tabs.size() >= this.options.maxTabsPerSection) return false; + // Remove from old section tab.section.remove(tab); From 024ba261cca26efabe064f52fa572876b7be0363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 16:32:24 +0100 Subject: [PATCH 035/646] Add base for new grid ui --- addons/cb.panel.files/stylesheets/panel.less | 1 + client/core/app.js | 25 +++----- client/core/commands/menu.js | 1 - client/core/themes.js | 2 +- client/resources/stylesheets/panels/list.less | 62 +++---------------- client/resources/stylesheets/tabs/base.less | 1 + client/resources/stylesheets/tabs/tab.less | 10 ++- client/resources/stylesheets/ui/body.less | 8 +-- client/resources/stylesheets/ui/grid.less | 8 +-- client/views/panels/manager.js | 19 +----- client/views/tabs/manager.js | 3 +- 11 files changed, 38 insertions(+), 102 deletions(-) diff --git a/addons/cb.panel.files/stylesheets/panel.less b/addons/cb.panel.files/stylesheets/panel.less index 65b56333..fb86161c 100644 --- a/addons/cb.panel.files/stylesheets/panel.less +++ b/addons/cb.panel.files/stylesheets/panel.less @@ -5,6 +5,7 @@ left: 0px; width: 100%; z-index: 10; + overflow-y: auto; .files-panel-section { margin-bottom: 10px; diff --git a/client/core/app.js b/client/core/app.js index ea10fabe..0b50da4d 100644 --- a/client/core/app.js +++ b/client/core/app.js @@ -4,6 +4,7 @@ define([ 'utils/dialogs', 'utils/alerts', 'utils/loading', + 'views/grid', 'core/box', 'core/session', 'core/addons', @@ -18,7 +19,7 @@ define([ 'core/themes', 'core/search/files', 'core/search/tags' -], function (hr, url, dialogs, alerts, loading, +], function (hr, url, dialogs, alerts, loading, GridView, box, session, addons, box, files, commands, menu, tabs, panels, operations, localfs, themes) { // Define base application @@ -46,19 +47,17 @@ box, session, addons, box, files, commands, menu, tabs, panels, operations, loca this._autologin = true; this.loginError = null; + this.grid = new GridView({ + columns: 1000 + }); + this.grid.addView(panels); + this.grid.addView(tabs); + // Tabs tabs.on("tabs:default tabs:opennew", function() { files.openNew(); }, this); - // Panels - panels.on("open", function() { - this.toggleMode("body-fullpage", false); - }, this); - panels.on("close", function() { - this.toggleMode("body-fullpage", true); - }, this); - // Offline: state/update hr.Offline.on("state", function(state) { if (!state) { @@ -114,13 +113,7 @@ box, session, addons, box, files, commands, menu, tabs, panels, operations, loca commands.$el.appendTo(this.$(".cb-commands")); commands.render(); - // Add tabs - tabs.$el.appendTo(this.$(".cb-body")); - tabs.render(); - - // Add panels - panels.$el.appendTo(this.$(".cb-panels")); - panels.render(); + this.grid.$el.appendTo(this.$(".cb-body")); // Add operations operations.$el.appendTo(this.$(".lateral-operations")); diff --git a/client/core/commands/menu.js b/client/core/commands/menu.js index c69b1ecc..4393082b 100644 --- a/client/core/commands/menu.js +++ b/client/core/commands/menu.js @@ -23,7 +23,6 @@ define([ settings.open("themes"); } }).menuSection([ - panels.visibilityCommand, panels.panelsCommand ]).menuSection([ tabs.layoutCommand diff --git a/client/core/themes.js b/client/core/themes.js index d5a05be5..cf05d7b7 100644 --- a/client/core/themes.js +++ b/client/core/themes.js @@ -86,7 +86,7 @@ define([ // lateral bar 'lateralbar': ".cb-lateralbar", 'lateralbar commands': ".cb-lateralbar .lateral-commands", - 'lateralbar body': ".cb-lateralbar .lateral-body", + 'lateralbar body': ".cb-panels", // body 'body': ".cb-body", diff --git a/client/resources/stylesheets/panels/list.less b/client/resources/stylesheets/panels/list.less index 60ab5f85..8c8c96a3 100644 --- a/client/resources/stylesheets/panels/list.less +++ b/client/resources/stylesheets/panels/list.less @@ -1,59 +1,15 @@ .cb-panels { - .cb-panel-list { - list-style: none; - margin: 0px; - padding: 6px; + .component-tab { + width: 100%; + max-width: 100%; - >li { - padding: 0px 8px; - line-height: 26px; - cursor: pointer; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &.header { - margin: 0px; - color: @codeboxActiveColor; - text-transform: uppercase; - font-weight: 600; - } - - &.selected { - background: @codeboxActiveColor; - color: #fff; - } - - &.link { - padding: 0px; - - a { - color: inherit; - display: block; - padding: 4px 8px; - - &:hover { - text-decoration: none; - } - } - } - - img { - width: 24px; - height: 24px; - border-radius: 24px; - float: left; - margin-right: 6px; - } + .inner { + max-width: 100%; } + } - &.selected-hover { - >li:not(.header) { - &:hover { - background: #5E9EF3; - color: #fff; - } - } - } + .tabs-section-header, .tabs-section-content { + background: inherit !important; + color: inherit !important; } } \ No newline at end of file diff --git a/client/resources/stylesheets/tabs/base.less b/client/resources/stylesheets/tabs/base.less index 4a25a225..620cd4f0 100644 --- a/client/resources/stylesheets/tabs/base.less +++ b/client/resources/stylesheets/tabs/base.less @@ -8,6 +8,7 @@ left: 0px; right: 0px; bottom: 0px; + overflow: hidden; &.active { display: block; diff --git a/client/resources/stylesheets/tabs/tab.less b/client/resources/stylesheets/tabs/tab.less index 6e79674d..75c3aa5f 100644 --- a/client/resources/stylesheets/tabs/tab.less +++ b/client/resources/stylesheets/tabs/tab.less @@ -21,7 +21,15 @@ } &.dragover { - padding-left: 150px; + &:before { + position: absolute; + left: 0px; + width: 2px; + top: 0px; + bottom: 0px; + background: @codeboxActiveColor; + content: " "; + } } diff --git a/client/resources/stylesheets/ui/body.less b/client/resources/stylesheets/ui/body.less index 519e9f96..5e85d56a 100644 --- a/client/resources/stylesheets/ui/body.less +++ b/client/resources/stylesheets/ui/body.less @@ -2,17 +2,11 @@ .cb-body { position: absolute; top: @codeboxMenubarHeight; - left: @codeboxLateralbarWidth; + left: @codeboxToolbarWidth; right: 0px; bottom: 0px; z-index: 10; background: #ddd; .transition(left @lateralTransitionSpeed); } - - &.mode-body-fullpage:not(.mode-search) { - .cb-body { - left: @codeboxToolbarWidth; - } - } } \ No newline at end of file diff --git a/client/resources/stylesheets/ui/grid.less b/client/resources/stylesheets/ui/grid.less index b5fac366..9532b691 100644 --- a/client/resources/stylesheets/ui/grid.less +++ b/client/resources/stylesheets/ui/grid.less @@ -1,9 +1,9 @@ .component-grid { position: absolute; - top: -1px; - left: -1px; - right: -1px; - bottom: -1px; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; .grid-section { position: absolute; diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index d5bcd16b..2b003573 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -8,7 +8,7 @@ define([ ], function(_, $, hr, Command, PanelBaseView, TabsManager) { var PanelsView = hr.View.extend({ - className: "cb-panels-list", + className: "cb-panels", defaults: {}, events: {}, @@ -33,22 +33,6 @@ define([ this.activePanel = null; this.previousPanel = null; - // Panels visibility - this.visibilityCommand = new Command({}, { - 'type': "checkbox", - 'title': "Show Side Bar", - 'action': function(state) { - if (state) { - that.show(); - } else { - that.close(); - } - } - }); - this.on("state", function(state) { - that.visibilityCommand.toggleFlag("active", state); - }); - // Menu of panels choice this.panelsCommand = new Command({}, { 'type': "menu", @@ -101,7 +85,6 @@ define([ } else { this.trigger("close"); } - this.trigger("state", opened); return this; }, diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 8f0e45cb..9d2e03d9 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -295,7 +295,7 @@ define([ return Q.all( _.chain(state.sections || []) .map(function(section) { - if (section.tabs.length > 0) that.getSection(section.id); + that.getSection(section.id); return section.tabs; }) .flatten() @@ -321,6 +321,7 @@ define([ .value() ) .then(function() { + that.checkSections(); return n; }); } From 47f81775527086862e589f7081886654c96cb937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 16:59:02 +0100 Subject: [PATCH 036/646] Add optional default width for grid section --- client/core/app.js | 4 +++- client/views/grid.js | 27 +++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/client/core/app.js b/client/core/app.js index 0b50da4d..0b7b8676 100644 --- a/client/core/app.js +++ b/client/core/app.js @@ -50,7 +50,9 @@ box, session, addons, box, files, commands, menu, tabs, panels, operations, loca this.grid = new GridView({ columns: 1000 }); - this.grid.addView(panels); + this.grid.addView(panels, { + width: 20 + }); this.grid.addView(tabs); // Tabs diff --git a/client/views/grid.js b/client/views/grid.js index 5bafa1e3..c16cff29 100644 --- a/client/views/grid.js +++ b/client/views/grid.js @@ -24,7 +24,12 @@ define([ /* * Add a view */ - addView: function(view) { + addView: function(view, options) { + view._grid = this; + view._gridOptions = _.defaults(options || {}, { + width: null + }); + this.views.push(view); return this.render(); }, @@ -77,7 +82,7 @@ define([ * Re-render the complete layout */ render: function() { - var x, y; + var x, y, lineW; // Detach view _.each(this.views, function(view) { @@ -94,22 +99,31 @@ define([ var sectionHeight = (100/layout.lines).toFixed(3); // Add grid content - x = 0; y = 0; + x = 0; y = 0; lineW = 100; + _.each(this.views, function(view, i) { - var $section, $content; + 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(3); // Container object $section = $("
", { 'class': 'grid-section', 'css': { - 'left': (x * sectionWidth)+"%", + 'left': (100 - lineW)+"%", 'top': (y * sectionHeight)+"%", - 'width': sectionWidth+"%", + 'width': w+"%", 'height': sectionHeight+"%" } }); $section.appendTo(this.$el); + lineW = lineW - w; + // Content $content = $("
", { 'class': 'grid-section-content' @@ -144,6 +158,7 @@ define([ if (x >= layout.columns) { x = 0; y = y + 1; + lineW = 100; } }, this); From 9764c81a9595c74ea09c6716ddedaf80b00f7070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 17:31:31 +0100 Subject: [PATCH 037/646] Add operations visible on lateral bar --- client/core/app.js | 29 +++++++++++-------- client/resources/stylesheets/main.less | 3 +- .../resources/stylesheets/ui/lateralbar.less | 24 +-------------- .../resources/stylesheets/ui/operations.less | 5 ++++ .../{panels/list.less => ui/panels.less} | 6 ++++ client/resources/stylesheets/variables.less | 1 - client/resources/templates/main.html | 9 ++---- client/views/grid.js | 8 +++-- 8 files changed, 37 insertions(+), 48 deletions(-) rename client/resources/stylesheets/{panels/list.less => ui/panels.less} (76%) diff --git a/client/core/app.js b/client/core/app.js index 0b7b8676..79e2f189 100644 --- a/client/core/app.js +++ b/client/core/app.js @@ -47,15 +47,29 @@ box, session, addons, box, files, commands, menu, tabs, panels, operations, loca this._autologin = true; this.loginError = null; + // Init base grid for UI this.grid = new GridView({ columns: 1000 }); - this.grid.addView(panels, { - width: 20 + + // Add lateral bar: panels and operations + var v = this.grid.addView(new hr.View(), { + width: 18 + }); + panels.$el.appendTo(v.$el); + operations.$el.appendTo(v.$el); + + // Add operations + operations.on("add remove reset", function() { + setTimeout(function() { + panels.$el.css("bottom", operations.$el.height()); + }, 200); }); + + // Add tabs this.grid.addView(tabs); - // Tabs + // Default tab: new file tabs.on("tabs:default tabs:opennew", function() { files.openNew(); }, this); @@ -117,15 +131,6 @@ box, session, addons, box, files, commands, menu, tabs, panels, operations, loca this.grid.$el.appendTo(this.$(".cb-body")); - // Add operations - operations.$el.appendTo(this.$(".lateral-operations")); - operations.render(); - operations.on("add remove reset", function() { - setTimeout(function() { - that.$(".lateral-body").css("bottom", operations.$el.height()); - }, 200); - }); - // Load addons loading.show(addons.loadAll()).fail(function(err) { return dialogs.alert("Error loading Add-ons", diff --git a/client/resources/stylesheets/main.less b/client/resources/stylesheets/main.less index 031cda31..0ffbf371 100644 --- a/client/resources/stylesheets/main.less +++ b/client/resources/stylesheets/main.less @@ -9,8 +9,7 @@ @import "tabs/section.less"; @import "tabs/manager.less"; -@import "panels/list.less"; - +@import "ui/panels.less"; @import "ui/menu.less"; @import "ui/alert.less"; @import "ui/loading.less"; diff --git a/client/resources/stylesheets/ui/lateralbar.less b/client/resources/stylesheets/ui/lateralbar.less index b5f40553..277206c1 100644 --- a/client/resources/stylesheets/ui/lateralbar.less +++ b/client/resources/stylesheets/ui/lateralbar.less @@ -4,7 +4,7 @@ top: @codeboxMenubarHeight; left: 0px; bottom: 0px; - width: @codeboxLateralbarWidth; + width: @codeboxToolbarWidth; .transition(width @lateralTransitionSpeed); .user-select(none); background: @codeboxBodyBackground; @@ -19,27 +19,5 @@ height: 100%; background: @codeboxBodyBackground; } - - .lateral-body { - position: absolute; - top: 0px; - left: @codeboxToolbarWidth; - bottom: 0px; - right: 0px; - overflow-y: auto; - overflow-x: hidden; - .transition(all @lateralTransitionSpeed); - z-index: 1; - color: #000; - text-shadow: none; - } - - .lateral-operations { - position: absolute; - bottom: 0px; - left: 0px; - right: 0px; - z-index: 10; - } } } \ No newline at end of file diff --git a/client/resources/stylesheets/ui/operations.less b/client/resources/stylesheets/ui/operations.less index 1c39ad1d..e133e869 100644 --- a/client/resources/stylesheets/ui/operations.less +++ b/client/resources/stylesheets/ui/operations.less @@ -2,6 +2,11 @@ list-style: none; padding: 0px; margin: 0px; + position: absolute; + bottom: 0px; + right: 0px; + left: 0px; + z-index: 10; .operation-item { padding: 5px 10px; diff --git a/client/resources/stylesheets/panels/list.less b/client/resources/stylesheets/ui/panels.less similarity index 76% rename from client/resources/stylesheets/panels/list.less rename to client/resources/stylesheets/ui/panels.less index 8c8c96a3..2fa2c36c 100644 --- a/client/resources/stylesheets/panels/list.less +++ b/client/resources/stylesheets/ui/panels.less @@ -1,4 +1,10 @@ .cb-panels { + position: absolute; + bottom: 0px; + right: 0px; + left: 0px; + top: 0px; + .component-tab { width: 100%; max-width: 100%; diff --git a/client/resources/stylesheets/variables.less b/client/resources/stylesheets/variables.less index 4207bfb3..f11f0934 100644 --- a/client/resources/stylesheets/variables.less +++ b/client/resources/stylesheets/variables.less @@ -17,7 +17,6 @@ @codeboxToolbarWidth: 34px; /* Lateral bar */ -@codeboxLateralbarWidth: 266px; @lateralTransitionSpeed: 350ms; @codeboxBodyBackground: #e5e5e5; diff --git a/client/resources/templates/main.html b/client/resources/templates/main.html index 1e9bc363..5d8182f1 100644 --- a/client/resources/templates/main.html +++ b/client/resources/templates/main.html @@ -4,15 +4,10 @@
<%- session.user.get("email") %>
- - - + +
-
-
-
-
diff --git a/client/views/grid.js b/client/views/grid.js index c16cff29..7891baf3 100644 --- a/client/views/grid.js +++ b/client/views/grid.js @@ -31,7 +31,9 @@ define([ }); this.views.push(view); - return this.render(); + this.update(); + + return view; }, /* @@ -43,7 +45,7 @@ define([ this.views = _.filter(this.views, function(_v) { return _v.cid != view; }); - return this.render(); + this.update(); }, /* @@ -51,7 +53,7 @@ define([ */ setLayout: function(n) { this.columns = n; - return this.render(); + this.update(); }, /* From f1e37c8764c93194d52f19e190c5306c4a0e53cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 18:30:37 +0100 Subject: [PATCH 038/646] Improve menubar design --- addons/cb.theme.dark/main.js | 10 ++++++---- client/resources/stylesheets/ui/menubar.less | 9 +++++---- client/resources/stylesheets/variables.less | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/addons/cb.theme.dark/main.js b/addons/cb.theme.dark/main.js index dc2c898d..f18a02e2 100644 --- a/addons/cb.theme.dark/main.js +++ b/addons/cb.theme.dark/main.js @@ -3,6 +3,9 @@ define([ ], function(aceTheme) { var themes = codebox.require("core/themes"); + var bgDarker = "#1a1d24"; + var colorDarker = "#505c66"; + var bgDark = "#222830"; var colorDark = "#64737e"; @@ -22,13 +25,12 @@ define([ styles: { // Top menubar menubar: { - 'background': bgNormal, - 'color': colorNormal, + 'background': bgDarker, + 'color': colorDarker, 'border-color': "#111", button: { - 'border-color': bgNormal, - 'border-bottom-color': "transparent" + 'border-color': bgNormal } }, diff --git a/client/resources/stylesheets/ui/menubar.less b/client/resources/stylesheets/ui/menubar.less index 755e3f87..40278bbe 100644 --- a/client/resources/stylesheets/ui/menubar.less +++ b/client/resources/stylesheets/ui/menubar.less @@ -3,14 +3,14 @@ top: 0px; right: 0px; left: 0px; - height: @codeboxMenubarHeight; + height: @codeboxMenubarHeight+1; border-bottom: 1px solid #ccc; z-index: 1000; .cb-commands-menubar { float: left; margin: 0px; - padding: 2px; + padding: 0px; .menu-command-item { color: inherit; @@ -23,11 +23,13 @@ & > .btn { border-radius: 0px; background: transparent; - padding: 2px 8px; + padding: 3px 8px; cursor: default; color: inherit; -webkit-font-smoothing: antialiased; border: 1px solid transparent; + border-top: 0px; + border-bottom: 0px; } &:hover { @@ -44,7 +46,6 @@ box-shadow: none; color: #333333; border-color: rgba(0, 0, 0, 0.15); - border-bottom-color: transparent; } } diff --git a/client/resources/stylesheets/variables.less b/client/resources/stylesheets/variables.less index f11f0934..c6fa01bf 100644 --- a/client/resources/stylesheets/variables.less +++ b/client/resources/stylesheets/variables.less @@ -11,7 +11,7 @@ @codeboxActiveColor2Darker: #27ae60; /* Menu bar */ -@codeboxMenubarHeight: 32px; +@codeboxMenubarHeight: 26px; /* Commands toolbar */ @codeboxToolbarWidth: 34px; From a0384dbc5931f550104059f5abc23bd927b8d77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 18:35:05 +0100 Subject: [PATCH 039/646] Fix body color for default theme --- client/resources/stylesheets/ui/body.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/resources/stylesheets/ui/body.less b/client/resources/stylesheets/ui/body.less index 5e85d56a..e1c06ac8 100644 --- a/client/resources/stylesheets/ui/body.less +++ b/client/resources/stylesheets/ui/body.less @@ -6,7 +6,7 @@ right: 0px; bottom: 0px; z-index: 10; - background: #ddd; + background: @codeboxBodyBackground; .transition(left @lateralTransitionSpeed); } } \ No newline at end of file From 0b5629acf1df5072e76d6712deabf45af065dd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 18:57:42 +0100 Subject: [PATCH 040/646] Disable drag and drop for panels --- client/utils/dragdrop.js | 11 +++++++++++ client/views/panels/manager.js | 1 + client/views/tabs/manager.js | 6 +++++- client/views/tabs/tab.js | 2 -- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/client/utils/dragdrop.js b/client/utils/dragdrop.js index a31e2e6a..644d6e69 100644 --- a/client/utils/dragdrop.js +++ b/client/utils/dragdrop.js @@ -57,10 +57,19 @@ define([ // 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; @@ -108,7 +117,9 @@ define([ if (options.data) data = options.data; $el.mousedown(function(e) { + if (!that.state) return; e.preventDefault(); + var dx, dy, hasMove = false; // origin mouse diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index 2b003573..e449819e 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -25,6 +25,7 @@ define([ }, tabMenu: false, newTab: false, + draggable: false, maxTabsPerSection: 1 }, this); this.tabs.$el.appendTo(this.$el); diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 9d2e03d9..da15afe1 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -40,7 +40,10 @@ define([ newTab: true, // Max number of tabs per sections (-1 for unlimited) - maxTabsPerSection: -1 + maxTabsPerSection: -1, + + // Tabs are draggable + draggable: true }, events: {}, @@ -56,6 +59,7 @@ define([ // Drag and drop of tabs this.drag = new dnd.DraggableType(); + this.drag.toggle(this.options.draggable); this.drag.on("drop", function(section, tab) { if (!section && tab) tab.splitSection(); }) diff --git a/client/views/tabs/tab.js b/client/views/tabs/tab.js index 495a5733..3bc51878 100644 --- a/client/views/tabs/tab.js +++ b/client/views/tabs/tab.js @@ -36,8 +36,6 @@ define([ var that = this; var $document = $(document); - this.$el.attr("draggable", true); - // Drop tabs to order this.dropArea = new dnd.DropArea({ view: this, From b3127bcfde0fc36070020f8a994598fa4929e0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 18:59:20 +0100 Subject: [PATCH 041/646] Improve menubar for dark theme --- addons/cb.theme.dark/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/cb.theme.dark/main.js b/addons/cb.theme.dark/main.js index f18a02e2..16fa27a5 100644 --- a/addons/cb.theme.dark/main.js +++ b/addons/cb.theme.dark/main.js @@ -26,7 +26,7 @@ define([ // Top menubar menubar: { 'background': bgDarker, - 'color': colorDarker, + 'color': colorDark, 'border-color': "#111", button: { From da3ba0e4faad098e24ed33c7ad599a5556893dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 19:12:47 +0100 Subject: [PATCH 042/646] Use beautiful cursors for drag and drop and grid resize --- client/utils/dragdrop.js | 29 +++++++++++++++++++++++++++-- client/views/grid.js | 8 ++++++-- client/views/tabs/manager.js | 2 -- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/client/utils/dragdrop.js b/client/utils/dragdrop.js index 644d6e69..21a71bed 100644 --- a/client/utils/dragdrop.js +++ b/client/utils/dragdrop.js @@ -1,7 +1,21 @@ define([ + 'hr/utils', 'hr/hr', 'hr/dom' -], function (hr, $) { +], function (_, hr, $) { + + // 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 @@ -110,7 +124,10 @@ define([ baseDropArea: null, // Before dragging - start: null + start: null, + + // Cursor + cursor: "copy" }); if (options.el) $el = $(options.el); if (options.view) $el = options.view.$el, data = options.view; @@ -176,9 +193,13 @@ define([ }); }; + setCursor(options.cursor); + $document.mousemove(f); $document.one("mouseup", function(e) { $document.unbind('mousemove', f); + resetCursor(); + var _drop = that.getDrop(); if (hasMove && (!options.baseDropArea || !_drop || (options.baseDropArea.cid != _drop.cid))) { @@ -202,6 +223,10 @@ define([ }); return { + cursor: { + set: setCursor, + reset: resetCursor + }, DropArea: DropArea, DraggableType: DraggableType }; diff --git a/client/views/grid.js b/client/views/grid.js index 7891baf3..f4e1a325 100644 --- a/client/views/grid.js +++ b/client/views/grid.js @@ -1,8 +1,9 @@ define([ "hr/utils", "hr/dom", - "hr/hr" -], function(_, $, hr) { + "hr/hr", + "utils/dragdrop" +], function(_, $, hr, dnd) { var GridView = hr.View.extend({ className: "component-grid", defaults: { @@ -177,6 +178,8 @@ define([ 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; @@ -194,6 +197,7 @@ define([ $document.mousemove(f); $document.mouseup(function(e) { $document.unbind('mousemove', f); + dnd.cursor.reset(); }); }; }, diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index da15afe1..eff42d48 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -184,11 +184,9 @@ define([ var sectionId = options.section; for (;;) { var section = this.getSection(sectionId); - console.log("test section ", sectionId) if (this.options.maxTabsPerSection > 0 && section.tabs.size() >= this.options.maxTabsPerSection) { sectionId = _.uniqueId("tabSection"); } else { - console.log("add to section ", sectionId); section.addTab(tab); break; } From 9d0923eeaef9b12c6bdd5ee7a5205c369930be71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 19:16:36 +0100 Subject: [PATCH 043/646] Fix cursor on click and draggable tab --- client/utils/dragdrop.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/utils/dragdrop.js b/client/utils/dragdrop.js index 21a71bed..27077ec9 100644 --- a/client/utils/dragdrop.js +++ b/client/utils/dragdrop.js @@ -168,7 +168,10 @@ define([ dy = oY - e.pageY; if (Math.abs(dx) > 20 || Math.abs(dy) > 20) { - if (!hasMove) $el.addClass("move"); + if (!hasMove) { + setCursor(options.cursor); + $el.addClass("move"); + } hasMove = true; } @@ -193,13 +196,11 @@ define([ }); }; - setCursor(options.cursor); - $document.mousemove(f); $document.one("mouseup", function(e) { $document.unbind('mousemove', f); resetCursor(); - + var _drop = that.getDrop(); if (hasMove && (!options.baseDropArea || !_drop || (options.baseDropArea.cid != _drop.cid))) { From 3820ea908156ecc99b97ceb977d2e0928043cd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 22:05:32 +0100 Subject: [PATCH 044/646] Improve files panel --- addons/cb.panel.files/client.js | 7 ++++- addons/cb.panel.files/settings.js | 4 --- addons/cb.panel.files/stylesheets/files.less | 14 ++++++--- addons/cb.panel.files/stylesheets/panel.less | 13 -------- addons/cb.panel.files/templates/item.html | 8 +++-- addons/cb.panel.files/templates/panel.html | 10 +----- addons/cb.panel.files/views/list.js | 23 -------------- addons/cb.panel.files/views/panel.js | 32 +++----------------- client/models/box.js | 3 ++ client/models/file.js | 2 +- client/resources/stylesheets/ui/panels.less | 5 +++ client/views/panels/base.js | 4 ++- client/views/panels/manager.js | 8 +++-- 13 files changed, 43 insertions(+), 90 deletions(-) delete mode 100644 addons/cb.panel.files/views/list.js diff --git a/addons/cb.panel.files/client.js b/addons/cb.panel.files/client.js index 5f2de0c7..fb7ec810 100644 --- a/addons/cb.panel.files/client.js +++ b/addons/cb.panel.files/client.js @@ -10,7 +10,9 @@ define([ var box = codebox.require("core/box"); // Add files panels - var panel = panels.register("files", PanelFilesView); + var panel = panels.register("files", PanelFilesView, { + title: "Folder" + }); // Open files panel panel.connectCommand(commands.register("files.tree.open", { @@ -73,4 +75,7 @@ define([ } } ]); + + // Open panel + panel.open(); }); \ No newline at end of file diff --git a/addons/cb.panel.files/settings.js b/addons/cb.panel.files/settings.js index f56f3c9d..95326ed3 100644 --- a/addons/cb.panel.files/settings.js +++ b/addons/cb.panel.files/settings.js @@ -12,10 +12,6 @@ define([], function() { 'gitfolder': false }, 'fields': { - 'openfiles': { - 'label': "Show Open Files", - 'type': "checkbox" - }, 'hiddenfiles': { 'label': "Show Hidden Files", 'type': "checkbox" diff --git a/addons/cb.panel.files/stylesheets/files.less b/addons/cb.panel.files/stylesheets/files.less index d9fa0ddd..288c2f67 100644 --- a/addons/cb.panel.files/stylesheets/files.less +++ b/addons/cb.panel.files/stylesheets/files.less @@ -57,15 +57,19 @@ width: 11px; text-align: center; - &.fa-file { + &.file-icon { + margin-right: 3px; + } + + &.invisibile { color: transparent; text-shadow: none; } } - >.fa-caret-right { + >.fa-angle-right { display: inline-block; } - >.fa-caret-down { + >.fa-angle-down { display: none; } } @@ -81,10 +85,10 @@ display: block; } >.name { - >.fa-caret-right { + >.fa-angle-right { display: none; } - >.fa-caret-down { + >.fa-angle-down { display: inline-block; } } diff --git a/addons/cb.panel.files/stylesheets/panel.less b/addons/cb.panel.files/stylesheets/panel.less index fb86161c..6091a148 100644 --- a/addons/cb.panel.files/stylesheets/panel.less +++ b/addons/cb.panel.files/stylesheets/panel.less @@ -6,17 +6,4 @@ width: 100%; z-index: 10; overflow-y: auto; - - .files-panel-section { - margin-bottom: 10px; - - .section-header { - text-transform: uppercase; - font-weight: 800; - padding: 0px 10px; - font-size: 13px; - opacity: 0.65; - line-height: 28px; - } - } } \ No newline at end of file diff --git a/addons/cb.panel.files/templates/item.html b/addons/cb.panel.files/templates/item.html index ccbc5a13..583ff42e 100644 --- a/addons/cb.panel.files/templates/item.html +++ b/addons/cb.panel.files/templates/item.html @@ -1,9 +1,11 @@ <% if (file.isDirectory()) { %> - - + + + <% } else { %> - + + <% } %> <%- file.get("name") %> diff --git a/addons/cb.panel.files/templates/panel.html b/addons/cb.panel.files/templates/panel.html index 7a9f2853..1f49b2cb 100644 --- a/addons/cb.panel.files/templates/panel.html +++ b/addons/cb.panel.files/templates/panel.html @@ -1,9 +1 @@ -
-
Open Files
-
-
- -
-
Folders
-
-
\ No newline at end of file +
    \ No newline at end of file diff --git a/addons/cb.panel.files/views/list.js b/addons/cb.panel.files/views/list.js deleted file mode 100644 index 3503341d..00000000 --- a/addons/cb.panel.files/views/list.js +++ /dev/null @@ -1,23 +0,0 @@ -define([ - "views/tree" -], function(FilesTreeView) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - - // Commands list - var FilesListView = hr.List.extend({ - tagName: "ul", - className: "cb-files-tree", - - Item: FilesTreeView.Item, - defaults: _.extend({}, hr.List.prototype.defaults, { - baseFilter: function(model) { - return !model.isNewfile(); - } - }) - }); - - - return FilesListView; -}); \ No newline at end of file diff --git a/addons/cb.panel.files/views/panel.js b/addons/cb.panel.files/views/panel.js index e183e78f..c8f62bb2 100644 --- a/addons/cb.panel.files/views/panel.js +++ b/addons/cb.panel.files/views/panel.js @@ -1,10 +1,9 @@ define([ "settings", "views/tree", - "views/list", "text!templates/panel.html", "less!stylesheets/panel.less" -], function(panelSettings, FilesTreeView, FilesListView, templateFile) { +], function(panelSettings, FilesTreeView, templateFile) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); @@ -22,22 +21,11 @@ define([ initialize: function() { PanelFilesView.__super__.initialize.apply(this, arguments); - this.listActive = new FilesListView({ - collection: files.active - }); - - this.tree = new FilesTreeView({ + this.tree = new FilesTreeView.Item({ model: box.root }); this.tree.update(); - - this.tree.on("count", function(count) { - this.toggle(count > 0); - }, this); - - this.listActive.on("add remove filter", function(item, state) { - this.$(".files-section-open").toggle(panelSettings.user.get("openfiles") && this.listActive.count() > 0); - }, this); + this.tree.select(); // Offline hr.Offline.on("state", function() { @@ -55,22 +43,12 @@ define([ render: function() { this.tree.$el.detach(); - this.listActive.$el.detach(); return PanelFilesView.__super__.render.apply(this, arguments); }, - finish: function() { - this.toggle(this.tree.countFiles > 0); - - if (panelSettings.user.get("openfiles")) { - this.listActive.$el.appendTo(this.$(".files-section-open .section-content")); - this.$(".files-section-open").toggle(this.listActive.count() > 0); - } else { - this.$(".files-section-open").hide(); - } - - this.tree.$el.appendTo(this.$(".files-section-tree .section-content")); + finish: function() { + this.tree.$el.appendTo(this.$(".root-tree")); return PanelFilesView.__super__.finish.apply(this, arguments); }, diff --git a/client/models/box.js b/client/models/box.js index 95990433..6b699159 100644 --- a/client/models/box.js +++ b/client/models/box.js @@ -36,6 +36,9 @@ define([ 'codebox': this }); this.root.getByPath("/"); + this.on("change:name", function() { + this.root.set("name", this.get("name")); + }, this); // Active file this.activeFile = "/"; diff --git a/client/models/file.js b/client/models/file.js index 882739a1..0f9c7489 100644 --- a/client/models/file.js +++ b/client/models/file.js @@ -355,7 +355,7 @@ define([ path = this.path(path); if (path == "/") { var fileData = { - "name": "/", + "name": this.codebox.get("name"), "size": 0, "mtime": 0, "mime": "inode/directory", diff --git a/client/resources/stylesheets/ui/panels.less b/client/resources/stylesheets/ui/panels.less index 2fa2c36c..084f5455 100644 --- a/client/resources/stylesheets/ui/panels.less +++ b/client/resources/stylesheets/ui/panels.less @@ -11,6 +11,11 @@ .inner { max-width: 100%; + text-transform: uppercase; + font-weight: 800; + font-size: 13px; + opacity: 0.65; + line-height: 28px; } } diff --git a/client/views/panels/base.js b/client/views/panels/base.js index 32c69510..bf179b6a 100644 --- a/client/views/panels/base.js +++ b/client/views/panels/base.js @@ -4,7 +4,9 @@ define([ 'hr/hr' ], function(_, $, hr) { var PanelBaseView = hr.View.extend({ - defaults: {}, + defaults: { + title: "" + }, events: {}, // Constructor diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index e449819e..ccf47d3a 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -47,8 +47,10 @@ define([ }, // Register a new panel - register: function(panelId, panelView, constructor, options) { - constructor = _.extend(constructor || {}, { + register: function(panelId, panelView, constructor) { + constructor = _.extend({ + 'title': panelId + }, constructor || {}, { 'panel': panelId }); @@ -70,7 +72,7 @@ define([ if (pId && this.panels[pId]) { opened = true; var tab = this.tabs.add(TabsManager.Panel, {}, { - 'title': pId, + 'title': this.panels[pId].options.title, 'uniqueId': pId }); if (tab.$el.is(':empty')) { From a4caae69da1f4991d783ad396f9cf0176ae8a2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 3 Mar 2014 22:28:25 +0100 Subject: [PATCH 045/646] Fix events on dom of panel views --- client/views/panels/manager.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/views/panels/manager.js b/client/views/panels/manager.js index ccf47d3a..31dcb447 100644 --- a/client/views/panels/manager.js +++ b/client/views/panels/manager.js @@ -75,8 +75,15 @@ define([ 'title': this.panels[pId].options.title, 'uniqueId': pId }); + + // If new tab if (tab.$el.is(':empty')) { + tab.once("tab:close", function() { + this.panels[pId].$el.detach(); + }, this); + this.panels[pId].$el.appendTo(tab.$el); + this.panels[pId].render(); } } From 7c3f6e3eccbc21724ea7c98ba4d29c77baea0145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 4 Mar 2014 16:24:24 +0100 Subject: [PATCH 046/646] Update hr.js, fix files panel and fix some memory leak with listenTo --- addons/cb.panel.files/templates/panel.html | 1 - addons/cb.panel.files/views/panel.js | 30 +++++++++++----------- addons/cb.panel.files/views/tree.js | 9 ++++++- client/models/tab.js | 4 +-- client/views/commands/menu.js | 11 ++++++-- client/views/files/base.js | 6 +++-- client/views/grid.js | 2 +- client/views/panels/base.js | 6 ----- client/views/panels/manager.js | 5 ++-- client/views/tabs/base.js | 8 +++--- client/views/tabs/file.js | 6 ++--- client/views/tabs/section.js | 8 ++++++ package.json | 2 +- 13 files changed, 57 insertions(+), 41 deletions(-) delete mode 100644 addons/cb.panel.files/templates/panel.html diff --git a/addons/cb.panel.files/templates/panel.html b/addons/cb.panel.files/templates/panel.html deleted file mode 100644 index 1f49b2cb..00000000 --- a/addons/cb.panel.files/templates/panel.html +++ /dev/null @@ -1 +0,0 @@ -
      \ No newline at end of file diff --git a/addons/cb.panel.files/views/panel.js b/addons/cb.panel.files/views/panel.js index c8f62bb2..c4886193 100644 --- a/addons/cb.panel.files/views/panel.js +++ b/addons/cb.panel.files/views/panel.js @@ -1,9 +1,8 @@ define([ "settings", "views/tree", - "text!templates/panel.html", "less!stylesheets/panel.less" -], function(panelSettings, FilesTreeView, templateFile) { +], function(panelSettings, FilesTreeView) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); @@ -15,18 +14,23 @@ define([ var PanelFilesView = PanelBaseView.extend({ className: "cb-panel-files", - templateLoader: "text", - template: templateFile, initialize: function() { PanelFilesView.__super__.initialize.apply(this, arguments); this.tree = new FilesTreeView.Item({ model: box.root - }); + }, this); this.tree.update(); this.tree.select(); + + var $rootTree = $("
      <% } else { %> diff --git a/client/views/commands/menu.js b/client/views/commands/menu.js index 33df4766..1a084dfc 100644 --- a/client/views/commands/menu.js +++ b/client/views/commands/menu.js @@ -85,7 +85,7 @@ define([ var $li = this.$el; $li.empty(); - $li.attr("class", this.className+" "+this.getFlagsClass()); + $li.attr("class", this.className+" "+this.getFlagsClass()+" menu-item-"+itemType); if (itemType == "action") { var $a = this.buildAction(); diff --git a/client/views/commands/statusbar.js b/client/views/commands/statusbar.js new file mode 100644 index 00000000..9dc261a0 --- /dev/null +++ b/client/views/commands/statusbar.js @@ -0,0 +1,11 @@ +define([ + "hr/hr", + "views/commands/manager", + "views/commands/menu" +], function(hr, CommandsView, MenuView) { + var StatusbarView = MenuView.extend({ + className: "cb-commands-menubar" + }); + + return StatusbarView; +}); \ No newline at end of file diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index d44e2469..13bcf45b 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -5,8 +5,9 @@ define([ "utils/dragdrop", "utils/keyboard", "utils/contextmenu", - "models/command" -], function(_, $, hr, DragDrop, Keyboard, ContextMenu, Command) { + "models/command", + "collections/commands" +], function(_, $, hr, DragDrop, Keyboard, ContextMenu, Command, Commands) { // Tab body view var TabPanelView = hr.View.extend({ className: "component-tab-panel", @@ -29,6 +30,7 @@ define([ initialize: function() { TabPanelView.__super__.initialize.apply(this, arguments); var menu = require("core/commands/menu"); + var statusbar = require("core/commands/statusbar"); this.tabs = this.parent; this.tab = this.options.tab; @@ -41,15 +43,28 @@ define([ }); if (this.tab.manager.options.tabMenu) menu.collection.add(this.menu); + // Create statusbar commands + this.statusbar = new Commands(); + this.statusbar.pipe(statusbar.collection); + // Bind tab event this.listenTo(this.tab.manager, "active", function(tab) { var state = tab.id == this.tab.id; this.trigger("tab:state", state); + + // Toggle visibility of commands this.menu.toggleFlag("hidden", !state); + this.statusbar.each(function (command){ + command.toggleFlag("hidden", !state); + }); }); this.on("tab:close", function() { this.menu.destroy(); + this.statusbar.each(function (command){ + command.destroy(); + }); + this.statusbar.stopListening(); }, this); // Keyboard shortcuts diff --git a/package.json b/package.json index a6d6dfaa..03280f0b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "event-stream": "3.0.11", "readable-stream": "1.0.2", "request": "2.27.0", - "hr.js": "git+https://github.com/FriendCode/hr.js.git#ec91c467883c8cb2d068ba9e3766edfff346a40d", + "hr.js": "0.6.3", "commander": "1.1.1", "googlediff": "0.1.0", "eventemitter2": "0.4.13", From 9ee4a394e2405db0541b10e652c560777f386c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 11:27:07 -0700 Subject: [PATCH 104/646] Enable menu in statusbar --- addons/cb.files.editor/views/file.js | 2 ++ .../resources/stylesheets/ui/statusbar.less | 30 ++++++++++++++++++- client/views/tabs/base.js | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/addons/cb.files.editor/views/file.js b/addons/cb.files.editor/views/file.js index 6aff21f1..598f7601 100644 --- a/addons/cb.files.editor/views/file.js +++ b/addons/cb.files.editor/views/file.js @@ -87,6 +87,8 @@ define([ 'type': "label" }); this.tab.statusbar.add(this.editorStatusCommand); + this.tab.statusbar.add(this.collaboratorsMenu); + this.tab.statusbar.add(syntaxMenu); // Tab menu this.tab.menu.menuSection([ diff --git a/client/resources/stylesheets/ui/statusbar.less b/client/resources/stylesheets/ui/statusbar.less index 69941aff..41950552 100644 --- a/client/resources/stylesheets/ui/statusbar.less +++ b/client/resources/stylesheets/ui/statusbar.less @@ -13,7 +13,7 @@ list-style: none; font-size: 13px; - .menu-item { + & > .menu-item { color: inherit; margin: 0px 4px; cursor: default; @@ -23,11 +23,35 @@ float: right; display: inline-block; + .menu-label { + margin-left: 4px; + } + + /* Simple label */ &.menu-item-label { float: left; padding: 3px 3px; } + /* Menu */ + &.menu-item-menu { + .ui-menu { + bottom: 100%; + top: auto; + left: auto; + right: 0px; + max-height: 500px; + overflow: auto; + } + + &:not(.disabled):hover { + .ui-menu { + display: block; + } + } + } + + /* Action/Menu */ & > a { border-radius: 0px; background: transparent; @@ -39,6 +63,10 @@ border-top: 0px; border-bottom: 0px; display: inline-block; + + &:hover, &:focus { + text-decoration: none; + } } &:hover { diff --git a/client/views/tabs/base.js b/client/views/tabs/base.js index 13bcf45b..5d9d9cba 100644 --- a/client/views/tabs/base.js +++ b/client/views/tabs/base.js @@ -61,10 +61,10 @@ define([ }); this.on("tab:close", function() { this.menu.destroy(); + this.statusbar.stopListening(); this.statusbar.each(function (command){ command.destroy(); }); - this.statusbar.stopListening(); }, this); // Keyboard shortcuts From d02d0b2f1d2a1cad4c42be5cbcc22f4b8cf2c69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 11:41:05 -0700 Subject: [PATCH 105/646] Add settings for code editor to show invisibles characters --- addons/cb.files.editor/client.js | 5 +++++ addons/cb.files.editor/views/file.js | 2 ++ 2 files changed, 7 insertions(+) diff --git a/addons/cb.files.editor/client.js b/addons/cb.files.editor/client.js index 0e5d8ded..1ce56981 100644 --- a/addons/cb.files.editor/client.js +++ b/addons/cb.files.editor/client.js @@ -20,6 +20,7 @@ define([ 'theme': "github", 'fontsize': "12", 'printmargincolumn': 80, + 'showinvisibles': false, 'showprintmargin': false, 'highlightactiveline': false, 'wraplimitrange': 80, @@ -68,6 +69,10 @@ define([ 'label': "Show Print Margin", 'type': "checkbox" }, + 'showinvisibles': { + 'label': "Show Invisibles", + 'type': "checkbox" + }, 'highlightactiveline': { 'label': "Highlight Active Line", 'type': "checkbox" diff --git a/addons/cb.files.editor/views/file.js b/addons/cb.files.editor/views/file.js index 598f7601..bdd96ae4 100644 --- a/addons/cb.files.editor/views/file.js +++ b/addons/cb.files.editor/views/file.js @@ -402,6 +402,7 @@ define([ fontsize: "12", printmargincolumn: 80, showprintmargin: false, + showinvisibles: false, highlightactiveline: false, wraplimitrange: 80, enablesoftwrap: false, @@ -416,6 +417,7 @@ define([ this.$editor.css("font-size", this.options.fontsize+"px"); this.editor.setPrintMarginColumn(this.options.printmargincolumn); this.editor.setShowPrintMargin(this.options.showprintmargin); + this.editor.setShowInvisibles(this.options.showinvisibles); this.editor.setHighlightActiveLine(this.options.highlightactiveline); this.editor.getSession().setUseWrapMode(this.options.enablesoftwrap); this.editor.getSession().setWrapLimitRange(this.options.wraplimitrange, this.options.wraplimitrange); From 817c7b9029248aad8425bbf07b6d87b735bd6b0a Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Sun, 9 Mar 2014 11:42:26 -0700 Subject: [PATCH 106/646] Add MySQL start/stop support to run_apache.sh Improves #290 --- core/cb.project/php/run_apache.sh | 129 +++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 20 deletions(-) diff --git a/core/cb.project/php/run_apache.sh b/core/cb.project/php/run_apache.sh index 67ba10bd..1a19a053 100755 --- a/core/cb.project/php/run_apache.sh +++ b/core/cb.project/php/run_apache.sh @@ -22,13 +22,13 @@ CONF="apache2.conf" # Platform specific apache extras EXTRA_CONF='' if [[ $platform == 'Linux' ]]; then - EXTRA_CONF=" +EXTRA_CONF=" # Include module configuration: Include /etc/apache2/mods-enabled/*.load Include /etc/apache2/mods-enabled/*.conf " elif [[ $platform == 'Darwin' ]]; then - EXTRA_CONF=" +EXTRA_CONF=" # Modules $(cat /etc/apache2/httpd.conf | grep LoadModule | sed 's/libexec/\/usr\/libexec/g') LoadModule php5_module /usr/libexec/apache2/libphp5.so @@ -37,7 +37,7 @@ fi # Include phpmyadmin only if there if [[ -f "/etc/apache2/conf.d/phpmyadmin.conf" ]]; then - EXTRA_CONF+=" +EXTRA_CONF+=" Include /etc/apache2/conf.d/phpmyadmin.conf " fi @@ -64,9 +64,9 @@ MaxSpareServers 1 # Serve our workspace DocumentRoot "${WORKSPACE}" - AllowOverride all - Order allow,deny - Allow from all +AllowOverride all +Order allow,deny +Allow from all AddType application/x-httpd-php .php @@ -77,30 +77,116 @@ ${EXTRA_CONF} EOF +# Keep track of MySQL setup state +SUDO_MYSQL=false +MYSQL_STARTED=false +MYSQL_PORT=3306 + +function is_mysql_running() { +# Check if there is a TCP server listening on MySQL's port +netstat -nat | grep -i listen | grep -e "[\:\.]${MYSQL_ROOT}" &> /dev/null +# Check for success +if [ $? = 0 ]; then +echo "true" +fi +} + +# Echoes sudo if the system supports sudo without password for current user (codebox.io boxes for example) +function needs_sudo_pwd() { +sudo -n echo | head -n 1 | grep -q -v "sudo:" +local success=$? +if [ success = 0 ]; then +# No password needed +echo "true" +fi +} + +# Start mysql and set SUDO_MYSQL +function start_mysql() { +echo "Starting MySQL server ..." + +# Exit if MySQL is not on $PATH +if [ -z "$(which mysqld)" ]; then +echo "Could not start MySQL because it is not installed on the system's \$PATH" +fi + +# Check if MySQL is already running +if [ -n "$(is_mysql_running)" ]; then +echo "MySQL appears to already be running on PORT=${MYSQL_PORT}" +return +fi + +locals needs_pwd="$(needs_sudo_pwd)" +# Try running in sudo or not +if [ -n ${needs_pwd} ]; then +sudo -n mysqld & +SUDO_MYSQL=true +else +mysqld & +fi + +# If MySQL is not running +if [ -z "$(is_mysql_running)" ]; then +# Try running with sudo (and passwd prompt) +if [ ! $SUDO_MYSQL ]; then +echo "Please enter sudo password for MySQL" +sudo mysqld & +fi +else +echo "MySQL is up and running" +fi + +# After all our different tries is MySQL up ? +if [ -n "$(is_mysql_running)" ]; then +MYSQL_STARTED=true +fi +} + +# Stop MySQL started by "start_mysql" +function stop_mysql() { +echo "Killing MySQL" +# MySQL wasn't started by this script +# or is already dead +if [ !${MYSQL_STARTED} || -z "$(is_mysql_running)" ]; then +# So do nothing +return +fi + +# Force kill MySQL +killall -s KILL mysqld +} # Wait for a process or group of processes function anywait() { - for pid in "$@"; do - while kill -0 "$pid" &> /dev/null; do - sleep 0.5 - done - done +for pid in "$@"; do +while kill -0 "$pid" &> /dev/null; do +sleep 0.5 +done +done } function cleanup { - if [[ -f ${PID_FILE} ]]; then - echo "Killed process" - # Kill process and all children - /bin/kill -s KILL -$(cat ${PID_FILE}) - fi - # Remove folder on exit - echo "Cleaning up ${FOLDER}" - rm -rf ${FOLDER} +# Kill Apache +if [[ -f ${PID_FILE} ]]; then +echo "Killed process" +# Kill process and all children +/bin/kill -s KILL -$(cat ${PID_FILE}) +fi + +# Kill MySQL +stop_mysql + +# Remove folder on exit +echo "Cleaning up ${FOLDER}" +rm -rf ${FOLDER} } # Cleanup when killed trap cleanup EXIT INT KILL +# Run MySQL +start_mysql + # Run apache process in foreground echo "Running apache2 on ${WORKSPACE} (${FOLDER})" /usr/sbin/apachectl -d ${FOLDER} -f ${CONF} -e info @@ -112,4 +198,7 @@ ${DIR}/_waitfile.sh ${PID_FILE} 5 PID=$(cat ${PID_FILE}) echo "Waiting for Apache2 process : ${PID}" anywait ${PID} -echo "Apache is dead (pid=${PID})" \ No newline at end of file +echo "Apache is dead (pid=${PID})" + +# Cleanup on exit +cleanup From 118462b9d915931b7a9aa150473d93b5a24eced3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 13:38:12 -0700 Subject: [PATCH 107/646] Fix run state when clicking the operation --- addons/cb.project/autorun.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/addons/cb.project/autorun.js b/addons/cb.project/autorun.js index b3979a0e..6d71e229 100644 --- a/addons/cb.project/autorun.js +++ b/addons/cb.project/autorun.js @@ -76,14 +76,10 @@ define([ } }); - // Stop - op.on("destroy", function() { - setRunTerminal(undefined); - }); - // Terminal is close: finish the operation runInfo.terminal.on("tab:close", function() { op.destroy(); + setRunTerminal(undefined); }); // Set active runningterminal From 6b02cf4d032d200d39a39af86a46d0c1b4c19fd8 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Sun, 9 Mar 2014 14:04:28 -0700 Subject: [PATCH 108/646] Fix run_apache.sh indentation and apache killing in cleanup --- core/cb.project/php/run_apache.sh | 184 ++++++++++++++++-------------- 1 file changed, 98 insertions(+), 86 deletions(-) diff --git a/core/cb.project/php/run_apache.sh b/core/cb.project/php/run_apache.sh index 1a19a053..3e7522d1 100755 --- a/core/cb.project/php/run_apache.sh +++ b/core/cb.project/php/run_apache.sh @@ -22,13 +22,13 @@ CONF="apache2.conf" # Platform specific apache extras EXTRA_CONF='' if [[ $platform == 'Linux' ]]; then -EXTRA_CONF=" + EXTRA_CONF=" # Include module configuration: Include /etc/apache2/mods-enabled/*.load Include /etc/apache2/mods-enabled/*.conf " elif [[ $platform == 'Darwin' ]]; then -EXTRA_CONF=" + EXTRA_CONF=" # Modules $(cat /etc/apache2/httpd.conf | grep LoadModule | sed 's/libexec/\/usr\/libexec/g') LoadModule php5_module /usr/libexec/apache2/libphp5.so @@ -37,7 +37,7 @@ fi # Include phpmyadmin only if there if [[ -f "/etc/apache2/conf.d/phpmyadmin.conf" ]]; then -EXTRA_CONF+=" + EXTRA_CONF+=" Include /etc/apache2/conf.d/phpmyadmin.conf " fi @@ -64,9 +64,9 @@ MaxSpareServers 1 # Serve our workspace DocumentRoot "${WORKSPACE}" -AllowOverride all -Order allow,deny -Allow from all + AllowOverride all + Order allow,deny + Allow from all AddType application/x-httpd-php .php @@ -83,106 +83,118 @@ MYSQL_STARTED=false MYSQL_PORT=3306 function is_mysql_running() { -# Check if there is a TCP server listening on MySQL's port -netstat -nat | grep -i listen | grep -e "[\:\.]${MYSQL_ROOT}" &> /dev/null -# Check for success -if [ $? = 0 ]; then -echo "true" -fi + # Check if there is a TCP server listening on MySQL's port + netstat -nat | grep -i listen | grep -e "[\:\.]${MYSQL_PORT}" &> /dev/null + # Check for success + if [ $? == 0 ]; then + echo "true" + fi } # Echoes sudo if the system supports sudo without password for current user (codebox.io boxes for example) function needs_sudo_pwd() { -sudo -n echo | head -n 1 | grep -q -v "sudo:" -local success=$? -if [ success = 0 ]; then -# No password needed -echo "true" -fi + sudo -n echo | head -n 1 | grep -q -v "sudo:" + if [ $? == 0 ]; then + # No password needed + echo "true" + fi } # Start mysql and set SUDO_MYSQL function start_mysql() { -echo "Starting MySQL server ..." - -# Exit if MySQL is not on $PATH -if [ -z "$(which mysqld)" ]; then -echo "Could not start MySQL because it is not installed on the system's \$PATH" -fi - -# Check if MySQL is already running -if [ -n "$(is_mysql_running)" ]; then -echo "MySQL appears to already be running on PORT=${MYSQL_PORT}" -return -fi - -locals needs_pwd="$(needs_sudo_pwd)" -# Try running in sudo or not -if [ -n ${needs_pwd} ]; then -sudo -n mysqld & -SUDO_MYSQL=true -else -mysqld & -fi - -# If MySQL is not running -if [ -z "$(is_mysql_running)" ]; then -# Try running with sudo (and passwd prompt) -if [ ! $SUDO_MYSQL ]; then -echo "Please enter sudo password for MySQL" -sudo mysqld & -fi -else -echo "MySQL is up and running" -fi - -# After all our different tries is MySQL up ? -if [ -n "$(is_mysql_running)" ]; then -MYSQL_STARTED=true -fi + echo "Starting MySQL server ..." + + # Commands + local sudo_mysql="mysqld --user=root" + local mysql="mysqld" + + # Exit if MySQL is not on $PATH + if [ -z "$(which mysqld)" ]; then + echo "Could not start MySQL because it is not installed on the system's \$PATH" + fi + + echo "MySQL RUNNING = $(is_mysql_running)" + # Check if MySQL is already running + if [ -n "$(is_mysql_running)" ]; then + echo "MySQL appears to already be running on PORT=${MYSQL_PORT}" + return + fi + + locals needs_pwd="$(needs_sudo_pwd)" + # Try running in sudo or not + if [ -n ${needs_pwd} ]; then + sudo -n ${sudo_mysql} & + SUDO_MYSQL=true + else + ${mysql} & + fi + + # If MySQL is not running + if [ -z "$(is_mysql_running)" ]; then + # Try running with sudo (and passwd prompt) + if [ ! $SUDO_MYSQL ]; then + echo "Please enter sudo password for MySQL" + sudo ${sudo_mysql} & + SUDO_MYSQL=true + fi + else + echo "MySQL is up and running" + fi + + # After all our different tries is MySQL up ? + if [ -n "$(is_mysql_running)" ]; then + MYSQL_STARTED=true + fi } # Stop MySQL started by "start_mysql" function stop_mysql() { -echo "Killing MySQL" -# MySQL wasn't started by this script -# or is already dead -if [ !${MYSQL_STARTED} || -z "$(is_mysql_running)" ]; then -# So do nothing -return -fi - -# Force kill MySQL -killall -s KILL mysqld + echo "Killing MySQL" + # MySQL wasn't started by this script + # or is already dead + if [ !${MYSQL_STARTED} ] || [ -z "$(is_mysql_running)" ]; then + echo "No need to kill MySQL, it is not running" + # So do nothing + return + fi + + # Force kill MySQL + if [ $SUDO_MYSQL ]; then + echo "Killing Sudo MySQL" + sudo -n killall -s KILL mysqld + else + killall -s KILL mysqld + fi } # Wait for a process or group of processes function anywait() { -for pid in "$@"; do -while kill -0 "$pid" &> /dev/null; do -sleep 0.5 -done -done + for pid in "$@"; do + while kill -0 "$pid" &> /dev/null; do + sleep 0.5 + done + done } function cleanup { -# Kill Apache -if [[ -f ${PID_FILE} ]]; then -echo "Killed process" -# Kill process and all children -/bin/kill -s KILL -$(cat ${PID_FILE}) -fi - -# Kill MySQL -stop_mysql - -# Remove folder on exit -echo "Cleaning up ${FOLDER}" -rm -rf ${FOLDER} + # Kill Apache + if [[ -f ${PID_FILE} ]]; then + echo "Killed process" + # Kill process and all children + kill -KILL -- -$(cat ${PID_FILE}) + fi + + # Kill MySQL + stop_mysql + + # Remove folder on exit + echo "Cleaning up ${FOLDER}" + rm -rf ${FOLDER} + exit } # Cleanup when killed -trap cleanup EXIT INT KILL +trap cleanup EXIT INT KILL TERM # Run MySQL start_mysql @@ -200,5 +212,5 @@ echo "Waiting for Apache2 process : ${PID}" anywait ${PID} echo "Apache is dead (pid=${PID})" -# Cleanup on exit -cleanup + + From 0f22ba7af37aad5967394c195469ed0849a855de Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Sun, 9 Mar 2014 14:14:19 -0700 Subject: [PATCH 109/646] Support cleaning up MySQL in run_apache.sh --- core/cb.project/php/run_apache.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/cb.project/php/run_apache.sh b/core/cb.project/php/run_apache.sh index 3e7522d1..df0ca257 100755 --- a/core/cb.project/php/run_apache.sh +++ b/core/cb.project/php/run_apache.sh @@ -112,6 +112,10 @@ function start_mysql() { if [ -z "$(which mysqld)" ]; then echo "Could not start MySQL because it is not installed on the system's \$PATH" fi + + # Specify that MySQL was started by this script + # And thus we handle it's termination + MYSQL_STARTED=true echo "MySQL RUNNING = $(is_mysql_running)" # Check if MySQL is already running @@ -150,9 +154,10 @@ function start_mysql() { # Stop MySQL started by "start_mysql" function stop_mysql() { echo "Killing MySQL" + # MySQL wasn't started by this script # or is already dead - if [ !${MYSQL_STARTED} ] || [ -z "$(is_mysql_running)" ]; then + if [ ! $MYSQL_STARTED ] || [ -z "$(is_mysql_running)" ]; then echo "No need to kill MySQL, it is not running" # So do nothing return From 4a5eb825ec2dedf46c5d29902a1923495efbf13f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 14:32:46 -0700 Subject: [PATCH 110/646] Fix #281: Allow setting custom JSHint options using .jshintrc --- addons/cb.files.editor/client.js | 91 +------------------ .../{views/file.js => editor.js} | 11 ++- addons/cb.files.editor/jshint.js | 48 ++++++++++ addons/cb.files.editor/settings.js | 88 ++++++++++++++++++ 4 files changed, 150 insertions(+), 88 deletions(-) rename addons/cb.files.editor/{views/file.js => editor.js} (97%) create mode 100644 addons/cb.files.editor/jshint.js create mode 100644 addons/cb.files.editor/settings.js diff --git a/addons/cb.files.editor/client.js b/addons/cb.files.editor/client.js index 1ce56981..2a027ec2 100644 --- a/addons/cb.files.editor/client.js +++ b/addons/cb.files.editor/client.js @@ -1,100 +1,17 @@ define([ "ace", - "views/file", - "codecomplete" -], function(ace, FileEditorView, codecomplete) { + "editor", + "codecomplete", + "settings" +], function(ace, FileEditorView, codecomplete, userSettings) { var $ = codebox.require("hr/dom"); var commands = codebox.require("core/commands/toolbar"); - var settings = codebox.require("core/settings"); var files = codebox.require("core/files"); var languages = codebox.require("utils/languages"); var aceconfig = ace.require("ace/config"); aceconfig.set("basePath", "static/addons/cb.files.editor/ace"); - // Add settings - settings.add({ - 'namespace': "editor", - 'title': "Code Editor", - 'defaults': { - 'theme': "github", - 'fontsize': "12", - 'printmargincolumn': 80, - 'showinvisibles': false, - 'showprintmargin': false, - 'highlightactiveline': false, - 'wraplimitrange': 80, - 'enablesoftwrap': false, - 'enablesofttabs': true, - 'autocollaboration': true, - 'tabsize': 4, - 'keyboard': "textinput" - }, - 'fields': { - 'keyboard': { - 'label': "Keyboard mode", - 'type': "select", - 'options': { - "vim": "Vim", - "emacs": "Emacs", - "textinput": "Default" - } - }, - 'fontsize': { - 'label': "Font Size", - 'type': "number", - 'min': 10, - 'max': 30, - 'step': 1 - }, - 'printmargincolumn': { - 'label': "Print Margin Column", - 'type': "number", - 'min': 0, - 'max': 1000, - 'step': 1 - }, - 'wraplimitrange': { - 'label': "Wrap Limit Range", - 'type': "number", - 'min': 0, - 'max': 1000, - 'step': 1 - }, - 'autocollaboration': { - 'label': "Auto enable realtime collaboration", - 'type': "checkbox" - }, - 'showprintmargin': { - 'label': "Show Print Margin", - 'type': "checkbox" - }, - 'showinvisibles': { - 'label': "Show Invisibles", - 'type': "checkbox" - }, - 'highlightactiveline': { - 'label': "Highlight Active Line", - 'type': "checkbox" - }, - 'enablesoftwrap': { - 'label': "Enable Soft Wrap", - 'type': "checkbox" - }, - 'enablesofttabs': { - 'label': "Use Soft Tabs", - 'type': "checkbox" - }, - 'tabsize': { - 'label': "Tab Size", - 'type': "number", - 'min': 0, - 'max': 1000, - 'step': 1 - } - } - }); - // Build code files extensions list var textExts = _.reduce(languages.LIST, function(list, language) { list = list.concat(language.extensions || []); diff --git a/addons/cb.files.editor/views/file.js b/addons/cb.files.editor/editor.js similarity index 97% rename from addons/cb.files.editor/views/file.js rename to addons/cb.files.editor/editor.js index bdd96ae4..23f1ad02 100644 --- a/addons/cb.files.editor/views/file.js +++ b/addons/cb.files.editor/editor.js @@ -1,9 +1,10 @@ define([ "ace", + "jshint", "theme/textmate", "text!templates/file.html", "less!stylesheets/file.less", -], function(ace, aceDefaultTheme, templateFile) { +], function(ace, jshint, aceDefaultTheme, templateFile) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); @@ -192,6 +193,14 @@ define([ that.sync.updateUserCursor(cursor.column, cursor.row); that.editorStatusCommand.set("title", "Line "+(cursor.row+1)+", Column "+(cursor.column+1)); }); + this.editor.getSession().on("changeMode", function() { + jshint.applySettings(that.editor) + .then(function(state) { + console.log("jshint config", state); + }, function(state) { + console.error("jshint error", state); + }); + }); var $doc = this.editor.session.doc; diff --git a/addons/cb.files.editor/jshint.js b/addons/cb.files.editor/jshint.js new file mode 100644 index 00000000..194bd2fe --- /dev/null +++ b/addons/cb.files.editor/jshint.js @@ -0,0 +1,48 @@ +define([], function() { + var File = codebox.require("models/file"); + var box = codebox.require("core/box"); + var Q = codebox.require("hr/promise"); + var _ = codebox.require("hr/utils"); + + var jshintFile = new File(); + + // Read the JSHint configuration from a file + var readJshintSettings = _.memoize(function() { + // Get file + return jshintFile.getByPath("/.jshintrc") + .then(function() { + //Read file + return jshintFile.read(); + }) + .then(function(content) { + // Parse JSON + return JSON.parse(content); + }); + }); + + // Apply JSHint config from a file to an editor + var applyJshintSettings = function(editor) { + var session = editor.session; + if (session.getMode().$id != "ace/mode/javascript") return Q.reject(new Error("Not a javascript editor")); + if (!session.$worker) return Q.reject(new Error("No JSHint worker")); + + return readJshintSettings() + .then(function(options) { + if (session.$worker) { + //session.$worker.send("changeOptions",[ {undef: true}]) + // or + session.$worker.send("setOptions",[options]) + } + return options; + }); + }; + + // Clear memoize cache when file change + jshintFile.on("file:change", function(e) { + readJshintSettings.cache = {}; + }); + + return { + 'applySettings': applyJshintSettings + }; +}); \ No newline at end of file diff --git a/addons/cb.files.editor/settings.js b/addons/cb.files.editor/settings.js new file mode 100644 index 00000000..1765a6b2 --- /dev/null +++ b/addons/cb.files.editor/settings.js @@ -0,0 +1,88 @@ +define([], function() { + var settings = codebox.require("core/settings"); + + // Add settings + var userSettings = settings.add({ + 'namespace': "editor", + 'title': "Code Editor", + 'defaults': { + 'theme': "github", + 'fontsize': "12", + 'printmargincolumn': 80, + 'showinvisibles': false, + 'showprintmargin': false, + 'highlightactiveline': false, + 'wraplimitrange': 80, + 'enablesoftwrap': false, + 'enablesofttabs': true, + 'autocollaboration': true, + 'tabsize': 4, + 'keyboard': "textinput" + }, + 'fields': { + 'keyboard': { + 'label': "Keyboard mode", + 'type': "select", + 'options': { + "vim": "Vim", + "emacs": "Emacs", + "textinput": "Default" + } + }, + 'fontsize': { + 'label': "Font Size", + 'type': "number", + 'min': 10, + 'max': 30, + 'step': 1 + }, + 'printmargincolumn': { + 'label': "Print Margin Column", + 'type': "number", + 'min': 0, + 'max': 1000, + 'step': 1 + }, + 'wraplimitrange': { + 'label': "Wrap Limit Range", + 'type': "number", + 'min': 0, + 'max': 1000, + 'step': 1 + }, + 'autocollaboration': { + 'label': "Auto enable realtime collaboration", + 'type': "checkbox" + }, + 'showprintmargin': { + 'label': "Show Print Margin", + 'type': "checkbox" + }, + 'showinvisibles': { + 'label': "Show Invisibles", + 'type': "checkbox" + }, + 'highlightactiveline': { + 'label': "Highlight Active Line", + 'type': "checkbox" + }, + 'enablesoftwrap': { + 'label': "Enable Soft Wrap", + 'type': "checkbox" + }, + 'enablesofttabs': { + 'label': "Use Soft Tabs", + 'type': "checkbox" + }, + 'tabsize': { + 'label': "Tab Size", + 'type': "number", + 'min': 0, + 'max': 1000, + 'step': 1 + } + } + }); + + return userSettings; +}); \ No newline at end of file From c540244a4393b734a27eb1ce4734b3931ec4e073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 14:35:25 -0700 Subject: [PATCH 111/646] Remove grayscale on desktop manager icons --- desktop/css/style.css | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/css/style.css b/desktop/css/style.css index e330666b..4c13b397 100644 --- a/desktop/css/style.css +++ b/desktop/css/style.css @@ -77,7 +77,6 @@ body { float: left; margin: 9px 10px 0px 2px; width: 32px; - -webkit-filter: grayscale(1); } #projects hr { margin: 20px 10px; From 1d8f100077788d7115bc0d2741c6c689e88e7a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 14:41:11 -0700 Subject: [PATCH 112/646] Remove install of dependencies at codebox startup --- core/cb.addons/main.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/cb.addons/main.js b/core/cb.addons/main.js index 51769834..b6b7e9c6 100644 --- a/core/cb.addons/main.js +++ b/core/cb.addons/main.js @@ -201,12 +201,6 @@ function setup(options, imports, register, app) { unlinkInvalid: true }); }) - .then(manager.runAddonsOperation(function(addon) { - // Install node dependencies - return addon.installDependencies(); - }, { - failOnError: false - })) .then(manager.runAddonsOperation(function(addon) { // Build non optimized addons return addon.optimizeClient(); From f99e9f5f322edc8b5788aa3b578f8bd372cbed94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 14:56:56 -0700 Subject: [PATCH 113/646] Improve path lisibility in desktop dashboard --- desktop/css/style.css | 5 +++++ desktop/js/main.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/desktop/css/style.css b/desktop/css/style.css index 4c13b397..edc10d0e 100644 --- a/desktop/css/style.css +++ b/desktop/css/style.css @@ -78,6 +78,11 @@ body { margin: 9px 10px 0px 2px; width: 32px; } +#projects .project .project-path { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} #projects hr { margin: 20px 10px; border: 0px; diff --git a/desktop/js/main.js b/desktop/js/main.js index a0c1d775..8846b74d 100644 --- a/desktop/js/main.js +++ b/desktop/js/main.js @@ -116,7 +116,7 @@ var updateProjects = function() { if (!fs.existsSync(path)) { projects = _.without(projects, path); } - addProjectItem(path.split("/").pop(), path, "icons/folder.png", function() { + addProjectItem(path.split("/").pop(), path.replace(process.env.HOME || "", "~"), "icons/folder.png", function() { runLocalCodebox(path); }); }); From 01b88b94185b638f383a3dbb798e11bd685e6c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 15:05:12 -0700 Subject: [PATCH 114/646] Bump version to 0.7.0 --- CHANGES | 14 ++++++++++++++ package.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index a151379b..d0ceb0b6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,17 @@ +2014-03-09 Samy Pessé + Aaron O'Mullan + + * Bump version to 0.7.0 + * New tabs system: grid system, resize, reorder + * New statusbar + * New command palette (test it using Command+Shift+P or Ctrl+Shift+P) + * Stop state for run button + * Apache runner is now working (with MySQL) + * Addons need an engine field + * Django support (auto-run) + * Performance improved (faster boot, ...) + * Editor uses .jshintrc file for JSHing worker configuration + 2014-02-25 Samy Pessé Aaron O'Mullan diff --git a/package.json b/package.json index 03280f0b..7f15f9c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codebox", - "version": "0.6.2", + "version": "0.7.0", "description": "Open source cloud & desktop IDE.", "homepage": "https://www.codebox.io", "author": "FriendCode Inc. ", From dc913c0ddef93949f3bc65d58452cd72b85b342d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 20:37:16 -0700 Subject: [PATCH 115/646] Fix addons installation --- core/cb.addons/main.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/cb.addons/main.js b/core/cb.addons/main.js index b6b7e9c6..aa4daccf 100644 --- a/core/cb.addons/main.js +++ b/core/cb.addons/main.js @@ -147,9 +147,10 @@ function setup(options, imports, register, app) { // Copy to addons dir return addon.transfer(configAddonsPath); }) - .fin(function(newAddon) { + .then(function(newAddon) { addon = newAddon; - + }) + .fin(function() { // Remove temporary dir return Q.nfcall(wrench.rmdirRecursive, tempDir, false); }) From 2a5304302a94e35ea2e8c14ee15998c2fdc14f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 9 Mar 2014 20:46:21 -0700 Subject: [PATCH 116/646] Fix addons installation and uninstallation command --- client/core/addons.js | 27 +++++++++++++++++++++++++-- client/core/search/addons.js | 4 ++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/client/core/addons.js b/client/core/addons.js index 91585d66..0a4c45df 100644 --- a/client/core/addons.js +++ b/client/core/addons.js @@ -22,9 +22,9 @@ define([ if (url) return url; return dialogs.prompt("Install a new addon", "GIT url for the addon:", ""); }) - .then(function(url) { + .then(function(_url) { return operations.start("addon.install", function(op) { - return addons.install(url); + return addons.install(_url); }, { title: "Installing add-on" }); @@ -32,5 +32,28 @@ define([ } }); + // Command to uninstall from a name + Command.register("addons.uninstall", { + category: "Add-ons", + title: "Uninstall", + description: "Uninstall with name", + offline: false, + search: false, + action: function(name) { + return Q() + .then(function() { + if (name) return name; + return dialogs.prompt("Uninstall an addon", "Name of the addon:", ""); + }) + .then(function(_name) { + return operations.start("addon.uninstall", function(op) { + return addons.uninstall(_name); + }, { + title: "Uinstalling add-on" + }); + }) + } + }); + return addons; }); \ No newline at end of file diff --git a/client/core/search/addons.js b/client/core/search/addons.js index 43e0b864..cfba069f 100644 --- a/client/core/search/addons.js +++ b/client/core/search/addons.js @@ -55,7 +55,7 @@ define([ }, 'offline': false, 'action': _.bind(function() { - return addons.install(addon.get("git")) + return Command.run("addons.install", addon.get("git")); }, this) }; }; @@ -71,7 +71,7 @@ define([ }, 'offline': false, 'action': _.bind(function() { - return Command.run("addon.install", addon.get("name")); + return Command.run("addons.uninstall", addon.get("name")); }, this) }; }; From b9dfeba051c44507e7f240ef5e6d3ed421b53a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 10:09:55 -0700 Subject: [PATCH 117/646] Add base for debugegr as a files handler --- addons/cb.debug/client.js | 38 ++++++++++++++++++++++++++++++++ addons/cb.debug/package.json | 19 ++++++++++++++++ addons/cb.debug/settings.js | 19 ++++++++++++++++ addons/cb.files.editor/client.js | 30 ++++++------------------- addons/cb.files.editor/editor.js | 7 +----- client/core/files.js | 17 +++++++++----- 6 files changed, 96 insertions(+), 34 deletions(-) create mode 100644 addons/cb.debug/client.js create mode 100644 addons/cb.debug/package.json create mode 100644 addons/cb.debug/settings.js diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js new file mode 100644 index 00000000..961833ab --- /dev/null +++ b/addons/cb.debug/client.js @@ -0,0 +1,38 @@ +define([ + "settings" +], function(settings) { + var toolbar = codebox.require("core/commands/toolbar"); + var dialogs = codebox.require("utils/dialogs"); + var files = codebox.require("core/files"); + + // Add files handler + files.addHandler("debug", { + name: "Debug", + valid: function(file) { + return (!file.isDirectory()); + }, + open: function(file) { + console.log("debug", file); + } + }); + + // Debugging command + toolbar.register("debug.open", { + category: "Debug", + title: "Debugger", + description: "Open Debugger", + icons: { + 'default': "bug", + }, + offline: false, + shortcuts: [ + "alt+d" + ] + }, function(path) { + path = path || settings.user.get("path"); + + if (!path) { + dialogs.alert("No file specified", "Click left on a file and select 'Debug' to open it with debugger, it'll be saved in your settings as last debugged file."); + } + }); +}); \ No newline at end of file diff --git a/addons/cb.debug/package.json b/addons/cb.debug/package.json new file mode 100644 index 00000000..040b64d1 --- /dev/null +++ b/addons/cb.debug/package.json @@ -0,0 +1,19 @@ +{ + "name": "cb.debug", + "version": "0.1.0", + "title": "Debug", + "description": "Debugger integration inside codebox", + "homepage": "https://github.com/FriendCode/codebox", + "license": "Apache", + "author": { + "name": "Codebox", + "email": "contact@friendco.de", + "url": "https://www.codebox.io" + }, + "client": { + "main": "client" + }, + "engines": { + "codebox": ">=0.7.0" + } +} \ No newline at end of file diff --git a/addons/cb.debug/settings.js b/addons/cb.debug/settings.js new file mode 100644 index 00000000..ceb8a0a2 --- /dev/null +++ b/addons/cb.debug/settings.js @@ -0,0 +1,19 @@ +define([], function() { + var settings = codebox.require("core/settings"); + + // Add settings + return settings.add({ + 'namespace': "debug", + 'title': "Debug", + 'defaults': { + 'path': "" + }, + 'fields': { + 'path': { + 'label': 'File Path', + 'type': "text", + 'help': "Click left on a file and select 'debug' to update this file." + } + } + }); +}); \ No newline at end of file diff --git a/addons/cb.files.editor/client.js b/addons/cb.files.editor/client.js index 2a027ec2..94881cd2 100644 --- a/addons/cb.files.editor/client.js +++ b/addons/cb.files.editor/client.js @@ -7,35 +7,19 @@ define([ var $ = codebox.require("hr/dom"); var commands = codebox.require("core/commands/toolbar"); var files = codebox.require("core/files"); - var languages = codebox.require("utils/languages"); var aceconfig = ace.require("ace/config"); aceconfig.set("basePath", "static/addons/cb.files.editor/ace"); - // Build code files extensions list - var textExts = _.reduce(languages.LIST, function(list, language) { - list = list.concat(language.extensions || []); - if (language.primary_extension) list.push(language.primary_extension); - return list; - }, [ - // Defaults extensions - '.txt' - ]); - - // List of mime type - var mimeTypes = [ - "text/plain" - ]; - // Add files handler files.addHandler("ace", { - name: "Code Editor", - View: FileEditorView, - fallback: true, - setActive: true, - valid: function(file) { - return (!file.isDirectory() - && (_.contains(textExts, file.extension()) || _.contains(mimeTypes, file.get("mime")))); + 'name': "Code Editor", + 'fallback': true, + 'setActive': true, + 'position': 1, + 'View': FileEditorView, + 'valid': function(file) { + return (!file.isDirectory()); } }); diff --git a/addons/cb.files.editor/editor.js b/addons/cb.files.editor/editor.js index 23f1ad02..243a1e7a 100644 --- a/addons/cb.files.editor/editor.js +++ b/addons/cb.files.editor/editor.js @@ -194,12 +194,7 @@ define([ that.editorStatusCommand.set("title", "Line "+(cursor.row+1)+", Column "+(cursor.column+1)); }); this.editor.getSession().on("changeMode", function() { - jshint.applySettings(that.editor) - .then(function(state) { - console.log("jshint config", state); - }, function(state) { - console.error("jshint error", state); - }); + jshint.applySettings(that.editor); }); var $doc = this.editor.session.doc; diff --git a/client/core/files.js b/client/core/files.js index d4614c7d..14e86533 100644 --- a/client/core/files.js +++ b/client/core/files.js @@ -64,7 +64,9 @@ define([ } handler = _.defaults(handler, { - 'setActive': false + 'setActive': false, + 'fallback': false, + 'position': 10 }); handler.id = handlerId; @@ -110,10 +112,15 @@ define([ }; // Get handler for a file - var getHandlers = function(file, defaultHandler) { - return _.filter(handlers, function(handler) { + var getHandlers = function(file) { + return _.chain(handlers) + .filter(function(handler) { return userSettings.get(handler.id, true) && handler.valid(file); - }); + }) + .sort(function(handler) { + return handler['position']; + }) + .value(); }; // get fallback handlers for a file @@ -177,7 +184,7 @@ define([ if (_.size(possibleHandlers) == 0) { return openFileWith(file); } - + if (_.size(possibleHandlers) == 1 || (options.userChoice != true)) { return Q(openFileHandler(_.first(possibleHandlers), file)); } From b317ecc4c37e1ff085c73d7893c74fa487810360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 10:24:45 -0700 Subject: [PATCH 118/646] Save last debugged file --- addons/cb.debug/client.js | 17 +++++++++++++++-- client/core/files.js | 8 +++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index 961833ab..b6657e06 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -1,18 +1,24 @@ define([ "settings" ], function(settings) { + var File = codebox.require("models/file"); var toolbar = codebox.require("core/commands/toolbar"); var dialogs = codebox.require("utils/dialogs"); var files = codebox.require("core/files"); // Add files handler - files.addHandler("debug", { + var filesHandler = files.addHandler("debug", { name: "Debug", valid: function(file) { return (!file.isDirectory()); }, open: function(file) { - console.log("debug", file); + if (file.path() != settings.user.get("path")) { + settings.user.set("path", file.path()); + settings.user.save(); + } + + alert("debug "+file.path()); } }); @@ -33,6 +39,13 @@ define([ if (!path) { dialogs.alert("No file specified", "Click left on a file and select 'Debug' to open it with debugger, it'll be saved in your settings as last debugged file."); + return; } + + var f = new File(); + f.getByPath(path) + .then(function() { + filesHandler.open(f); + }); }); }); \ No newline at end of file diff --git a/client/core/files.js b/client/core/files.js index 14e86533..02c38480 100644 --- a/client/core/files.js +++ b/client/core/files.js @@ -109,6 +109,8 @@ define([ // Register handler handlers[handlerId] = handler; + + return handlers[handlerId]; }; // Get handler for a file @@ -169,13 +171,13 @@ define([ 'codebox': box }); return nfile.getByPath(file).then(function() { - return openFile(nfile); + return openFile(nfile, options); }); } var possibleHandlers = getHandlers(file); - // get fallbacks + // Get fallbacks if (_.size(possibleHandlers) == 0 && options.useFallback) { possibleHandlers = getFallbacks(); } @@ -184,7 +186,7 @@ define([ if (_.size(possibleHandlers) == 0) { return openFileWith(file); } - + if (_.size(possibleHandlers) == 1 || (options.userChoice != true)) { return Q(openFileHandler(_.first(possibleHandlers), file)); } From 46a36f89b9480066cd7bcecb17ac349b56e56474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 10:51:07 -0700 Subject: [PATCH 119/646] Show handlers in file context menu --- addons/cb.debug/client.js | 1 + addons/cb.files.editor/client.js | 2 +- client/models/file.js | 33 ++++++++++++++------------------ 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index b6657e06..0141bece 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -8,6 +8,7 @@ define([ // Add files handler var filesHandler = files.addHandler("debug", { + icon: "bug", name: "Debug", valid: function(file) { return (!file.isDirectory()); diff --git a/addons/cb.files.editor/client.js b/addons/cb.files.editor/client.js index 94881cd2..b6549b9c 100644 --- a/addons/cb.files.editor/client.js +++ b/addons/cb.files.editor/client.js @@ -13,7 +13,7 @@ define([ // Add files handler files.addHandler("ace", { - 'name': "Code Editor", + 'name': "Edit", 'fallback': true, 'setActive': true, 'position': 1, diff --git a/client/models/file.js b/client/models/file.js index 7c133b5a..266d5dec 100644 --- a/client/models/file.js +++ b/client/models/file.js @@ -647,20 +647,25 @@ define([ // Return context menu contextMenu: function() { var that = this; + var files = require("core/files"); + return function() { var menu = []; // Open with if (!that.isDirectory()) { - menu.push({ - 'id': "file.open.select", - 'type': "action", - 'title': "Open with...", - 'action': function() { - that.open({ - 'userChoice': true - }); - } + var handlers = files.getHandlers(that); + _.each(handlers, function(handler) { + menu.push({ + 'type': "action", + 'title': handler.name, + 'icons': { + 'menu': handler.icon + }, + 'action': function() { + return handler.open(that); + } + }); }); menu.push({ 'type': "divider" }); } @@ -668,7 +673,6 @@ define([ // File or directory if (!that.isRoot()) { menu.push({ - 'id': "file.rename", 'type': "action", 'title': "Rename...", 'action': function() { @@ -676,7 +680,6 @@ define([ } }); menu.push({ - 'id': "file.remove", 'type': "action", 'title': "Remove", 'action': function() { @@ -689,7 +692,6 @@ define([ if (that.isDirectory()) { // Directory menu.push({ - 'id': "file.create", 'type': "action", 'title': "New file", 'action': function() { @@ -697,7 +699,6 @@ define([ } }); menu.push({ - 'id': "file.mkdir", 'type': "action", 'title': "New folder", 'action': function() { @@ -705,7 +706,6 @@ define([ } }); menu.push({ - 'id': "file.refresh", 'type': "action", 'title': "Refresh", 'action': function() { @@ -713,13 +713,11 @@ define([ } }); menu.push({ - 'id': "file.upload", 'type': "menu", 'title': "Add", 'offline': false, 'menu': [ { - 'id': "file.upload.files", 'type': "action", 'title': "Files", 'offline': false, @@ -728,7 +726,6 @@ define([ } }, { - 'id': "file.upload.directory", 'type': "action", 'title': "Directories", 'offline': false, @@ -742,7 +739,6 @@ define([ }); } else { menu.push({ - 'id': "file.download", 'type': "action", 'title': "Download", 'action': function() { @@ -751,7 +747,6 @@ define([ }); menu.push({ 'type': "divider" }); menu.push({ - 'id': "file.run", 'type': "action", 'title': "Run", 'offline': false, From b656299518344306a316747c9b60f3f8fb8d3b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 11:03:17 -0700 Subject: [PATCH 120/646] Fix order for files handlers --- client/core/files.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/core/files.js b/client/core/files.js index 02c38480..98cf4c5e 100644 --- a/client/core/files.js +++ b/client/core/files.js @@ -119,8 +119,8 @@ define([ .filter(function(handler) { return userSettings.get(handler.id, true) && handler.valid(file); }) - .sort(function(handler) { - return handler['position']; + .sortBy(function(handler) { + return handler.position || 10; }) .value(); }; From f25685159d86a440700f59ad773f34324ee44ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 11:38:22 -0700 Subject: [PATCH 121/646] Add base grid view in debugger --- addons/cb.debug/client.js | 20 ++++++++-- addons/cb.debug/stylesheets/tab.less | 7 ++++ addons/cb.debug/views/tab.js | 58 ++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 addons/cb.debug/stylesheets/tab.less create mode 100644 addons/cb.debug/views/tab.js diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index 0141bece..482365ba 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -1,10 +1,12 @@ define([ + "views/tab", "settings" -], function(settings) { +], function(DebugTab, settings) { var File = codebox.require("models/file"); var toolbar = codebox.require("core/commands/toolbar"); var dialogs = codebox.require("utils/dialogs"); var files = codebox.require("core/files"); + var tabs = codebox.require("core/tabs"); // Add files handler var filesHandler = files.addHandler("debug", { @@ -19,7 +21,16 @@ define([ settings.user.save(); } - alert("debug "+file.path()); + // Create trminal tab + var tab = tabs.add(DebugTab, { + + }, { + 'type': "debug", + 'section': "debug" + }); + + // Return the tab + return tab; } }); @@ -44,9 +55,10 @@ define([ } var f = new File(); - f.getByPath(path) + + return f.getByPath(path) .then(function() { - filesHandler.open(f); + return filesHandler.open(f); }); }); }); \ No newline at end of file diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less new file mode 100644 index 00000000..c00c4521 --- /dev/null +++ b/addons/cb.debug/stylesheets/tab.less @@ -0,0 +1,7 @@ +.addon-debugger-tab { + .component-grid .grid-section { + .grid-resize-bar-h { + background: rgba(0,0,0, 0.1); + } + } +} \ No newline at end of file diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js new file mode 100644 index 00000000..3f3921cc --- /dev/null +++ b/addons/cb.debug/views/tab.js @@ -0,0 +1,58 @@ +define([ + "less!stylesheets/tab.less" +], function() { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var hr = codebox.require("hr/hr"); + var Tab = codebox.require("views/tabs/base"); + var box = codebox.require("core/box"); + var user = codebox.require("core/user"); + + var GridView = codebox.require("views/grid"); + + var DebugTab = Tab.extend({ + className: Tab.prototype.className+ " addon-debugger-tab", + defaults: { + + }, + menuTitle: "Debugger", + events: { + + }, + + initialize: function(options) { + var that = this; + DebugTab.__super__.initialize.apply(this, arguments); + + // Create grid for sections + this.grid = new GridView({ + columns: 1000 + }); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); + this.grid.addView(new hr.View()); + this.grid.appendTo(this); + + // Describe debug tab + this.setTabTitle("Debugger"); + this.menu.menuSection([ + { + 'type': "checkbox", + 'title': "Exit", + 'action': function(state) { + that.closeTab(); + } + } + ]); + + return this; + }, + + // Render + render: function() { + return this.ready(); + } + }); + + return DebugTab; +}); From d1c5c694547dd26e4f97374a87ef1bf18c53a875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 11:49:18 -0700 Subject: [PATCH 122/646] Improve stability of grid view resize --- client/views/grid.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/views/grid.js b/client/views/grid.js index c45fa548..8a93a873 100644 --- a/client/views/grid.js +++ b/client/views/grid.js @@ -111,15 +111,15 @@ define([ dw = (lineW/(layout.columns - x)); w = view._gridOptions.width || dw - w = w.toFixed(3); + w = w.toFixed(4); // Container object $section = $("
      ", { 'class': 'grid-section', 'css': { - 'left': (100 - lineW)+"%", + 'left': Math.ceil(100 - lineW)+"%", 'top': (y * sectionHeight)+"%", - 'width': w+"%", + 'width': Math.floor(w)+"%", 'height': sectionHeight+"%" } }); From cc96a96f5ea233ee51cca78697b79ed7355e87d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 11:59:41 -0700 Subject: [PATCH 123/646] Add base command for debuggers --- addons/cb.debug/views/tab.js | 42 +++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index 3f3921cc..ba38fec4 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -4,6 +4,7 @@ define([ var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); + var Command = codebox.require("models/command"); var Tab = codebox.require("views/tabs/base"); var box = codebox.require("core/box"); var user = codebox.require("core/user"); @@ -32,10 +33,40 @@ define([ this.grid.addView(new hr.View()); this.grid.addView(new hr.View()); this.grid.appendTo(this); + + + // Base commands: start, stop, next, continue, restart + this.commandStart = new Command({}, { + title: "Start" + }); + this.commandStop = new Command({}, { + title: "Stop" + }); + this.commandNext = new Command({}, { + title: "Next" + }); + this.commandContinue = new Command({}, { + title: "Continue" + }); + this.commandRestart = new Command({}, { + title: "Restart" + }); // Describe debug tab this.setTabTitle("Debugger"); - this.menu.menuSection([ + + // Tab menu + this.menu + .menuSection([ + this.commandStart, + this.commandStop, + this.commandRestart + ]) + .menuSection([ + this.commandNext, + this.commandContinue + ]) + .menuSection([ { 'type': "checkbox", 'title': "Exit", @@ -45,6 +76,15 @@ define([ } ]); + // Statusbar menu + this.statusbar.add([ + this.commandStart, + this.commandStop, + this.commandNext, + this.commandContinue, + this.commandRestart + ]); + return this; }, From a426d722ffd6afc5edb274fa0f850a009af44df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 12:17:28 -0700 Subject: [PATCH 124/646] Fix tabs restoration --- client/views/tabs/manager.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/client/views/tabs/manager.js b/client/views/tabs/manager.js index 10e223e8..83a0c5c7 100644 --- a/client/views/tabs/manager.js +++ b/client/views/tabs/manager.js @@ -55,10 +55,13 @@ define([ var that = this; TabsView.__super__.initialize.apply(this, arguments); - // Current actiev tab id + // Current active tab id this.activeTab = null; this.activeSection = 0; + // Has been restored + this._restored = false; + // Current layout this.layout = this.options.layout; // null: mode auto this.grid = new GridView({}, this); @@ -217,7 +220,7 @@ define([ this.grid.setLayout(l); this.trigger("layout", l); - this.update(); + this.saveTabs(); }, // Check if tab is the active tab @@ -272,13 +275,15 @@ define([ // Save tabs saveTabs: function() { + if (!this._restored) return; + var state = {}; // Snapshot sections and tabs state.sections = _.map(this.grid.views, function(section) { return { 'id': section.sectionId, - 'tabs':section.tabs.map(function(tab) { + 'tabs': section.tabs.map(function(tab) { return tab.snapshot(); }) }; @@ -286,7 +291,6 @@ define([ // Snapshot layout state.layout = this.grid.columns; - hr.Storage.set("tabs", state); }, @@ -335,6 +339,7 @@ define([ .value() ) .then(function() { + that._restored = true; that.checkSections(); return n; }); From a3b45294b3406802b6ed337c56010dab72be8198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 12:38:32 -0700 Subject: [PATCH 125/646] Add base section for debugger: breakpoint, locals, stack --- addons/cb.debug/client.js | 2 +- addons/cb.debug/stylesheets/tab.less | 11 ++++++++++ addons/cb.debug/views/section.js | 30 ++++++++++++++++++++++++++++ addons/cb.debug/views/tab.js | 19 +++++++++++++----- addons/cb.theme.dark/main.js | 5 +++-- 5 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 addons/cb.debug/views/section.js diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index 482365ba..714eb141 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -23,7 +23,7 @@ define([ // Create trminal tab var tab = tabs.add(DebugTab, { - + 'path': file.path() }, { 'type': "debug", 'section': "debug" diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less index c00c4521..fc519b91 100644 --- a/addons/cb.debug/stylesheets/tab.less +++ b/addons/cb.debug/stylesheets/tab.less @@ -4,4 +4,15 @@ background: rgba(0,0,0, 0.1); } } + + .debug-section { + h4 { + display: block; + width: 100%; + background: rgba(0,0,0,.05); + font-size: 14px; + padding: 6px 8px; + margin: 0px; + } + } } \ No newline at end of file diff --git a/addons/cb.debug/views/section.js b/addons/cb.debug/views/section.js new file mode 100644 index 00000000..c086dc6e --- /dev/null +++ b/addons/cb.debug/views/section.js @@ -0,0 +1,30 @@ +define([], function() { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var hr = codebox.require("hr/hr"); + + var DebugSection = hr.View.extend({ + className: "debug-section", + defaults: { + title: "" + }, + events: { + + }, + + initialize: function(options) { + DebugSection.__super__.initialize.apply(this, arguments); + this.$el.html("

      "+this.options.title+"

      ") + + return this; + }, + + // Render + render: function() { + + return this.ready(); + } + }); + + return DebugSection; +}); diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index ba38fec4..4d040905 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -1,6 +1,7 @@ define([ + "views/section", "less!stylesheets/tab.less" -], function() { +], function(DebugSection) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); @@ -29,9 +30,17 @@ define([ this.grid = new GridView({ columns: 1000 }); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); - this.grid.addView(new hr.View()); + + this.grid.addView(new DebugSection({ + title: "Breackpoints" + })); + this.grid.addView(new DebugSection({ + title: "Stack" + })); + this.grid.addView(new DebugSection({ + title: "Locals" + })); + this.grid.appendTo(this); @@ -53,7 +62,7 @@ define([ }); // Describe debug tab - this.setTabTitle("Debugger"); + this.setTabTitle("Debugger "+this.options.path); // Tab menu this.menu diff --git a/addons/cb.theme.dark/main.js b/addons/cb.theme.dark/main.js index c369ff8a..a8e988b9 100644 --- a/addons/cb.theme.dark/main.js +++ b/addons/cb.theme.dark/main.js @@ -60,7 +60,8 @@ define([ // Body body: { - 'background': bgDark + 'background': bgDark, + 'color': colorDark }, // Tabs @@ -73,7 +74,7 @@ define([ 'color': colorDark }, content: { - 'background': bgLight, + 'background': bgLight }, tab: { '&.active': { From 06d139c6a198a9fef8f7487dccf28c99baa20524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 12:38:47 -0700 Subject: [PATCH 126/646] Fix title typo in debugger --- addons/cb.debug/views/tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index 4d040905..b01c5918 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -32,7 +32,7 @@ define([ }); this.grid.addView(new DebugSection({ - title: "Breackpoints" + title: "Breakpoints" })); this.grid.addView(new DebugSection({ title: "Stack" From 438ee048953350c630cff8ff447f5a4ff3a32212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 16:44:11 -0700 Subject: [PATCH 127/646] Improve stability of grid resize --- client/views/grid.js | 48 ++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/client/views/grid.js b/client/views/grid.js index 8a93a873..b1e96a1c 100644 --- a/client/views/grid.js +++ b/client/views/grid.js @@ -117,9 +117,9 @@ define([ $section = $("
      ", { 'class': 'grid-section', 'css': { - 'left': Math.ceil(100 - lineW)+"%", + 'left': (100 - lineW)+"%", 'top': (y * sectionHeight)+"%", - 'width': Math.floor(w)+"%", + 'width': w+"%", 'height': sectionHeight+"%" } }); @@ -209,8 +209,8 @@ define([ return this.$("> .grid-section").filter(function() { var r = false; - if ((sx != null && sx == x) - || (sy != null && sy == y)) { + if ((sx !== null && sx == x) + || (sy !== null && sy == y)) { r = true; } @@ -228,40 +228,46 @@ define([ _resize: function(type, i, d) { var getSection = _.bind(_.partialRight(this.getSection, null), this); var pixelToPercent = _.bind(_.partialRight(this.pixelToPercent, null), this); - var size = function(el, s) { - if (!s) return el.width(); - return el.width(s); - }; 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 = function(el, s) { - if (!s) return el.height(); - return el.height(s); - } + size = "height"; } + // Convert update to percent + d = pixelToPercent(d); + var $sections = getSection(i); var $sectionsAfter = getSection(i+1); // New size for next sections - var sAfterN = pixelToPercent(size($sectionsAfter)-d); + // 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 = pixelToPercent(size($sections)+d); + 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], [pixelToPercent($sectionsAfter.position()[position]+d)+"%"])); - size($sectionsAfter, sAfterN+"%"); + $sectionsAfter.css(_.object( + [position, size], + [ + (this.strToPercent($sectionsAfter.get(0).style[position])+d).toFixed(2)+"%", + sAfterN.toFixed(2)+"%" + ] + )); // Resize current line - size($sections, sCurrentN+"%"); + $sections.css(_.object( + [size], + [sCurrentN.toFixed(2)+"%"] + )); this.signalLayout(); @@ -277,8 +283,12 @@ define([ }, pixelToPercent: function(x, y) { - if (x != null) return ((x*100) / this.$el.width()); - if (y != null) return ((y*100) / this.$el.height()); + 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("%", "")) } }); From 1dce1721244b41b1ce13c253b8281cd07fe7a2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 18:56:26 -0700 Subject: [PATCH 128/646] Stop using wireFriendly in rpc (because of filtering of keys) --- core/cb.rpc/manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/cb.rpc/manager.js b/core/cb.rpc/manager.js index c347a93c..306c7d11 100644 --- a/core/cb.rpc/manager.js +++ b/core/cb.rpc/manager.js @@ -47,7 +47,7 @@ HttpRPCManager.prototype.methodHandler = function(method, methodUrl, opts) { }).then(function(data) { res.send(200, { 'ok': true, - 'data': wireFriendly(data), + 'data': data, 'method': methodUrl, }); }, function(err) { From eef4f2986e0ce1338a1fc2afd98a7280fc24c8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 18:59:30 -0700 Subject: [PATCH 129/646] Add base rpc service for debugger using bugs.js --- addons/cb.debug/node/main.js | 23 ++++++++++ addons/cb.debug/node/service.js | 81 +++++++++++++++++++++++++++++++++ addons/cb.debug/package.json | 11 ++++- package.json | 3 +- 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 addons/cb.debug/node/main.js create mode 100644 addons/cb.debug/node/service.js diff --git a/addons/cb.debug/node/main.js b/addons/cb.debug/node/main.js new file mode 100644 index 00000000..3e5187f4 --- /dev/null +++ b/addons/cb.debug/node/main.js @@ -0,0 +1,23 @@ +// Requires +var _ = require('lodash'); +var DebugRPCService = require('./service').DebugRPCService; + + +function setup(options, imports, register) { + // Import + var rpc = imports.rpc; + var events = imports.events; + var workspace = imports.workspace; + + // Service + var service = new DebugRPCService(workspace, events); + + // Register RPC + rpc.register('debug', service); + + // Register + register(null, {}); +} + +// Exports +module.exports = setup; diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js new file mode 100644 index 00000000..d34258ae --- /dev/null +++ b/addons/cb.debug/node/service.js @@ -0,0 +1,81 @@ +// Requires +var Q = require('q'); +var _ = require('lodash'); +var path = require('path'); +var bugs = require('bugs'); + +var debuggers = [ + "pdb", "gdb", "jdb", "rdb" +]; + +function DebugRPCService(workspace, events) { + this.workspace = workspace; + this.events = events; + + this.dbg = null; + + _.bindAll(this); +} + +// Prepare the debugger +DebugRPCService.prototype.init = function(args, meta) { + if (!args.tool || !args.path) throw "Need 'tool' and 'path' arguments"; + if (!_.contains(debuggers, args.tool)) throw "Invalid debugger"; + + // Create debugger interface + this.dbg = bugs[args.tool]( + path.join(this.workspace.root, args.path) + ); + + // Signal it + this.events.emit('debug.init', { + userId: meta.user.userId + }); + + return this.dbg.init() + .then(function() { + return {}; + }); +}; + +// Add breakpoint +DebugRPCService.prototype.breakpoint_add = function(args, meta) { + if (!args.line || !args.path) throw "Need 'line' and 'path' arguments"; + if (!this.dbg) throw "No active debugger"; + + return this.dbg.break(path.join(this.workspace.root, args.path), args.line); +}; + + +// Remove breakpoint +DebugRPCService.prototype.breakpoint_delete = function(args, meta) { + if (!args.id) throw "Need 'id' argument"; + if (!this.dbg) throw "No active debugger"; + + return this.dbg.delete(args.id); +}; + +// Get locals +DebugRPCService.prototype.locals = function(args, meta) { + if (!this.dbg) throw "No active debugger"; + + return this.dbg.locals(); +}; + +// Get breakpoints +DebugRPCService.prototype.breakpoints = function(args, meta) { + if (!this.dbg) throw "No active debugger"; + + return this.dbg.breakpoints(); +}; + +// Get backtrace +DebugRPCService.prototype.backtrace = function(args, meta) { + if (!this.dbg) throw "No active debugger"; + + return this.dbg.backtrace(); +}; + + +// Exports +exports.DebugRPCService = DebugRPCService; diff --git a/addons/cb.debug/package.json b/addons/cb.debug/package.json index 040b64d1..6eb73488 100644 --- a/addons/cb.debug/package.json +++ b/addons/cb.debug/package.json @@ -15,5 +15,14 @@ }, "engines": { "codebox": ">=0.7.0" - } + }, + "plugin": { + "provides": [], + "consumes": [ + "rpc", + "events", + "workspace" + ] + }, + "main": "node/main" } \ No newline at end of file diff --git a/package.json b/package.json index 7f15f9c2..f966245a 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "ctags": "0.8.0", "connect-gzip-static": "0.1.1", "minimatch": "0.2.14", - "semver": "2.2.1" + "semver": "2.2.1", + "bugs": "0.0.1" }, "devDependencies": { "codebox-io": "0.3.2", From 14c7a5b4793298e69640914d400686e7fa4ab6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 19:08:10 -0700 Subject: [PATCH 130/646] Remove completly wireFriendly --- core/cb.events.log/main.js | 3 +-- core/cb.events.socketio/main.js | 4 +--- core/cb.events.webhook/main.js | 4 +--- core/cb.rpc/manager.js | 2 -- core/utils.js | 26 -------------------------- 5 files changed, 3 insertions(+), 36 deletions(-) diff --git a/core/cb.events.log/main.js b/core/cb.events.log/main.js index 6f335f19..abc8c5f0 100644 --- a/core/cb.events.log/main.js +++ b/core/cb.events.log/main.js @@ -1,5 +1,4 @@ // Requires -var wireFriendly = require('../utils').wireFriendly; var timestamp = require('../utils').timestamp; function setup(options, imports, register) { @@ -14,7 +13,7 @@ function setup(options, imports, register) { workspace.mtime = timestamp(); // Log events to console - logger.log(this.event, ':', wireFriendly(data)); + logger.log(this.event, ':', data); }); // Register diff --git a/core/cb.events.socketio/main.js b/core/cb.events.socketio/main.js index fd92898c..cac064d8 100644 --- a/core/cb.events.socketio/main.js +++ b/core/cb.events.socketio/main.js @@ -1,5 +1,3 @@ -// Requires -var wireFriendly = require('../utils').wireFriendly; function setup(options, imports, register) { @@ -13,7 +11,7 @@ function setup(options, imports, register) { var handler = function(data) { socket.emit("event", { "event": this.event, - "data": wireFriendly(data) + "data": data }); }; diff --git a/core/cb.events.webhook/main.js b/core/cb.events.webhook/main.js index c03f3226..3cce6eee 100644 --- a/core/cb.events.webhook/main.js +++ b/core/cb.events.webhook/main.js @@ -1,7 +1,5 @@ // Requires var _ = require('lodash'); - -var wireFriendly = require('../utils').wireFriendly; var timestamp = require('../utils').timestamp; @@ -42,7 +40,7 @@ function setup(options, imports, register) { event: this.event, // Data of event - data: wireFriendly(data), + data: data, // Workspace ID workspaceId: workspace.id, diff --git a/core/cb.rpc/manager.js b/core/cb.rpc/manager.js index 306c7d11..23598d9f 100644 --- a/core/cb.rpc/manager.js +++ b/core/cb.rpc/manager.js @@ -5,8 +5,6 @@ var express = require('express'); var path = require('path'); -var wireFriendly = require('../utils').wireFriendly; - function HttpRPCManager(server, baseUrl, logger) { // Bind all methods diff --git a/core/utils.js b/core/utils.js index 42613935..dabe3a87 100644 --- a/core/utils.js +++ b/core/utils.js @@ -79,31 +79,6 @@ function methodObj(obj) { return newObj; } -function wireFriendly(obj) { - if(_.isArray(obj)) { - return _.map(obj, wireFriendly); - } else if(_.isString(obj) || _.isNumber(obj)) { - return obj; - } - - var newObj = {}; - var pairs = _.pairs(obj); - - pairs.forEach(function(pair) { - var key = pair[0], value = pair[1]; - - // Skip functions and keys starting with a lower dash - if(_.isFunction(value) || key[0] == '_') { - return; - } - - // Set - newObj[key] = value; - }); - - return newObj; -} - // Return a timestamp of the curent time function timestamp() { return Math.floor(Date.now() / 1000); @@ -203,7 +178,6 @@ exports.batch = batch; exports.execFile = execFile; exports.constant = constant; exports.methodObj = methodObj; -exports.wireFriendly = wireFriendly; exports.timestamp = timestamp; exports.startsWith = startsWith; exports.atob = atob; From 2d1b7999ddd9577760ea3b490df67896d8184de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 19:31:03 -0700 Subject: [PATCH 131/646] Enable log for r.js build of addons --- core/cb.addons/addon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/cb.addons/addon.js b/core/cb.addons/addon.js index f0b2e2ce..0712935c 100644 --- a/core/cb.addons/addon.js +++ b/core/cb.addons/addon.js @@ -138,7 +138,7 @@ var Addon = function(_rootPath, options) { 'baseUrl': addonPath, 'name': main, 'out': output, - 'logLevel': 4, // silent + //'logLevel': 4, // silent 'paths': { 'require-tools': requiretoolsPath }, From 5d6d8334f0c577bfc396931029125b061500ef57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 20:19:37 -0700 Subject: [PATCH 132/646] Add base sections for locals and backtrace --- addons/cb.debug/client.js | 3 +- addons/cb.debug/node/service.js | 15 ++++++++- addons/cb.debug/stylesheets/tab.less | 17 ++++++++++ addons/cb.debug/views/backtrace.js | 33 ++++++++++++++++++++ addons/cb.debug/views/breakpoints.js | 28 +++++++++++++++++ addons/cb.debug/views/locals.js | 38 +++++++++++++++++++++++ addons/cb.debug/views/section.js | 46 +++++++++++++++++++++++++--- addons/cb.debug/views/tab.js | 45 +++++++++++++++++++++------ 8 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 addons/cb.debug/views/backtrace.js create mode 100644 addons/cb.debug/views/breakpoints.js create mode 100644 addons/cb.debug/views/locals.js diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index 714eb141..15817567 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -23,7 +23,8 @@ define([ // Create trminal tab var tab = tabs.add(DebugTab, { - 'path': file.path() + 'path': file.path(), + 'tool': "pdb" }, { 'type': "debug", 'section': "debug" diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index d34258ae..8989e83c 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -71,9 +71,22 @@ DebugRPCService.prototype.breakpoints = function(args, meta) { // Get backtrace DebugRPCService.prototype.backtrace = function(args, meta) { + var that = this; if (!this.dbg) throw "No active debugger"; - return this.dbg.backtrace(); + return this.dbg.backtrace() + .then(function(stack) { + return _.map(stack, function(item) { + var itemPath = item.filename; + + if (itemPath.indexOf(that.workspace.root) == 0) itemPath = itemPath.replace(that.workspace.root, ""); + + return { + 'filename': itemPath, + 'line': item.line + } + }); + }); }; diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less index fc519b91..6102b715 100644 --- a/addons/cb.debug/stylesheets/tab.less +++ b/addons/cb.debug/stylesheets/tab.less @@ -14,5 +14,22 @@ padding: 6px 8px; margin: 0px; } + + .table { + position: absolute; + top: 27px; + bottom: 0px; + left: 0px; + right: 0px; + display: block; + margin: 0px; + overflow: auto; + + tr { + td, th { + border-top-color: rgba(0,0,0, 0.05); + } + } + } } } \ No newline at end of file diff --git a/addons/cb.debug/views/backtrace.js b/addons/cb.debug/views/backtrace.js new file mode 100644 index 00000000..c2309cc8 --- /dev/null +++ b/addons/cb.debug/views/backtrace.js @@ -0,0 +1,33 @@ +define([ + "views/section" +], function(DebugSection) { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var hr = codebox.require("hr/hr"); + var rpc = codebox.require("core/backends/rpc"); + + var BacktraceSection = DebugSection.extend({ + title: "Backtrace", + formats: [ + { + id: "filename", + title: "File" + }, + { + id: "line", + title: "Line" + } + ], + + update: function() { + var that = this; + rpc.execute("debug/backtrace") + .then(function(stack) { + that.clearLines(); + _.each(stack, that.addLine, that); + }); + } + }); + + return BacktraceSection; +}); diff --git a/addons/cb.debug/views/breakpoints.js b/addons/cb.debug/views/breakpoints.js new file mode 100644 index 00000000..06838755 --- /dev/null +++ b/addons/cb.debug/views/breakpoints.js @@ -0,0 +1,28 @@ +define([ + "views/section" +], function(DebugSection) { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var hr = codebox.require("hr/hr"); + var rpc = codebox.require("core/backends/rpc"); + + var BreakpointsSection = DebugSection.extend({ + title: "Breakpoints", + formats: [ + { + id: "filename", + title: "File" + }, + { + id: "line", + title: "Line" + } + ], + + update: function() { + this.clearLines(); + } + }); + + return BreakpointsSection; +}); diff --git a/addons/cb.debug/views/locals.js b/addons/cb.debug/views/locals.js new file mode 100644 index 00000000..142266ad --- /dev/null +++ b/addons/cb.debug/views/locals.js @@ -0,0 +1,38 @@ +define([ + "views/section" +], function(DebugSection) { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var hr = codebox.require("hr/hr"); + var rpc = codebox.require("core/backends/rpc"); + + var LocalsSection = DebugSection.extend({ + title: "Locals", + formats: [ + { + id: "name", + title: "Name" + }, + { + id: "value", + title: "Value" + } + ], + + update: function() { + var that = this; + rpc.execute("debug/locals") + .then(function(locals) { + that.clearLines(); + _.each(locals, function(value, key) { + that.addLine({ + 'name': key, + 'value': value + }); + }); + }); + } + }); + + return LocalsSection; +}); diff --git a/addons/cb.debug/views/section.js b/addons/cb.debug/views/section.js index c086dc6e..0a232169 100644 --- a/addons/cb.debug/views/section.js +++ b/addons/cb.debug/views/section.js @@ -8,21 +8,57 @@ define([], function() { defaults: { title: "" }, - events: { - - }, + events: {}, + formats: [], initialize: function(options) { DebugSection.__super__.initialize.apply(this, arguments); - this.$el.html("

      "+this.options.title+"

      ") + this.$el.html("

      "+this.title+"

      "); + + this.$table = $("", { + 'class': "table" + }); + this.$table.appendTo(this.$el); + this.clearLines(); return this; }, // Render render: function() { - return this.ready(); + }, + + // Clear items + clearLines: function() { + this.$table.empty(); + + // Initiliaze format + var $line = $(""); + + _.each(this.formats, function(key) { + $(""); + + _.each(this.formats, function(key) { + $("
      ", { + 'text': key.title + }).appendTo($line); + }); + $line.appendTo(this.$table); + }, + + // Add an item + addLine: function(item) { + var $line = $("
      ", { + 'text': item[key.id] + }).appendTo($line); + }); + $line.appendTo(this.$table); + }, + + // Update content + update: function() { + // to defined } }); diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index b01c5918..bfd9ae76 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -1,10 +1,14 @@ define([ "views/section", + "views/locals", + "views/backtrace", + "views/breakpoints", "less!stylesheets/tab.less" -], function(DebugSection) { +], function(DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); + var rpc = codebox.require("core/backends/rpc"); var Command = codebox.require("models/command"); var Tab = codebox.require("views/tabs/base"); var box = codebox.require("core/box"); @@ -26,20 +30,19 @@ define([ var that = this; DebugTab.__super__.initialize.apply(this, arguments); + // Create sections + this.locals = new LocalsSection(); + this.backtrace = new BacktraceSection(); + this.breakpoints = new BreakpointsSection(); + // Create grid for sections this.grid = new GridView({ columns: 1000 }); - this.grid.addView(new DebugSection({ - title: "Breakpoints" - })); - this.grid.addView(new DebugSection({ - title: "Stack" - })); - this.grid.addView(new DebugSection({ - title: "Locals" - })); + this.grid.addView(this.breakpoints); + this.grid.addView(this.backtrace); + this.grid.addView(this.locals); this.grid.appendTo(this); @@ -94,12 +97,34 @@ define([ this.commandRestart ]); + // Start debugger + this.initDebugger(); + return this; }, + initDebugger: function() { + var that = this; + return rpc.execute("debug/init", { + 'tool': this.options.tool, + 'path': this.options.path + }) + .then(function() { + return that.updateState(); + }) + }, + // Render render: function() { return this.ready(); + }, + + + // Update debugegr state: stack, locals + updateState: function() { + this.locals.update(); + this.backtrace.update(); + this.breakpoints.update(); } }); From 152ca9fd5b68f5af5766d4bf4fe594be0e24e1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 22:13:00 -0700 Subject: [PATCH 133/646] Add list of breakpoints and command to add a breakpoint --- addons/cb.debug/node/service.js | 23 +++++++++++----- addons/cb.debug/stylesheets/tab.less | 6 ++++- addons/cb.debug/views/breakpoints.js | 11 +++++++- addons/cb.debug/views/section.js | 15 +++++++++-- addons/cb.debug/views/tab.js | 39 ++++++++++++++++++++++++++++ package.json | 2 +- 6 files changed, 85 insertions(+), 11 deletions(-) diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index 8989e83c..d287a731 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -17,6 +17,11 @@ function DebugRPCService(workspace, events) { _.bindAll(this); } +DebugRPCService.prototype._normPath = function(_path) { + if (_path.indexOf(this.workspace.root) == 0) _path = _path.replace(this.workspace.root, ""); + return _path; +}; + // Prepare the debugger DebugRPCService.prototype.init = function(args, meta) { if (!args.tool || !args.path) throw "Need 'tool' and 'path' arguments"; @@ -64,9 +69,19 @@ DebugRPCService.prototype.locals = function(args, meta) { // Get breakpoints DebugRPCService.prototype.breakpoints = function(args, meta) { + var that = this; if (!this.dbg) throw "No active debugger"; - return this.dbg.breakpoints(); + return this.dbg.breakpoints() + .then(function(breakpoints) { + return _.map(breakpoints, function(breakpoint) { + return { + 'num': breakpoint.num, + 'filename': that._normPath(breakpoint.location.filename), + 'line': breakpoint.location.line + } + }); + }); }; // Get backtrace @@ -77,12 +92,8 @@ DebugRPCService.prototype.backtrace = function(args, meta) { return this.dbg.backtrace() .then(function(stack) { return _.map(stack, function(item) { - var itemPath = item.filename; - - if (itemPath.indexOf(that.workspace.root) == 0) itemPath = itemPath.replace(that.workspace.root, ""); - return { - 'filename': itemPath, + 'filename': that._normPath(item.filename), 'line': item.line } }); diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less index 6102b715..c6c7dee4 100644 --- a/addons/cb.debug/stylesheets/tab.less +++ b/addons/cb.debug/stylesheets/tab.less @@ -15,7 +15,7 @@ margin: 0px; } - .table { + .table-container { position: absolute; top: 27px; bottom: 0px; @@ -24,6 +24,10 @@ display: block; margin: 0px; overflow: auto; + } + + .table { + margin: 0px; tr { td, th { diff --git a/addons/cb.debug/views/breakpoints.js b/addons/cb.debug/views/breakpoints.js index 06838755..41dea694 100644 --- a/addons/cb.debug/views/breakpoints.js +++ b/addons/cb.debug/views/breakpoints.js @@ -9,6 +9,10 @@ define([ var BreakpointsSection = DebugSection.extend({ title: "Breakpoints", formats: [ + { + id: "num", + title: "#" + }, { id: "filename", title: "File" @@ -20,7 +24,12 @@ define([ ], update: function() { - this.clearLines(); + var that = this; + rpc.execute("debug/breakpoints") + .then(function(stack) { + that.clearLines(); + _.each(stack, that.addLine, that); + }); } }); diff --git a/addons/cb.debug/views/section.js b/addons/cb.debug/views/section.js index 0a232169..7872a735 100644 --- a/addons/cb.debug/views/section.js +++ b/addons/cb.debug/views/section.js @@ -13,12 +13,23 @@ define([], function() { initialize: function(options) { DebugSection.__super__.initialize.apply(this, arguments); - this.$el.html("

      "+this.title+"

      "); + + this.$title = $("

      ", { + 'text': this.title + }); + + this.$container = $("
      ", { + 'class': "table-container" + }); this.$table = $("", { 'class': "table" }); - this.$table.appendTo(this.$el); + + this.$title.appendTo(this.$el); + this.$container.appendTo(this.$el); + this.$table.appendTo(this.$container); + this.clearLines(); return this; diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index bfd9ae76..91fad299 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -13,6 +13,7 @@ define([ var Tab = codebox.require("views/tabs/base"); var box = codebox.require("core/box"); var user = codebox.require("core/user"); + var dialogs = codebox.require("utils/dialogs"); var GridView = codebox.require("views/grid"); @@ -63,12 +64,21 @@ define([ this.commandRestart = new Command({}, { title: "Restart" }); + this.commandAddBreakpoint = new Command({}, { + title: "Add Breakpoint", + action: function() { + return that.breakpointAdd(); + } + }); // Describe debug tab this.setTabTitle("Debugger "+this.options.path); // Tab menu this.menu + .menuSection([ + this.commandAddBreakpoint + ]) .menuSection([ this.commandStart, this.commandStop, @@ -103,6 +113,7 @@ define([ return this; }, + // Initialize the debugger initDebugger: function() { var that = this; return rpc.execute("debug/init", { @@ -114,6 +125,34 @@ define([ }) }, + // Add a breakpoint + breakpointAdd: function(options) { + var that = this; + return Q() + .then(function() { + if (!options) return dialogs.fields("Add a breakpoint", { + 'path': { + 'label': "Path", + 'type': "text", + 'default': that.options.path + }, + 'line': { + 'label': "Line", + 'type': "number", + 'default': 0 + } + }); + + return options; + }) + .then(function(args) { + return rpc.execute("debug/breakpoint/add", args) + }) + .then(function() { + return that.updateState(); + }); + }, + // Render render: function() { return this.ready(); diff --git a/package.json b/package.json index f966245a..5353d46d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "connect-gzip-static": "0.1.1", "minimatch": "0.2.14", "semver": "2.2.1", - "bugs": "0.0.1" + "bugs": "git+https://github.com/FriendCode/bugs.js.git#cbcff1d1480bbde214a1b50e3234fc4c4a4b99b8" }, "devDependencies": { "codebox-io": "0.3.2", From de53d2f783b6e3b9da50677bb5d89feb796cb437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 22:17:18 -0700 Subject: [PATCH 134/646] Fix commands order in debugger statusbar --- addons/cb.debug/views/tab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index 91fad299..eb325ebd 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -100,11 +100,12 @@ define([ // Statusbar menu this.statusbar.add([ - this.commandStart, - this.commandStop, - this.commandNext, + this.commandRestart, this.commandContinue, - this.commandRestart + this.commandNext, + this.commandStop, + this.commandStart, + this.commandAddBreakpoint ]); // Start debugger From 1366150053079a8bddd96f4f7066f49bb0684349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 22:19:47 -0700 Subject: [PATCH 135/646] Don't send _instance for watcher event success or error --- core/cb.watch/init.js | 1 - 1 file changed, 1 deletion(-) diff --git a/core/cb.watch/init.js b/core/cb.watch/init.js index a50dd2c0..52a1ae3c 100644 --- a/core/cb.watch/init.js +++ b/core/cb.watch/init.js @@ -45,7 +45,6 @@ function init(logger, events, rootPath) { watching: isWatching, state: watcherInstance.state, path: normalize(watcherInstance.path), - _instance: watcherInstance, error: err || null }; From a6afa6d2536913af33ddff678b350c6776ff896f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 10 Mar 2014 22:45:33 -0700 Subject: [PATCH 136/646] Limit debugger to one tab and manage events from debugger runner --- addons/cb.debug/client.js | 44 +++++++++++++++++++++++++-------- addons/cb.debug/node/service.js | 16 ++++++++++-- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index 15817567..280d0904 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -2,11 +2,16 @@ define([ "views/tab", "settings" ], function(DebugTab, settings) { + var Q = codebox.require("hr/promise"); var File = codebox.require("models/file"); var toolbar = codebox.require("core/commands/toolbar"); var dialogs = codebox.require("utils/dialogs"); var files = codebox.require("core/files"); var tabs = codebox.require("core/tabs"); + var box = codebox.require("core/box"); + + // Current debugger tab + var debugTab = null; // Add files handler var filesHandler = files.addHandler("debug", { @@ -21,17 +26,30 @@ define([ settings.user.save(); } - // Create trminal tab - var tab = tabs.add(DebugTab, { - 'path': file.path(), - 'tool': "pdb" - }, { - 'type': "debug", - 'section': "debug" - }); + return Q() + .then(function() { + if (debugTab) return dialogs.confirm("Do you wan to close current debugger?", + "There is already a debugger running, are you sure you want to close it for opening a new one.") + return Q(); + }) + .then(function() { + if (debugTab) debugTab.closeTab(); - // Return the tab - return tab; + // Create debug tab + debugTab = tabs.add(DebugTab, { + 'path': file.path(), + 'tool': "pdb" + }, { + 'type': "debug", + 'section': "debug" + }); + debugTab.on("tab:close", function() { + debugTab = null; + }); + + // Return the tab + return debugTab; + }); } }); @@ -62,4 +80,10 @@ define([ return filesHandler.open(f); }); }); + + + // Bind debug event + box.on("box:debug", function() { + console.log("debugegr event !!!!", arguments); + }); }); \ No newline at end of file diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index d287a731..42322838 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -12,11 +12,13 @@ function DebugRPCService(workspace, events) { this.workspace = workspace; this.events = events; + this.path = null; this.dbg = null; _.bindAll(this); } +// Normalize an absolute path DebugRPCService.prototype._normPath = function(_path) { if (_path.indexOf(this.workspace.root) == 0) _path = _path.replace(this.workspace.root, ""); return _path; @@ -24,17 +26,27 @@ DebugRPCService.prototype._normPath = function(_path) { // Prepare the debugger DebugRPCService.prototype.init = function(args, meta) { + var that = this; if (!args.tool || !args.path) throw "Need 'tool' and 'path' arguments"; if (!_.contains(debuggers, args.tool)) throw "Invalid debugger"; // Create debugger interface + this.path = args.path; this.dbg = bugs[args.tool]( - path.join(this.workspace.root, args.path) + path.join(this.workspace.root, this.path) ); // Signal it this.events.emit('debug.init', { - userId: meta.user.userId + 'path': this.path, + 'userId': meta.user.userId + }); + + // Bind events + this.dbg.runner.on('update', function() { + that.events.emit('debug.update', { + 'path': that.path + }); }); return this.dbg.init() From 86715f9762d31ad0bb3d06f235884123fd879132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 09:46:42 -0700 Subject: [PATCH 137/646] Refactor editor and add base breakpoints in gutter --- addons/cb.debug/breakpoints.js | 3 + addons/cb.files.editor/client.js | 6 +- addons/cb.files.editor/editor/breakpoints.js | 55 ++++++++ .../{ => editor}/codecomplete.js | 0 addons/cb.files.editor/{ => editor}/jshint.js | 0 .../cb.files.editor/{ => editor}/settings.js | 0 .../{editor.js => editor/view.js} | 122 ++++++------------ addons/cb.files.editor/stylesheets/file.less | 5 + 8 files changed, 106 insertions(+), 85 deletions(-) create mode 100644 addons/cb.debug/breakpoints.js create mode 100644 addons/cb.files.editor/editor/breakpoints.js rename addons/cb.files.editor/{ => editor}/codecomplete.js (100%) rename addons/cb.files.editor/{ => editor}/jshint.js (100%) rename addons/cb.files.editor/{ => editor}/settings.js (100%) rename addons/cb.files.editor/{editor.js => editor/view.js} (88%) diff --git a/addons/cb.debug/breakpoints.js b/addons/cb.debug/breakpoints.js new file mode 100644 index 00000000..33f0556b --- /dev/null +++ b/addons/cb.debug/breakpoints.js @@ -0,0 +1,3 @@ +define([], function() { + +}); \ No newline at end of file diff --git a/addons/cb.files.editor/client.js b/addons/cb.files.editor/client.js index b6549b9c..ba09a662 100644 --- a/addons/cb.files.editor/client.js +++ b/addons/cb.files.editor/client.js @@ -1,9 +1,7 @@ define([ "ace", - "editor", - "codecomplete", - "settings" -], function(ace, FileEditorView, codecomplete, userSettings) { + "editor/view" +], function(ace, FileEditorView) { var $ = codebox.require("hr/dom"); var commands = codebox.require("core/commands/toolbar"); var files = codebox.require("core/files"); diff --git a/addons/cb.files.editor/editor/breakpoints.js b/addons/cb.files.editor/editor/breakpoints.js new file mode 100644 index 00000000..de22c1d0 --- /dev/null +++ b/addons/cb.files.editor/editor/breakpoints.js @@ -0,0 +1,55 @@ +define([], function() { + var hr = codebox.require("hr/hr"); + var box = codebox.require("core/box"); + + var EditorBreakpoints = hr.Class.extend({ + initialize: function() { + var that = this; + EditorBreakpoints.__super__.initialize.apply(this, arguments); + + this.editor = this.options.editor; + this.$editor = this.editor.editor; + + + // Breakpoints list change, it signals the change to the box + this.$editor.session.on("changeBreakpoint", function() { + box.trigger("debug:breakpoints", { + 'path': that.editor.model.path(), + 'list': that.getBreakpoints() + }); + }); + + // Add remove breakpoints by clicking the gutter + this.$editor.on("guttermousedown", function(e) { + var target = e.domEvent.target; + if (target.className.indexOf("ace_gutter-cell") == -1) + return; + if (!e.editor.isFocused()) + return; + if (e.clientX > 25 + target.getBoundingClientRect().left) + return; + + var row = e.getDocumentPosition().row; + + if (_.contains(that.getBreakpoints(), row)) { + e.editor.session.clearBreakpoint(row) + } else { + e.editor.session.setBreakpoint(row); + } + e.stop(); + }); + }, + + // Return list of active breakpoints in ace + getBreakpoints: function() { + return _.chain(this.$editor.session.getBreakpoints()) + .keys() + .map(function(row) { + return parseInt(row); + }) + .value(); + } + }); + + return EditorBreakpoints; +}); \ No newline at end of file diff --git a/addons/cb.files.editor/codecomplete.js b/addons/cb.files.editor/editor/codecomplete.js similarity index 100% rename from addons/cb.files.editor/codecomplete.js rename to addons/cb.files.editor/editor/codecomplete.js diff --git a/addons/cb.files.editor/jshint.js b/addons/cb.files.editor/editor/jshint.js similarity index 100% rename from addons/cb.files.editor/jshint.js rename to addons/cb.files.editor/editor/jshint.js diff --git a/addons/cb.files.editor/settings.js b/addons/cb.files.editor/editor/settings.js similarity index 100% rename from addons/cb.files.editor/settings.js rename to addons/cb.files.editor/editor/settings.js diff --git a/addons/cb.files.editor/editor.js b/addons/cb.files.editor/editor/view.js similarity index 88% rename from addons/cb.files.editor/editor.js rename to addons/cb.files.editor/editor/view.js index 243a1e7a..87f02f20 100644 --- a/addons/cb.files.editor/editor.js +++ b/addons/cb.files.editor/editor/view.js @@ -1,10 +1,13 @@ define([ "ace", - "jshint", + "editor/breakpoints", + "editor/codecomplete", + "editor/jshint", + "editor/settings", "theme/textmate", "text!templates/file.html", "less!stylesheets/file.less", -], function(ace, jshint, aceDefaultTheme, templateFile) { +], function(ace, Breakpoints, codecomplete, jshint, editorSettings, aceDefaultTheme, templateFile) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); @@ -21,7 +24,6 @@ define([ var keyboard = codebox.require("utils/keyboard"); var logging = hr.Logger.addNamespace("editor"); - var userSettings = user.settings("editor"); // Import ace var aceRange = ace.require("ace/range"); @@ -123,13 +125,13 @@ define([ { 'title': "Convert Indentation to Spaces", 'action': function() { - that.convertIndentation(" ", userSettings.get("tabsize", 4)); + aceWhitespace.convertIndentation(that.editor.session, " ", editorSettings.user.get("tabsize", 4)); } }, { 'title': "Convert Indentation to Tabs", 'action': function() { - that.convertIndentation("\t", 1); + aceWhitespace.convertIndentation(that.editor.session, "\t", 1); } } ]).menuSection([ @@ -161,15 +163,20 @@ define([ }); this.editor = ace.edit(this.$editor.get(0)); this.editor.session.setUseWorker(true); + this.editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true + }); this.setOptions(); + + this.markersS = {}; this.markersC = {}; this._op_set = false; - // Configure editor - this.editor.setOptions({ - enableBasicAutocompletion: true, - enableSnippets: true + // Breakpoints + this.breakpoints = new Breakpoints({ + editor: this }); // Bind settings changement @@ -180,7 +187,7 @@ define([ }); this.setOptions(ops); }; - userSettings.change(update, this); + editorSettings.user.change(update, this); user.settings("themes").change(update, this); // Bind editor changement -> sync @@ -366,8 +373,9 @@ define([ // Define file for code editor this.sync.setFile(this.model, { - 'sync': userSettings.get("autocollaboration") ? collaborators.size() > 1 : false + 'sync': editorSettings.user.get("autocollaboration") ? collaborators.size() > 1 : false }); + this.focus(); var $input = this.editor.textInput.getElement(); @@ -382,7 +390,6 @@ define([ // Finish rendering finish: function() { - // Add editor to content this.$editor.appendTo(this.$(".editor-inner")); this.editor.resize(); this.editor.renderer.updateFull(); @@ -390,6 +397,7 @@ define([ return FileEditorView.__super__.finish.apply(this, arguments); }, + // Focus editor focus: function() { this.editor.resize(); this.editor.renderer.updateFull(); @@ -397,11 +405,10 @@ define([ return this; }, - /* - * Set editor options: theme, fontsize, ... - */ + // Define editor options setOptions: function(opts) { - this.options = _.defaults(opts || {}, userSettings.all({}), { + var that = this; + this.options = _.defaults(opts || {}, editorSettings.user.all({}), { mode: "text", fontsize: "12", printmargincolumn: 80, @@ -415,9 +422,15 @@ define([ tabsize: 4 }); + // Ste mode this.setMode(this.options.mode); - this.setKeyboardmode(this.options.keyboard); - this.setTheme(themes.current().editor.theme || aceDefaultTheme); + + // Det keyboard mode + ace.config.loadModule(["keybinding", "ace/keyboard/"+this.options.keyboard], function(binding) { + if (binding && binding.handler) that.editor.setKeyboardHandler(binding.handler); + }); + + this.editor.setTheme(themes.current().editor.theme || aceDefaultTheme); this.$editor.css("font-size", this.options.fontsize+"px"); this.editor.setPrintMarginColumn(this.options.printmargincolumn); this.editor.setShowPrintMargin(this.options.showprintmargin); @@ -430,6 +443,13 @@ define([ return this; }, + // Define mdoe option + setMode: function(lang) { + this.options.mode = lang; + this.editor.getSession().setMode("ace/mode/"+lang); + }, + + // Get position (row, column) from index in file posFromIndex: function(index) { var row, lines; lines = this.editor.session.doc.getAllLines(); @@ -445,79 +465,19 @@ define([ }; }, - /* - * Get editor mode - */ - getMode: function() { - return this.options.mode; - }, - - /* - * Set editor mode - * @lang : editor lang mode - */ - setMode: function(lang) { - this.options.mode = lang; - this.editor.getSession().setMode("ace/mode/"+lang); - this.trigger("change:mode"); - return this; - }, - - /* - * Set editor keyboard mode - * @mode: keyboard mode (vim, emacs) - */ - setKeyboardmode: function(mode) { - var self = this; - this.options.keyboard = mode; - ace.config.loadModule(["keybinding", "ace/keyboard/"+self.options.keyboard], function(binding) { - if (binding && binding.handler) { - self.editor.setKeyboardHandler(binding.handler); - self.trigger("change:keyboardmode"); - } - }); - return this; - }, - - /* - * Set theme - * @theme_name : name of the theme - */ - setTheme: function(theme) { - if (_.isString(theme)) { - theme = "ace/theme/" + theme; - } - this.editor.setTheme(theme); - this.trigger("change:theme"); - return this; - }, - - /* - * Convert indentations to - */ - convertIndentation: function(ch, len) { - aceWhitespace.convertIndentation(this.editor.session, ch, len); - }, - - /* - * Save file - */ + // (action) Save file saveFile: function(e) { if (e) e.preventDefault(); this.sync.save(); }, - /* - * Run this file - */ + // (action) Run this file runFile: function(e) { if (e) e.preventDefault(); this.model.run(); }, - /* - * Open search box in code editor - */ + // (action) Open search box searchInFile: function(e) { if (e) e.preventDefault(); this.editor.execCommand("find"); diff --git a/addons/cb.files.editor/stylesheets/file.less b/addons/cb.files.editor/stylesheets/file.less index cdae1423..cac3e176 100644 --- a/addons/cb.files.editor/stylesheets/file.less +++ b/addons/cb.files.editor/stylesheets/file.less @@ -86,6 +86,11 @@ border-right: 3px solid #0ff; border-bottom: 3px solid #0ff; } + + .ace_gutter-cell.ace_breakpoint{ + border-radius: 20px 0px 0px 20px; + box-shadow: 0px 0px 1px 1px red inset; + } } } } From 171b3595ab07ffb558dc50bc40633065785397ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 10:03:38 -0700 Subject: [PATCH 138/646] Move breakpoints when code change in code editor --- addons/cb.files.editor/editor/breakpoints.js | 55 ++++++++++++++++++-- addons/cb.files.editor/editor/view.js | 22 ++++---- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/addons/cb.files.editor/editor/breakpoints.js b/addons/cb.files.editor/editor/breakpoints.js index de22c1d0..85f1a6c5 100644 --- a/addons/cb.files.editor/editor/breakpoints.js +++ b/addons/cb.files.editor/editor/breakpoints.js @@ -10,13 +10,9 @@ define([], function() { this.editor = this.options.editor; this.$editor = this.editor.editor; - // Breakpoints list change, it signals the change to the box this.$editor.session.on("changeBreakpoint", function() { - box.trigger("debug:breakpoints", { - 'path': that.editor.model.path(), - 'list': that.getBreakpoints() - }); + that.signalBreakpoints(); }); // Add remove breakpoints by clicking the gutter @@ -38,6 +34,47 @@ define([], function() { } e.stop(); }); + + // Document change -> update breakpoints list + this.$editor.session.doc.on("change", function(e) { + var delta = e.data; + var range = delta.range; + var changed = false; + + if (range.end.row == range.start.row) + return; + + var len, firstRow; + len = range.end.row - range.start.row; + if (delta.action == "insertText") { + firstRow = range.start.column ? range.start.row + 1 : range.start.row; + } + else { + firstRow = range.start.row; + } + + if (delta.action[0] == "i") { + var args = Array(len); + args.unshift(firstRow, 0); + changed = true; + that.$editor.session.$breakpoints.splice.apply(that.$editor.session.$breakpoints, args); + } + else { + var rem = that.$editor.session.$breakpoints.splice(firstRow + 1, len); + + if (!that.$editor.session.$breakpoints[firstRow]) { + for (var i = rem.length; i--; ) { + if (rem[i]) { + changed = true; + that.$editor.session.$breakpoints[firstRow] = rem[i]; + break; + } + } + } + } + + if (changed) that.signalBreakpoints(); + }); }, // Return list of active breakpoints in ace @@ -48,6 +85,14 @@ define([], function() { return parseInt(row); }) .value(); + }, + + // Signal breakpoints list + signalBreakpoints: function() { + box.trigger("debug:breakpoints", { + 'path': this.editor.model.path(), + 'list': this.getBreakpoints() + }); } }); diff --git a/addons/cb.files.editor/editor/view.js b/addons/cb.files.editor/editor/view.js index 87f02f20..6bdc8156 100644 --- a/addons/cb.files.editor/editor/view.js +++ b/addons/cb.files.editor/editor/view.js @@ -49,6 +49,10 @@ define([ var that = this; FileEditorView.__super__.initialize.apply(this, arguments); + this.markersS = {}; + this.markersC = {}; + this._op_set = false; + // Syntax menu command var syntaxMenu = new Command({}, { 'title': "Syntax", @@ -157,22 +161,23 @@ define([ this.collaboratorsMenu.toggleFlag("disabled", !options.sync); }, this); - // Create base ace editor instance + // Create base ace editor instance and configure it this.$editor = $("
      ", { 'class': "editor-ace" }); this.editor = ace.edit(this.$editor.get(0)); + var $doc = this.editor.session.doc; + + // Set base options this.editor.session.setUseWorker(true); this.editor.setOptions({ enableBasicAutocompletion: true, enableSnippets: true }); - this.setOptions(); - - this.markersS = {}; - this.markersC = {}; - this._op_set = false; + // Force unix newline mode (for cursor position calcul) + $doc.setNewLineMode("unix"); + this.setOptions(); // Breakpoints this.breakpoints = new Breakpoints({ @@ -204,11 +209,6 @@ define([ jshint.applySettings(that.editor); }); - var $doc = this.editor.session.doc; - - // Force unix newline mode (for cursor position calcul) - $doc.setNewLineMode("unix"); - // Send change $doc.on('change', function(d) { if (that._op_set) return; From 9ac9d1a38592706fab3df50d3d6dd0e1846a7691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 10:31:55 -0700 Subject: [PATCH 139/646] Prepare propagation of editor breakpoints to debug service --- addons/cb.debug/breakpoints.js | 41 ++++++++++++++++++++ addons/cb.debug/views/tab.js | 12 +++++- addons/cb.files.editor/editor/breakpoints.js | 11 +++--- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/addons/cb.debug/breakpoints.js b/addons/cb.debug/breakpoints.js index 33f0556b..4b47edd3 100644 --- a/addons/cb.debug/breakpoints.js +++ b/addons/cb.debug/breakpoints.js @@ -1,3 +1,44 @@ define([], function() { + var hr = codebox.require("hr/hr"); + var _ = codebox.require("hr/utils"); + var box = codebox.require("core/box"); + + var Breakpoints = hr.Class.extend({ + initialize: function() { + Breakpoints.__super__.initialize.apply(this, arguments); + // map filename -> array of int + this.breakpoints = {}; + + this.listenTo(box, "debug:breakpoints", this.onBreakpointsChange) + }, + + // Return all breakpoints + all: function() { + return _.clone(this.breakpoints); + }, + + // Signal change for a breakpoint + signalChange: function(change, path, line) { + this.trigger("change:"+change, { + 'change': change, + 'path': path, + 'line': line + }); + }, + + // When breakpoints changed for a file + onBreakpointsChange: function(e) { + var oldBreakpoints = this.breakpoints[e.path] || []; + this.breakpoints[e.path] = _.clone(e.breakpoints); + + var added = _.difference(this.breakpoints[e.path], oldBreakpoints); + var removed = _.difference(this.breakpoints[e.path], oldBreakpoints); + + _.each(added, _.partial(_.bind(this.signalChange, this), "add", e.path)); + _.each(removed, _.partial(_.bind(this.signalChange, this), "remove", e.path)); + } + }); + + return new Breakpoints(); }); \ No newline at end of file diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index eb325ebd..b9c37a00 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -1,10 +1,11 @@ define([ + "breakpoints", "views/section", "views/locals", "views/backtrace", "views/breakpoints", "less!stylesheets/tab.less" -], function(DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) { +], function(breakpoints, DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); @@ -111,6 +112,11 @@ define([ // Start debugger this.initDebugger(); + // Bind event on breakponts changements + this.listenTo(breakpoints, "change", function(e) { + console.log("send to debugger breakpoint", e.change, e.path, e.line) + }); + return this; }, @@ -119,8 +125,10 @@ define([ var that = this; return rpc.execute("debug/init", { 'tool': this.options.tool, - 'path': this.options.path + 'path': this.options.path, + 'breakpoints': breakpoints.all() }) + // Update states .then(function() { return that.updateState(); }) diff --git a/addons/cb.files.editor/editor/breakpoints.js b/addons/cb.files.editor/editor/breakpoints.js index 85f1a6c5..0900366f 100644 --- a/addons/cb.files.editor/editor/breakpoints.js +++ b/addons/cb.files.editor/editor/breakpoints.js @@ -79,11 +79,12 @@ define([], function() { // Return list of active breakpoints in ace getBreakpoints: function() { - return _.chain(this.$editor.session.getBreakpoints()) - .keys() - .map(function(row) { - return parseInt(row); + return _.chain(this.$editor.session.getBreakpoints() || {}) + .map(function(value, key) { + if (!value) return null; + return parseInt(key)+1; }) + .compact() .value(); }, @@ -91,7 +92,7 @@ define([], function() { signalBreakpoints: function() { box.trigger("debug:breakpoints", { 'path': this.editor.model.path(), - 'list': this.getBreakpoints() + 'breakpoints': this.getBreakpoints() }); } }); From 2a4bbeaf3b8fe8faf5205af321bbb604b6880ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 10:40:04 -0700 Subject: [PATCH 140/646] Add set of base breakpoints when init debugger service --- addons/cb.debug/node/service.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index 42322838..90e3060b 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -50,6 +50,26 @@ DebugRPCService.prototype.init = function(args, meta) { }); return this.dbg.init() + // Add breakpoints + .then(function() { + return Q.all( + _.chain(args.breakpoints || {}) + .map(function(lines, path) { + return _.map(lines, function(line) { + return { + 'line': line, + 'path': path + }; + }) + }) + .flatten() + .map(function(point) { + return that.dbg.break(path.join(that.workspace.root, point.path), point.line); + }) + .value() + ); + }) + // Return .then(function() { return {}; }); From 4b21844e9ab097d126d5453ade11908410498e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 10:45:02 -0700 Subject: [PATCH 141/646] Add breakpoint using service when clicking the gutter --- addons/cb.debug/views/tab.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index b9c37a00..dcf31482 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -114,7 +114,13 @@ define([ // Bind event on breakponts changements this.listenTo(breakpoints, "change", function(e) { - console.log("send to debugger breakpoint", e.change, e.path, e.line) + console.log("send to debugger breakpoint", e.change, e.path, e.line); + if (e.change == "add") { + this.breakpointAdd({ + 'path': e.path, + 'line': e.line + }); + } }); return this; From 8c78da01f42457a6d6b56e7b54a024c19092def2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 10:51:59 -0700 Subject: [PATCH 142/646] Fix remove of breakpoints in ace editor --- addons/cb.debug/breakpoints.js | 2 +- addons/cb.files.editor/editor/breakpoints.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/cb.debug/breakpoints.js b/addons/cb.debug/breakpoints.js index 4b47edd3..79981050 100644 --- a/addons/cb.debug/breakpoints.js +++ b/addons/cb.debug/breakpoints.js @@ -33,7 +33,7 @@ define([], function() { this.breakpoints[e.path] = _.clone(e.breakpoints); var added = _.difference(this.breakpoints[e.path], oldBreakpoints); - var removed = _.difference(this.breakpoints[e.path], oldBreakpoints); + var removed = _.difference(oldBreakpoints, this.breakpoints[e.path]); _.each(added, _.partial(_.bind(this.signalChange, this), "add", e.path)); _.each(removed, _.partial(_.bind(this.signalChange, this), "remove", e.path)); diff --git a/addons/cb.files.editor/editor/breakpoints.js b/addons/cb.files.editor/editor/breakpoints.js index 0900366f..c5637f90 100644 --- a/addons/cb.files.editor/editor/breakpoints.js +++ b/addons/cb.files.editor/editor/breakpoints.js @@ -27,7 +27,7 @@ define([], function() { var row = e.getDocumentPosition().row; - if (_.contains(that.getBreakpoints(), row)) { + if (that.hasBreakpoint(row)) { e.editor.session.clearBreakpoint(row) } else { e.editor.session.setBreakpoint(row); @@ -88,6 +88,11 @@ define([], function() { .value(); }, + // Check if has breakpoint at a line + hasBreakpoint: function(row) { + return _.contains(this.getBreakpoints(), row+1) + }, + // Signal breakpoints list signalBreakpoints: function() { box.trigger("debug:breakpoints", { From a843c55763ebf103181e14bd3f3e2d6afd5e4e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 10:59:36 -0700 Subject: [PATCH 143/646] Remove breakpoints from service by toggling it off --- addons/cb.debug/node/service.js | 4 +-- addons/cb.debug/views/breakpoints.js | 13 +++++-- addons/cb.debug/views/tab.js | 54 +++++++++++++--------------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index 90e3060b..4b14a413 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -85,11 +85,11 @@ DebugRPCService.prototype.breakpoint_add = function(args, meta) { // Remove breakpoint -DebugRPCService.prototype.breakpoint_delete = function(args, meta) { +DebugRPCService.prototype.breakpoint_clear = function(args, meta) { if (!args.id) throw "Need 'id' argument"; if (!this.dbg) throw "No active debugger"; - return this.dbg.delete(args.id); + return this.dbg.clear(args.id); }; // Get locals diff --git a/addons/cb.debug/views/breakpoints.js b/addons/cb.debug/views/breakpoints.js index 41dea694..8edb749e 100644 --- a/addons/cb.debug/views/breakpoints.js +++ b/addons/cb.debug/views/breakpoints.js @@ -23,12 +23,21 @@ define([ } ], + initialize: function(options) { + BreakpointsSection.__super__.initialize.apply(this, arguments); + + this.list = []; + + return this; + }, + update: function() { var that = this; rpc.execute("debug/breakpoints") - .then(function(stack) { + .then(function(breakpoints) { + that.list = breakpoints; that.clearLines(); - _.each(stack, that.addLine, that); + _.each(breakpoints, that.addLine, that); }); } }); diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index dcf31482..98ced938 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -65,21 +65,12 @@ define([ this.commandRestart = new Command({}, { title: "Restart" }); - this.commandAddBreakpoint = new Command({}, { - title: "Add Breakpoint", - action: function() { - return that.breakpointAdd(); - } - }); // Describe debug tab this.setTabTitle("Debugger "+this.options.path); // Tab menu this.menu - .menuSection([ - this.commandAddBreakpoint - ]) .menuSection([ this.commandStart, this.commandStop, @@ -105,8 +96,7 @@ define([ this.commandContinue, this.commandNext, this.commandStop, - this.commandStart, - this.commandAddBreakpoint + this.commandStart ]); // Start debugger @@ -120,6 +110,13 @@ define([ 'path': e.path, 'line': e.line }); + } else { + var point = this.getBreakpoint({ + 'path': e.path, + 'line': e.line + }); + + if (point) this.breakpointRemove(point.num); } }); @@ -140,28 +137,27 @@ define([ }) }, + // Get a breapoint id from its location + getBreakpoint: function(location) { + return _.find(this.breakpoints.list, function(point) { + return point.filename == location.path && point.line == location.line; + }); + }, + // Add a breakpoint - breakpointAdd: function(options) { + breakpointAdd: function(args) { var that = this; - return Q() + rpc.execute("debug/breakpoint/add", args) .then(function() { - if (!options) return dialogs.fields("Add a breakpoint", { - 'path': { - 'label': "Path", - 'type': "text", - 'default': that.options.path - }, - 'line': { - 'label': "Line", - 'type': "number", - 'default': 0 - } - }); + return that.updateState(); + }); + }, - return options; - }) - .then(function(args) { - return rpc.execute("debug/breakpoint/add", args) + // Remove a breakpoint + breakpointRemove: function(num) { + var that = this; + rpc.execute("debug/breakpoint/clear", { + 'id': num }) .then(function() { return that.updateState(); From b8de5c5051914df54a42305957e9761f967e6296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 11:22:01 -0700 Subject: [PATCH 144/646] Move breakpoints manager to codebox core Clear breakpoints when closing a file --- addons/cb.debug/views/tab.js | 5 +-- addons/cb.files.editor/editor/breakpoints.js | 6 +-- addons/cb.files.editor/editor/view.js | 10 +++++ .../core/debug}/breakpoints.js | 39 ++++++++++--------- client/models/file.js | 24 +++++++++++- 5 files changed, 57 insertions(+), 27 deletions(-) rename {addons/cb.debug => client/core/debug}/breakpoints.js (62%) diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index 98ced938..d1a49992 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -1,15 +1,15 @@ define([ - "breakpoints", "views/section", "views/locals", "views/backtrace", "views/breakpoints", "less!stylesheets/tab.less" -], function(breakpoints, DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) { +], function(DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); var rpc = codebox.require("core/backends/rpc"); + var breakpoints = codebox.require("core/debug/breakpoints"); var Command = codebox.require("models/command"); var Tab = codebox.require("views/tabs/base"); var box = codebox.require("core/box"); @@ -104,7 +104,6 @@ define([ // Bind event on breakponts changements this.listenTo(breakpoints, "change", function(e) { - console.log("send to debugger breakpoint", e.change, e.path, e.line); if (e.change == "add") { this.breakpointAdd({ 'path': e.path, diff --git a/addons/cb.files.editor/editor/breakpoints.js b/addons/cb.files.editor/editor/breakpoints.js index c5637f90..8bad72b9 100644 --- a/addons/cb.files.editor/editor/breakpoints.js +++ b/addons/cb.files.editor/editor/breakpoints.js @@ -1,6 +1,5 @@ define([], function() { var hr = codebox.require("hr/hr"); - var box = codebox.require("core/box"); var EditorBreakpoints = hr.Class.extend({ initialize: function() { @@ -95,10 +94,7 @@ define([], function() { // Signal breakpoints list signalBreakpoints: function() { - box.trigger("debug:breakpoints", { - 'path': this.editor.model.path(), - 'breakpoints': this.getBreakpoints() - }); + this.editor.model.setBreakpoints(this.getBreakpoints()); } }); diff --git a/addons/cb.files.editor/editor/view.js b/addons/cb.files.editor/editor/view.js index 6bdc8156..986a4943 100644 --- a/addons/cb.files.editor/editor/view.js +++ b/addons/cb.files.editor/editor/view.js @@ -340,8 +340,18 @@ define([ if (state) this.focus(); }, this); this.tab.on("tab:close", function() { + // Clear breakpoints + this.model.clearBreakpoints(); + + // Finish sync this.sync.close(); + + // Destroy the editor + this.editor.destroy(); + + // Destroy events and instance this.off(); + this.stopListening(); }, this); // Bind editor sync state changements diff --git a/addons/cb.debug/breakpoints.js b/client/core/debug/breakpoints.js similarity index 62% rename from addons/cb.debug/breakpoints.js rename to client/core/debug/breakpoints.js index 79981050..9eb1fc75 100644 --- a/addons/cb.debug/breakpoints.js +++ b/client/core/debug/breakpoints.js @@ -1,7 +1,7 @@ -define([], function() { - var hr = codebox.require("hr/hr"); - var _ = codebox.require("hr/utils"); - var box = codebox.require("core/box"); +define([ + "hr/hr", + "hr/utils" +], function(hr, _) { var Breakpoints = hr.Class.extend({ initialize: function() { @@ -9,13 +9,6 @@ define([], function() { // map filename -> array of int this.breakpoints = {}; - - this.listenTo(box, "debug:breakpoints", this.onBreakpointsChange) - }, - - // Return all breakpoints - all: function() { - return _.clone(this.breakpoints); }, // Signal change for a breakpoint @@ -27,16 +20,26 @@ define([], function() { }); }, + // Return all breakpoints + all: function() { + return _.clone(this.breakpoints); + }, + + // Get breakpoints for a file + getFileBreakpoints: function(path) { + return this.breakpoints[path] || []; + }, + // When breakpoints changed for a file - onBreakpointsChange: function(e) { - var oldBreakpoints = this.breakpoints[e.path] || []; - this.breakpoints[e.path] = _.clone(e.breakpoints); + setFileBreakpoints: function(path, breakpoints) { + var oldBreakpoints = this.breakpoints[path] || []; + this.breakpoints[path] = _.clone(breakpoints); - var added = _.difference(this.breakpoints[e.path], oldBreakpoints); - var removed = _.difference(oldBreakpoints, this.breakpoints[e.path]); + var added = _.difference(this.breakpoints[path], oldBreakpoints); + var removed = _.difference(oldBreakpoints, this.breakpoints[path]); - _.each(added, _.partial(_.bind(this.signalChange, this), "add", e.path)); - _.each(removed, _.partial(_.bind(this.signalChange, this), "remove", e.path)); + _.each(added, _.partial(_.bind(this.signalChange, this), "add", path)); + _.each(removed, _.partial(_.bind(this.signalChange, this), "remove", path)); } }); diff --git a/client/models/file.js b/client/models/file.js index 266d5dec..22ce3376 100644 --- a/client/models/file.js +++ b/client/models/file.js @@ -4,6 +4,7 @@ define([ "hr/hr", 'core/backends/rpc', "core/backends/vfs", + "core/debug/breakpoints", 'models/command', "utils/string", "utils/url", @@ -12,7 +13,7 @@ define([ "utils/dialogs", "utils/uploader", "core/operations" -], function(Q, _, hr, rpc, vfs, Command, string, Url, Languages, FileSync, dialogs, Uploader, operations) { +], function(Q, _, hr, rpc, vfs, breakpoints, Command, string, Url, Languages, FileSync, dialogs, Uploader, operations) { var logging = hr.Logger.addNamespace("files"); var File = hr.Model.extend({ @@ -298,6 +299,27 @@ define([ return "."+this.get("name").split('.').pop(); }, + /* + * Return current breakpoints for this file + */ + getBreakpoints: function() { + return breakpoints.getFileBreakpoints(this.path()); + }, + + /* + * Set breakpoints for this file + */ + setBreakpoints: function(points) { + return breakpoints.setFileBreakpoints(this.path(), points); + }, + + /* + * Clear breakpoints for this file + */ + clearBreakpoints: function() { + return this.setBreakpoints([]); + }, + /* * Return file sync environment id */ From b4980d68e019bdf1facd04d2f9d05adac6e6118b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 12:15:52 -0700 Subject: [PATCH 145/646] Switch to multi debuggers --- addons/cb.debug/client.js | 6 -- addons/cb.debug/debugger.js | 91 ++++++++++++++++++++++++++++ addons/cb.debug/node/service.js | 56 ++++++++++------- addons/cb.debug/views/backtrace.js | 3 +- addons/cb.debug/views/breakpoints.js | 12 +--- addons/cb.debug/views/locals.js | 3 +- addons/cb.debug/views/section.js | 6 +- addons/cb.debug/views/tab.js | 74 ++++++++-------------- package.json | 2 +- 9 files changed, 162 insertions(+), 91 deletions(-) create mode 100644 addons/cb.debug/debugger.js diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index 280d0904..a573177a 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -80,10 +80,4 @@ define([ return filesHandler.open(f); }); }); - - - // Bind debug event - box.on("box:debug", function() { - console.log("debugegr event !!!!", arguments); - }); }); \ No newline at end of file diff --git a/addons/cb.debug/debugger.js b/addons/cb.debug/debugger.js new file mode 100644 index 00000000..5c1e2eb4 --- /dev/null +++ b/addons/cb.debug/debugger.js @@ -0,0 +1,91 @@ +define([], function() { + var hr = codebox.require("hr/hr"); + var Q = codebox.require("hr/promise"); + var box = codebox.require("core/box"); + var rpc = codebox.require("core/backends/rpc"); + + var Debugger = hr.Class.extend({ + initialize: function(options) { + Debugger.__super__.initialize.apply(this, arguments); + + this.id = null; + this._breakpoints = []; + + // Bind debug event + this.listenTo(box, "box:debug", function() { + console.log("debugger event !!!!", arguments); + //this.trigger("update"); + }); + }, + + // Initialize the debugger + init: function(args) { + var that = this; + return rpc.execute("debug/init", args) + .then(function(dbg) { + console.log("init !!", dbg); + that.id = dbg.id; + }); + }, + + // Execute a RPC requests + execute: function(method, args) { + return rpc.execute("debug/"+method, _.extend(args || {}, { + 'id': this.id + })); + }, + + // Get locals + locals: function() { + return this.execute("locals"); + }, + + // Get breakpoints + breakpoints: function() { + var that = this; + + return this.execute("breakpoints") + .then(function(list) { + that._breakpoints = list; + return list; + }); + }, + + // Get backtrace + backtrace: function() { + return this.execute("backtrace"); + }, + + // Get a breapoint id from its location + getBreakpoint: function(location) { + return _.find(this._breakpoints, function(point) { + return point.filename == location.path && point.line == location.line; + }); + }, + + // Add a breakpoint + breakpointAdd: function(args) { + var that = this; + return this.execute("breakpoint/add", args) + .then(function(point) { + that.trigger("update"); + return point; + }); + }, + + // Remove a breakpoint + breakpointRemove: function(num) { + var that = this; + + return this.execute("breakpoint/clear", { + 'id': num + }) + .then(function(point) { + that.trigger("update"); + return point; + }); + }, + }); + + return Debugger; +}); \ No newline at end of file diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index 4b14a413..6103e886 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -12,8 +12,7 @@ function DebugRPCService(workspace, events) { this.workspace = workspace; this.events = events; - this.path = null; - this.dbg = null; + this.dbgs = {}; _.bindAll(this); } @@ -24,32 +23,43 @@ DebugRPCService.prototype._normPath = function(_path) { return _path; }; +// Get debugger +DebugRPCService.prototype._dbg = function(id) { + if (!this.dbgs[id]) throw "No debugger associated with this id"; + return this.dbgs[id]; +}; + // Prepare the debugger DebugRPCService.prototype.init = function(args, meta) { var that = this; if (!args.tool || !args.path) throw "Need 'tool' and 'path' arguments"; if (!_.contains(debuggers, args.tool)) throw "Invalid debugger"; + args.id = args.id || _.uniqueId("debugger"); + + if (this.dbgs[args.id]) throw "This debugger already exists"; + // Create debugger interface - this.path = args.path; - this.dbg = bugs[args.tool]( - path.join(this.workspace.root, this.path) + this.dbgs[args.id] = bugs[args.tool]( + path.join(this.workspace.root, args.path) ); // Signal it this.events.emit('debug.init', { - 'path': this.path, + 'id': args.id, + 'path': args.path, 'userId': meta.user.userId }); // Bind events - this.dbg.runner.on('update', function() { + this.dbgs[args.id].runner.on('update', function() { that.events.emit('debug.update', { - 'path': that.path + 'id': args.id, + 'path': args.path }); }); - return this.dbg.init() + return this.dbgs[args.id].init() // Add breakpoints .then(function() { return Q.all( @@ -64,12 +74,22 @@ DebugRPCService.prototype.init = function(args, meta) { }) .flatten() .map(function(point) { - return that.dbg.break(path.join(that.workspace.root, point.path), point.line); + return that.dbgs[args.id].break(path.join(that.workspace.root, point.path), point.line); }) .value() ); }) // Return + .then(function() { + return { + 'id': args.id + }; + }); +}; + +// Close a debugger +DebugRPCService.prototype.close = function(args, meta) { + return this._dbg(args.id).kill() .then(function() { return {}; }); @@ -78,33 +98,28 @@ DebugRPCService.prototype.init = function(args, meta) { // Add breakpoint DebugRPCService.prototype.breakpoint_add = function(args, meta) { if (!args.line || !args.path) throw "Need 'line' and 'path' arguments"; - if (!this.dbg) throw "No active debugger"; - return this.dbg.break(path.join(this.workspace.root, args.path), args.line); + return this._dbg(args.id).break(path.join(this.workspace.root, args.path), args.line); }; // Remove breakpoint DebugRPCService.prototype.breakpoint_clear = function(args, meta) { if (!args.id) throw "Need 'id' argument"; - if (!this.dbg) throw "No active debugger"; - return this.dbg.clear(args.id); + return this._dbg(args.id).clear(args.id); }; // Get locals DebugRPCService.prototype.locals = function(args, meta) { - if (!this.dbg) throw "No active debugger"; - - return this.dbg.locals(); + return this._dbg(args.id).locals(); }; // Get breakpoints DebugRPCService.prototype.breakpoints = function(args, meta) { var that = this; - if (!this.dbg) throw "No active debugger"; - return this.dbg.breakpoints() + return this._dbg(args.id).breakpoints() .then(function(breakpoints) { return _.map(breakpoints, function(breakpoint) { return { @@ -119,9 +134,8 @@ DebugRPCService.prototype.breakpoints = function(args, meta) { // Get backtrace DebugRPCService.prototype.backtrace = function(args, meta) { var that = this; - if (!this.dbg) throw "No active debugger"; - return this.dbg.backtrace() + return this._dbg(args.id).backtrace() .then(function(stack) { return _.map(stack, function(item) { return { diff --git a/addons/cb.debug/views/backtrace.js b/addons/cb.debug/views/backtrace.js index c2309cc8..c755dced 100644 --- a/addons/cb.debug/views/backtrace.js +++ b/addons/cb.debug/views/backtrace.js @@ -21,7 +21,8 @@ define([ update: function() { var that = this; - rpc.execute("debug/backtrace") + + return this.dbg.backtrace() .then(function(stack) { that.clearLines(); _.each(stack, that.addLine, that); diff --git a/addons/cb.debug/views/breakpoints.js b/addons/cb.debug/views/breakpoints.js index 8edb749e..781cece2 100644 --- a/addons/cb.debug/views/breakpoints.js +++ b/addons/cb.debug/views/breakpoints.js @@ -23,19 +23,11 @@ define([ } ], - initialize: function(options) { - BreakpointsSection.__super__.initialize.apply(this, arguments); - - this.list = []; - - return this; - }, - update: function() { var that = this; - rpc.execute("debug/breakpoints") + + return this.dbg.breakpoints() .then(function(breakpoints) { - that.list = breakpoints; that.clearLines(); _.each(breakpoints, that.addLine, that); }); diff --git a/addons/cb.debug/views/locals.js b/addons/cb.debug/views/locals.js index 142266ad..650bb79d 100644 --- a/addons/cb.debug/views/locals.js +++ b/addons/cb.debug/views/locals.js @@ -21,7 +21,8 @@ define([ update: function() { var that = this; - rpc.execute("debug/locals") + + return this.dbg.locals() .then(function(locals) { that.clearLines(); _.each(locals, function(value, key) { diff --git a/addons/cb.debug/views/section.js b/addons/cb.debug/views/section.js index 7872a735..2250a3b8 100644 --- a/addons/cb.debug/views/section.js +++ b/addons/cb.debug/views/section.js @@ -6,7 +6,8 @@ define([], function() { var DebugSection = hr.View.extend({ className: "debug-section", defaults: { - title: "" + title: "", + dbg: null }, events: {}, formats: [], @@ -14,6 +15,9 @@ define([], function() { initialize: function(options) { DebugSection.__super__.initialize.apply(this, arguments); + // Debugger client + this.dbg = this.options.dbg; + this.$title = $("

      ", { 'text': this.title }); diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index d1a49992..c0a3f9c1 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -1,14 +1,14 @@ define([ + "debugger", "views/section", "views/locals", "views/backtrace", "views/breakpoints", "less!stylesheets/tab.less" -], function(DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) { +], function(Debugger, DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); - var rpc = codebox.require("core/backends/rpc"); var breakpoints = codebox.require("core/debug/breakpoints"); var Command = codebox.require("models/command"); var Tab = codebox.require("views/tabs/base"); @@ -32,10 +32,21 @@ define([ var that = this; DebugTab.__super__.initialize.apply(this, arguments); + this.dbg = new Debugger(); + this.listenTo(this.dbg, "update", function() { + this.updateState(); + }); + // Create sections - this.locals = new LocalsSection(); - this.backtrace = new BacktraceSection(); - this.breakpoints = new BreakpointsSection(); + this.locals = new LocalsSection({ + dbg: this.dbg + }); + this.backtrace = new BacktraceSection({ + dbg: this.dbg + }); + this.breakpoints = new BreakpointsSection({ + dbg: this.dbg + }); // Create grid for sections this.grid = new GridView({ @@ -100,69 +111,32 @@ define([ ]); // Start debugger - this.initDebugger(); + this.dbg.init({ + 'tool': this.options.tool, + 'path': this.options.path, + 'breakpoints': breakpoints.all() + }); // Bind event on breakponts changements this.listenTo(breakpoints, "change", function(e) { if (e.change == "add") { - this.breakpointAdd({ + this.dbg.breakpointAdd({ 'path': e.path, 'line': e.line }); } else { - var point = this.getBreakpoint({ + var point = this.dbg.getBreakpoint({ 'path': e.path, 'line': e.line }); - if (point) this.breakpointRemove(point.num); + if (point) this.dbg.breakpointRemove(point.num); } }); return this; }, - // Initialize the debugger - initDebugger: function() { - var that = this; - return rpc.execute("debug/init", { - 'tool': this.options.tool, - 'path': this.options.path, - 'breakpoints': breakpoints.all() - }) - // Update states - .then(function() { - return that.updateState(); - }) - }, - - // Get a breapoint id from its location - getBreakpoint: function(location) { - return _.find(this.breakpoints.list, function(point) { - return point.filename == location.path && point.line == location.line; - }); - }, - - // Add a breakpoint - breakpointAdd: function(args) { - var that = this; - rpc.execute("debug/breakpoint/add", args) - .then(function() { - return that.updateState(); - }); - }, - - // Remove a breakpoint - breakpointRemove: function(num) { - var that = this; - rpc.execute("debug/breakpoint/clear", { - 'id': num - }) - .then(function() { - return that.updateState(); - }); - }, - // Render render: function() { return this.ready(); diff --git a/package.json b/package.json index 5353d46d..c406cd8f 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "connect-gzip-static": "0.1.1", "minimatch": "0.2.14", "semver": "2.2.1", - "bugs": "git+https://github.com/FriendCode/bugs.js.git#cbcff1d1480bbde214a1b50e3234fc4c4a4b99b8" + "bugs": "git+https://github.com/FriendCode/bugs.js.git#a8831f169b657f1a0b9b28860d4aaac5ee65f863" }, "devDependencies": { "codebox-io": "0.3.2", From c54e43312c4673f7202920f195772ee58747de60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 14:26:26 -0700 Subject: [PATCH 146/646] Add action start, restart, next, continue, stop to debugger --- addons/cb.debug/debugger.js | 65 +++++++++++++++++++++++++++++++-- addons/cb.debug/node/service.js | 29 ++++++++++++++- addons/cb.debug/views/tab.js | 30 ++++++++++++--- package.json | 2 +- 4 files changed, 114 insertions(+), 12 deletions(-) diff --git a/addons/cb.debug/debugger.js b/addons/cb.debug/debugger.js index 5c1e2eb4..05f48218 100644 --- a/addons/cb.debug/debugger.js +++ b/addons/cb.debug/debugger.js @@ -23,8 +23,18 @@ define([], function() { var that = this; return rpc.execute("debug/init", args) .then(function(dbg) { - console.log("init !!", dbg); that.id = dbg.id; + that.trigger("update:init"); + }); + }, + + // Close debugger + close: function() { + var that = this; + + return this.execute("close") + .then(function() { + that.stopListening(); }); }, @@ -68,7 +78,7 @@ define([], function() { var that = this; return this.execute("breakpoint/add", args) .then(function(point) { - that.trigger("update"); + that.trigger("update:breakpoints:add"); return point; }); }, @@ -78,13 +88,60 @@ define([], function() { var that = this; return this.execute("breakpoint/clear", { - 'id': num + 'num': num }) .then(function(point) { - that.trigger("update"); + that.trigger("update:breakpoints:clear"); return point; }); }, + + // Start + start: function(arg) { + var that = this; + return this.execute("start", { + 'arg': arg + }) + .then(function() { + that.trigger("update:start"); + }); + }, + + // Stop + stop: function() { + var that = this; + return this.execute("stop") + .then(function() { + that.trigger("update:stop"); + }); + }, + + // Next + next: function() { + var that = this; + return this.execute("next") + .then(function() { + that.trigger("update:next"); + }); + }, + + // Continue + cont: function() { + var that = this; + return this.execute("cont") + .then(function() { + that.trigger("update:cont"); + }); + }, + + // Restart + restart: function() { + var that = this; + return this.execute("restart") + .then(function() { + that.trigger("update:restart"); + }); + }, }); return Debugger; diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index 6103e886..81496094 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -95,6 +95,31 @@ DebugRPCService.prototype.close = function(args, meta) { }); }; +// Start the debugger +DebugRPCService.prototype.start = function(args, meta) { + return this._dbg(args.id).start(args.arg || ""); +}; + +// Stop the debugger +DebugRPCService.prototype.stop = function(args, meta) { + return this._dbg(args.id).stop(); +}; + +// Next +DebugRPCService.prototype.next = function(args, meta) { + return this._dbg(args.id).next(); +}; + +// Continue +DebugRPCService.prototype.cont = function(args, meta) { + return this._dbg(args.id).continue(); +}; + +// Restart the debugger +DebugRPCService.prototype.restart = function(args, meta) { + return this._dbg(args.id).restart(); +}; + // Add breakpoint DebugRPCService.prototype.breakpoint_add = function(args, meta) { if (!args.line || !args.path) throw "Need 'line' and 'path' arguments"; @@ -105,9 +130,9 @@ DebugRPCService.prototype.breakpoint_add = function(args, meta) { // Remove breakpoint DebugRPCService.prototype.breakpoint_clear = function(args, meta) { - if (!args.id) throw "Need 'id' argument"; + if (!args.num) throw "Need 'num' argument"; - return this._dbg(args.id).clear(args.id); + return this._dbg(args.id).clear(args.num); }; // Get locals diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index c0a3f9c1..8e98c62a 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -62,19 +62,34 @@ define([ // Base commands: start, stop, next, continue, restart this.commandStart = new Command({}, { - title: "Start" + title: "Start", + action: function() { + that.dbg.start(); + } }); this.commandStop = new Command({}, { - title: "Stop" + title: "Stop", + action: function() { + that.dbg.stop(); + } }); this.commandNext = new Command({}, { - title: "Next" + title: "Next", + action: function() { + that.dbg.next(); + } }); this.commandContinue = new Command({}, { - title: "Continue" + title: "Continue", + action: function() { + that.dbg.cont(); + } }); this.commandRestart = new Command({}, { - title: "Restart" + title: "Restart", + action: function() { + that.dbg.restart(); + } }); // Describe debug tab @@ -134,6 +149,11 @@ define([ } }); + // Bind close tab + this.on("tab:close", function() { + this.dbg.close(); + }, this); + return this; }, diff --git a/package.json b/package.json index c406cd8f..af9ee912 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "connect-gzip-static": "0.1.1", "minimatch": "0.2.14", "semver": "2.2.1", - "bugs": "git+https://github.com/FriendCode/bugs.js.git#a8831f169b657f1a0b9b28860d4aaac5ee65f863" + "bugs": "git+https://github.com/FriendCode/bugs.js.git#27ef22296ef946165d31928d106dc4fe9ed8fcd5" }, "devDependencies": { "codebox-io": "0.3.2", From 3e412935140a1deafef745ce65582a7dd70db25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 14:41:12 -0700 Subject: [PATCH 147/646] Add auto selection of debuggers --- addons/cb.debug/client.js | 3 +-- addons/cb.debug/node/service.js | 33 ++++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js index a573177a..e7ac8153 100644 --- a/addons/cb.debug/client.js +++ b/addons/cb.debug/client.js @@ -37,8 +37,7 @@ define([ // Create debug tab debugTab = tabs.add(DebugTab, { - 'path': file.path(), - 'tool': "pdb" + 'path': file.path() }, { 'type': "debug", 'section': "debug" diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index 81496094..7b21d2a7 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -4,9 +4,18 @@ var _ = require('lodash'); var path = require('path'); var bugs = require('bugs'); -var debuggers = [ - "pdb", "gdb", "jdb", "rdb" -]; +var debuggers = { + // Python + "pdb": ["py"], + + // Native + "gdb": ["o"], + + // Java + "jdb": ["java"], + + "rdb": ["rb"] +}; function DebugRPCService(workspace, events) { this.workspace = workspace; @@ -32,8 +41,22 @@ DebugRPCService.prototype._dbg = function(id) { // Prepare the debugger DebugRPCService.prototype.init = function(args, meta) { var that = this; - if (!args.tool || !args.path) throw "Need 'tool' and 'path' arguments"; - if (!_.contains(debuggers, args.tool)) throw "Invalid debugger"; + if (!args.path) throw "Need 'tool' and 'path' arguments"; + + // Auto choice debugger + if (!args.tool) { + var ext = args.path.split('.').pop(); + args.tool = _.chain(debuggers) + .pairs() + .find(function(d) { + return _.contains(d[1], ext) + }) + .value(); + args.tool = args.tool ? args.tool[0] : null; + } + + // Check debugger is valide + if (!_.contains(_.keys(debuggers), args.tool)) throw "Invalid debugger"; args.id = args.id || _.uniqueId("debugger"); From 90bcec1806cac51992cc6e18109d9ab0c64ebb24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 16:36:14 -0700 Subject: [PATCH 148/646] Add console to debugger --- addons/cb.debug/debugger.js | 21 ++++++ addons/cb.debug/node/service.js | 7 ++ addons/cb.debug/stylesheets/tab.less | 54 ++++++++++++++ addons/cb.debug/views/console.js | 101 +++++++++++++++++++++++++++ addons/cb.debug/views/section.js | 1 - addons/cb.debug/views/tab.js | 23 ++++-- package.json | 2 +- 7 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 addons/cb.debug/views/console.js diff --git a/addons/cb.debug/debugger.js b/addons/cb.debug/debugger.js index 05f48218..13b5f927 100644 --- a/addons/cb.debug/debugger.js +++ b/addons/cb.debug/debugger.js @@ -142,6 +142,27 @@ define([], function() { that.trigger("update:restart"); }); }, + + // Eval code + eval: function(code) { + var that = this; + + var handle = function(type, data) { + that.trigger("update:eval:"+type); + return { + 'type': type, + 'content': data + }; + }; + + return this.execute("eval", { + 'code': code + }) + .then( + _.partial(handle, "log"), + _.partial(handle, "error") + ); + }, }); return Debugger; diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js index 7b21d2a7..9f46231f 100644 --- a/addons/cb.debug/node/service.js +++ b/addons/cb.debug/node/service.js @@ -163,6 +163,13 @@ DebugRPCService.prototype.locals = function(args, meta) { return this._dbg(args.id).locals(); }; +// Eval code +DebugRPCService.prototype.eval = function(args, meta) { + if (!args.code) throw "Need 'code' arguments"; + + return this._dbg(args.id).eval(args.code); +}; + // Get breakpoints DebugRPCService.prototype.breakpoints = function(args, meta) { var that = this; diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less index c6c7dee4..76cb7361 100644 --- a/addons/cb.debug/stylesheets/tab.less +++ b/addons/cb.debug/stylesheets/tab.less @@ -35,5 +35,59 @@ } } } + + &.debug-console { + .console-body { + .line { + padding-bottom: 3px; + border-bottom: 1px solid rgba(0,0,0, 0.05); + + * { + line-height: 1.4em; + font-size: 14px; + } + + i { + float: left; + width: 24px; + text-align: center; + } + + pre { + margin: 0px; + padding: 0px; + background: none; + color: inherit; + border: none; + display: block; + margin-left: 24px; + font-family: inherit; + } + + &.error { + color: #a94442; + } + &.input { + padding-top: 3px; + padding-bottom: 0px; + border-color: transparent; + } + } + } + + input, input:focus { + color: inherit; + background: transparent; + border-color: transparent; + box-shadow: none; + font-size: 14px; + line-height: 1.4em; + padding: 0px; + margin: 0px; + height: 23px; + padding-left: 24px; + position: relative; + } + } } } \ No newline at end of file diff --git a/addons/cb.debug/views/console.js b/addons/cb.debug/views/console.js new file mode 100644 index 00000000..a3177bc1 --- /dev/null +++ b/addons/cb.debug/views/console.js @@ -0,0 +1,101 @@ +define([], function() { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var hr = codebox.require("hr/hr"); + + var ConsoleSection = hr.View.extend({ + className: "debug-section debug-console", + title: "Console", + defaults: { + dbg: null + }, + events: { + "click": "focus" + }, + logIcons: { + 'log': "fa fa-blank", + 'input': "fa fa-angle-right", + 'error': "fa fa-times-circle" + }, + + initialize: function(options) { + var that = this; + ConsoleSection.__super__.initialize.apply(this, arguments); + + // Debugger client + this.dbg = this.options.dbg; + + this.$title = $("

      ", { + 'text': this.title + }); + + this.$container = $("
      ", { + 'class': "table-container" + }); + + this.$body = $("
      ", { + 'class': "console-body" + }); + + this.$input = $("", { + 'class': "form-control input-sm", + 'keyup': function(e) { + var key = e.which || e.keyCode; + var code = $(e.currentTarget).val(); + + if (key == 13) { + /* ENTER */ + e.preventDefault(); + + that.eval(code); + $(e.currentTarget).val(""); + } + } + }); + + + this.$body.appendTo(this.$container); + this.$input.appendTo(this.$container); + + this.$title.appendTo(this.$el); + this.$container.appendTo(this.$el); + + return this; + }, + + addLine: function(output) { + var $line = $("
      ", { + 'class': "line "+output.type + }); + var $pre = $("
      ", {
      +                'text': output.content
      +            });
      +            var $icon = $("", {
      +                'class': this.logIcons[output.type]
      +            });
      +
      +            $icon.appendTo($line);
      +            $pre.appendTo($line);
      +
      +            $line.appendTo(this.$body);
      +        },
      +
      +        // Eval some code
      +        eval: function(code) {
      +            this.addLine({
      +                type: "input",
      +                content: code
      +            });
      +            return this.dbg.eval(code)
      +            .then(_.bind(this.addLine, this));
      +        },
      +
      +        // Focus the console
      +        focus: function(e) {
      +            if (e) e.preventDefault();
      +            this.$input.focus();
      +        }
      +    });
      +
      +    return ConsoleSection;
      +});
      diff --git a/addons/cb.debug/views/section.js b/addons/cb.debug/views/section.js
      index 2250a3b8..1e81f38a 100644
      --- a/addons/cb.debug/views/section.js
      +++ b/addons/cb.debug/views/section.js
      @@ -6,7 +6,6 @@ define([], function() {
           var DebugSection = hr.View.extend({
               className: "debug-section",
               defaults: {
      -            title: "",
                   dbg: null
               },
               events: {},
      diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js
      index 8e98c62a..e34891b4 100644
      --- a/addons/cb.debug/views/tab.js
      +++ b/addons/cb.debug/views/tab.js
      @@ -4,8 +4,9 @@ define([
           "views/locals",
           "views/backtrace",
           "views/breakpoints",
      +    "views/console",
           "less!stylesheets/tab.less"
      -], function(Debugger, DebugSection, LocalsSection, BacktraceSection, BreakpointsSection) {
      +], function(Debugger, DebugSection, LocalsSection, BacktraceSection, BreakpointsSection, ConsoleSection) {
           var _ = codebox.require("hr/utils");
           var $ = codebox.require("hr/dom");
           var hr = codebox.require("hr/hr");
      @@ -38,6 +39,9 @@ define([
                   });
       
                   // Create sections
      +            this.console = new ConsoleSection({
      +                dbg: this.dbg
      +            });
                   this.locals = new LocalsSection({
                       dbg: this.dbg
                   });
      @@ -48,16 +52,21 @@ define([
                       dbg: this.dbg
                   });
       
      -            // Create grid for sections
      -            this.grid = new GridView({
      +            // Create grids for sections
      +            this.gridV = new GridView({
      +                columns: 1
      +            });
      +            this.gridH = new GridView({
                       columns: 1000
                   });
       
      -            this.grid.addView(this.breakpoints);
      -            this.grid.addView(this.backtrace);
      -            this.grid.addView(this.locals);
      +            this.gridH.addView(this.breakpoints);
      +            this.gridH.addView(this.backtrace);
      +            this.gridH.addView(this.locals);
       
      -            this.grid.appendTo(this);
      +            this.gridV.addView(this.gridH);
      +            this.gridV.addView(this.console);
      +            this.gridV.appendTo(this);
       
       
                   // Base commands: start, stop, next, continue, restart
      diff --git a/package.json b/package.json
      index af9ee912..99f449b1 100644
      --- a/package.json
      +++ b/package.json
      @@ -44,7 +44,7 @@
               "connect-gzip-static": "0.1.1",
               "minimatch": "0.2.14",
               "semver": "2.2.1",
      -        "bugs": "git+https://github.com/FriendCode/bugs.js.git#27ef22296ef946165d31928d106dc4fe9ed8fcd5"
      +        "bugs": "git+https://github.com/FriendCode/bugs.js.git#bd503163abe59feceff4613a6203a779f7a4efff"
           },
           "devDependencies": {
               "codebox-io": "0.3.2",
      
      From 69eca6b9b3bc937b4988a169661855ef26e1d50a Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 16:45:20 -0700
      Subject: [PATCH 149/646] Show debuggers errors in console
      
      ---
       addons/cb.debug/debugger.js  | 20 +++++++++++++++++---
       addons/cb.debug/views/tab.js | 15 ++++++++++++---
       2 files changed, 29 insertions(+), 6 deletions(-)
      
      diff --git a/addons/cb.debug/debugger.js b/addons/cb.debug/debugger.js
      index 13b5f927..f9438803 100644
      --- a/addons/cb.debug/debugger.js
      +++ b/addons/cb.debug/debugger.js
      @@ -39,10 +39,22 @@ define([], function() {
               },
       
               // Execute a RPC requests
      -        execute: function(method, args) {
      +        execute: function(method, args, options) {
      +            var that = this;
      +            options = _.defaults(options || {}, {
      +                error: true
      +            });
      +
                   return rpc.execute("debug/"+method, _.extend(args || {}, {
                       'id': this.id
      -            }));
      +            }))
      +            .then(function(data) {
      +                return data;
      +            },
      +            function(err) {
      +                if (options.error) that.trigger("error", err);
      +                return Q.reject(err);
      +            });
               },
       
               // Get locals
      @@ -151,12 +163,14 @@ define([], function() {
                       that.trigger("update:eval:"+type);
                       return {
                           'type': type,
      -                    'content': data
      +                    'content': data.message || data
                       };
                   };
       
                   return this.execute("eval", {
                       'code': code
      +            }, {
      +                'error': false
                   })
                   .then(
                       _.partial(handle, "log"),
      diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js
      index e34891b4..6fc50828 100644
      --- a/addons/cb.debug/views/tab.js
      +++ b/addons/cb.debug/views/tab.js
      @@ -34,9 +34,7 @@ define([
                   DebugTab.__super__.initialize.apply(this, arguments);
       
                   this.dbg = new Debugger();
      -            this.listenTo(this.dbg, "update", function() {
      -                this.updateState();
      -            });
      +            
       
                   // Create sections
                   this.console = new ConsoleSection({
      @@ -52,6 +50,17 @@ define([
                       dbg: this.dbg
                   });
       
      +            // Listen events
      +            this.listenTo(this.dbg, "update", function() {
      +                this.updateState();
      +            });
      +            this.listenTo(this.dbg, "error", function(err) {
      +                this.console.addLine({
      +                    type: "error",
      +                    content: err.message || err
      +                });
      +            });
      +
                   // Create grids for sections
                   this.gridV = new GridView({
                       columns: 1
      
      From 856e1e2c743b89f5433cc34c935f17600a47e560 Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 16:48:21 -0700
      Subject: [PATCH 150/646] Start debugger even when breakpoints are invalid
      
      ---
       addons/cb.debug/node/service.js | 5 ++++-
       1 file changed, 4 insertions(+), 1 deletion(-)
      
      diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js
      index 9f46231f..94dcff49 100644
      --- a/addons/cb.debug/node/service.js
      +++ b/addons/cb.debug/node/service.js
      @@ -97,7 +97,10 @@ DebugRPCService.prototype.init = function(args, meta) {
                   })
                   .flatten()
                   .map(function(point) {
      -                return that.dbgs[args.id].break(path.join(that.workspace.root, point.path), point.line);
      +                return that.dbgs[args.id].break(path.join(that.workspace.root, point.path), point.line)
      +                .fail(function() {
      +                    return Q();
      +                })
                   })
                   .value()
               );
      
      From 53c64684346832ecb38e4eb98a9f2beb9ffe1e01 Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 16:50:29 -0700
      Subject: [PATCH 151/646] Remove debugger object when closing it
      
      ---
       addons/cb.debug/node/service.js | 6 ++++++
       1 file changed, 6 insertions(+)
      
      diff --git a/addons/cb.debug/node/service.js b/addons/cb.debug/node/service.js
      index 94dcff49..d597bfbb 100644
      --- a/addons/cb.debug/node/service.js
      +++ b/addons/cb.debug/node/service.js
      @@ -115,8 +115,14 @@ DebugRPCService.prototype.init = function(args, meta) {
       
       // Close a debugger
       DebugRPCService.prototype.close = function(args, meta) {
      +    var that = this;
      +
      +    // Kill debugger
           return this._dbg(args.id).kill()
           .then(function() {
      +
      +        // remove debugger session
      +        delete that.dbgs[args.id];
               return {};
           });
       };
      
      From cc6345b7b208ba4531fdfa93e3f52bfd00c71c43 Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 17:18:00 -0700
      Subject: [PATCH 152/646] Improve design of breakpoints in ace
      
      ---
       addons/cb.files.editor/stylesheets/file.less | 17 ++++++++++++++---
       1 file changed, 14 insertions(+), 3 deletions(-)
      
      diff --git a/addons/cb.files.editor/stylesheets/file.less b/addons/cb.files.editor/stylesheets/file.less
      index cac3e176..abc52c1c 100644
      --- a/addons/cb.files.editor/stylesheets/file.less
      +++ b/addons/cb.files.editor/stylesheets/file.less
      @@ -87,9 +87,20 @@
                       border-bottom: 3px solid #0ff;
                   }
       
      -            .ace_gutter-cell.ace_breakpoint{ 
      -                border-radius: 20px 0px 0px 20px; 
      -                box-shadow: 0px 0px 1px 1px red inset; 
      +            .ace_gutter-cell.ace_breakpoint {
      +                position: relative;
      +
      +                &:before {
      +                    content: " ";
      +                    width: 10px;
      +                    height: 10px;
      +                    border-radius: 16px;
      +                    border: 2px solid #fff;
      +                    position: absolute;
      +                    left: 4px;
      +                    background: #5E9EF3;
      +                    top: 3px;
      +                }
                   } 
               }
           }
      
      From c24209e3ba48ee5b34cc46e0c36c96f3f5c1604e Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 17:44:23 -0700
      Subject: [PATCH 153/646] Fix log for rpc method
      
      ---
       core/cb.logger/main.js | 2 +-
       core/cb.rpc/manager.js | 6 ++++--
       2 files changed, 5 insertions(+), 3 deletions(-)
      
      diff --git a/core/cb.logger/main.js b/core/cb.logger/main.js
      index a9c280a0..1759df0d 100644
      --- a/core/cb.logger/main.js
      +++ b/core/cb.logger/main.js
      @@ -24,7 +24,7 @@ function setup(options, imports, register) {
       	var error = _.partial(print, 'error');
       	var log = _.partial(print, 'log');
       	var exception = _.wrap(error, function(func, logEmit, logSection, err, kill) {
      -		func(logEmit, logSection, "Error ", err.message);
      +		func(logEmit, logSection, "Error ", err.message || err);
       		console.error(err.stack);
       
               // Kill process
      diff --git a/core/cb.rpc/manager.js b/core/cb.rpc/manager.js
      index 23598d9f..25fce8de 100644
      --- a/core/cb.rpc/manager.js
      +++ b/core/cb.rpc/manager.js
      @@ -27,6 +27,8 @@ HttpRPCManager.prototype.urlFor = function(serviceName, methodName) {
       
       // Build a handler to handle requests for this specific request
       HttpRPCManager.prototype.methodHandler = function(method, methodUrl, opts) {
      +    var that = this;
      +
           return function _methodHandler(req, res, next) {
               // Extract arguments
               var args = _.clone(req.query);
      @@ -49,6 +51,8 @@ HttpRPCManager.prototype.methodHandler = function(method, methodUrl, opts) {
                       'method': methodUrl,
                   });
               }, function(err) {
      +            that.logger.exception(err, false);
      +
                   // Error response
                   res.send(500, {
                       'ok': false,
      @@ -56,8 +60,6 @@ HttpRPCManager.prototype.methodHandler = function(method, methodUrl, opts) {
                       'code': err.code || 500,
                       'method': methodUrl,
                   });
      -
      -            logger.exception(err, false);
               });
           };
       };
      
      From 4839496a9e46f0fec15a944d94c8b4ece65faf60 Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 17:52:59 -0700
      Subject: [PATCH 154/646] Fix opening of only one limited debug tab
      
      ---
       addons/cb.debug/client.js | 10 +++++++---
       1 file changed, 7 insertions(+), 3 deletions(-)
      
      diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js
      index e7ac8153..39e3bce4 100644
      --- a/addons/cb.debug/client.js
      +++ b/addons/cb.debug/client.js
      @@ -28,13 +28,16 @@ define([
       
                   return Q()
                   .then(function() {
      -                if (debugTab) return dialogs.confirm("Do you wan to close current debugger?",
      +                if (debugTab) {
      +                    return dialogs.confirm("Do you wan to close current debugger?",
                           "There is already a debugger running, are you sure you want to close it for opening a new one.")
      +                    .then(function() {
      +                        return debugTab.closeTab();
      +                    });
      +                }
                       return Q();
                   })
                   .then(function() {
      -                if (debugTab) debugTab.closeTab();
      -
                       // Create debug tab
                       debugTab = tabs.add(DebugTab, {
                           'path': file.path()
      @@ -43,6 +46,7 @@ define([
                           'section': "debug"
                       });
                       debugTab.on("tab:close", function() {
      +                    console.log("close debug tab", debugTab);
                           debugTab = null;
                       });
       
      
      From 90cc777cd336b56aae1457a243176b550d007d9a Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 18:12:12 -0700
      Subject: [PATCH 155/646] Add settings for debugger argument and tool
      
      ---
       addons/cb.debug/client.js    |  5 +++--
       addons/cb.debug/settings.js  | 21 ++++++++++++++++++++-
       addons/cb.debug/views/tab.js |  2 +-
       3 files changed, 24 insertions(+), 4 deletions(-)
      
      diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js
      index 39e3bce4..e3c107ae 100644
      --- a/addons/cb.debug/client.js
      +++ b/addons/cb.debug/client.js
      @@ -40,13 +40,14 @@ define([
                   .then(function() {
                       // Create debug tab
                       debugTab = tabs.add(DebugTab, {
      -                    'path': file.path()
      +                    'path': file.path(),
      +                    'tool': settings.user.get("tool") == "auto" ? null : settings.user.get("tool"),
      +                    'argument': settings.user.get("argument")
                       }, {
                           'type': "debug",
                           'section': "debug"
                       });
                       debugTab.on("tab:close", function() {
      -                    console.log("close debug tab", debugTab);
                           debugTab = null;
                       });
       
      diff --git a/addons/cb.debug/settings.js b/addons/cb.debug/settings.js
      index ceb8a0a2..ac2be6bc 100644
      --- a/addons/cb.debug/settings.js
      +++ b/addons/cb.debug/settings.js
      @@ -6,13 +6,32 @@ define([], function() {
               'namespace': "debug",
               'title': "Debug",
               'defaults': {
      -            'path': ""
      +            'path': "",
      +            'argument': null,
      +            'tool': "auto"
               },
               'fields': {
                   'path': {
                       'label': 'File Path',
                       'type': "text",
                       'help': "Click left on a file and select 'debug' to update this file."
      +            },
      +            'argument': {
      +                'label': 'Arguments',
      +                'type': "text",
      +                'help': "Arguments to run the file with."
      +            },
      +            'tool': {
      +                'label': 'Debugger',
      +                'type': "select",
      +                'help': "Force the use of a specific debugger.",
      +                'options': {
      +                    'auto': "Auto",
      +                    'pdb': "Python Debugger",
      +                    'gdb': "Native Debugger (GDB)",
      +                    'jdb': "Java Debugger",
      +                    'rdb': "Ruby Debugger"
      +                }
                   }
               }
           });
      diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js
      index 6fc50828..27d6370c 100644
      --- a/addons/cb.debug/views/tab.js
      +++ b/addons/cb.debug/views/tab.js
      @@ -82,7 +82,7 @@ define([
                   this.commandStart = new Command({}, {
                       title: "Start",
                       action: function() {
      -                    that.dbg.start();
      +                    that.dbg.start(that.options.argument);
                       }
                   });
                   this.commandStop = new Command({}, {
      
      From 2ee86c3dfadbc7507568344054f7b86f9a4a707f Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 18:40:14 -0700
      Subject: [PATCH 156/646] Switch some on to listenTo
      
      ---
       client/models/command.js | 4 ++--
       client/models/file.js    | 8 ++++----
       2 files changed, 6 insertions(+), 6 deletions(-)
      
      diff --git a/client/models/command.js b/client/models/command.js
      index cb66dd46..f85bda28 100644
      --- a/client/models/command.js
      +++ b/client/models/command.js
      @@ -65,8 +65,8 @@ define([
                   this.menu.reset(this.get("menu", []));
       
                   if (this.get("offline") !== null) {
      -                hr.Offline.on("state", function() {
      -                    that.toggleFlag("disabled", that.get("offline") == hr.Offline.isConnected())
      +                this.listenTo(hr.Offline, "state", function() {
      +                    this.toggleFlag("disabled", this.get("offline") == hr.Offline.isConnected());
                       });
                       that.toggleFlag("disabled", that.get("offline") == hr.Offline.isConnected())
                   }
      diff --git a/client/models/file.js b/client/models/file.js
      index 22ce3376..1e4f9091 100644
      --- a/client/models/file.js
      +++ b/client/models/file.js
      @@ -57,7 +57,7 @@ define([
                   }, this);
       
                   // Listen to codebox event
      -            this.codebox.on("box:watch:change", function(e) {
      +            this.listenTo(this.codebox, "box:watch:change", function(e) {
                       // Event on this file itself
                       if (e.data.path == this.path()) {
                           this.trigger("file:change:"+e.data.change, e.data);
      @@ -67,12 +67,12 @@ define([
                       if (_.contains(["create", "delete"], e.data.change) && this.isChild(e.data.path)) {
                           this.trigger("files:change:"+e.data.change, e.data);
                       }
      -            }, this);
      -            this.codebox.on("box:files:write", function(e) {
      +            });
      +            this.listenTo(this.codebox, "box:files:write", function(e) {
                       if (e.data.path == this.path()) {
                           this.trigger("file:write", e.data);
                       }
      -            }, this);
      +            });
                   return this;
               },
       
      
      From c574e49f12c88688e5d491105b64173cb9b595d2 Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 19:41:26 -0700
      Subject: [PATCH 157/646] Fix design for resize bar in debug console
      
      ---
       addons/cb.debug/stylesheets/tab.less | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less
      index 76cb7361..9a26eded 100644
      --- a/addons/cb.debug/stylesheets/tab.less
      +++ b/addons/cb.debug/stylesheets/tab.less
      @@ -1,6 +1,6 @@
       .addon-debugger-tab {
           .component-grid .grid-section {
      -        .grid-resize-bar-h {
      +        .grid-resize-bar-h, .grid-resize-bar-v {
                   background: rgba(0,0,0, 0.1);
               }
           }
      
      From c381754873b48bc82876171715cd485b2ffe1ac5 Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Samy=20Pess=C3=A9?= 
      Date: Tue, 11 Mar 2014 19:49:33 -0700
      Subject: [PATCH 158/646] Improve prompt design on debugger console
      
      ---
       addons/cb.debug/stylesheets/tab.less | 88 ++++++++++++++--------------
       addons/cb.debug/views/console.js     |  5 +-
       2 files changed, 49 insertions(+), 44 deletions(-)
      
      diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less
      index 9a26eded..9fe0f75e 100644
      --- a/addons/cb.debug/stylesheets/tab.less
      +++ b/addons/cb.debug/stylesheets/tab.less
      @@ -37,56 +37,58 @@
               }
       
               &.debug-console {
      -            .console-body {
      -                .line {
      -                    padding-bottom: 3px;
      -                    border-bottom: 1px solid rgba(0,0,0, 0.05);
      +            .line {
      +                padding-bottom: 3px;
      +                border-bottom: 1px solid rgba(0,0,0, 0.05);
      +
      +                * {
      +                    line-height: 1.4em;
      +                    font-size: 14px;
      +                }
       
      -                    * {
      -                        line-height: 1.4em;
      -                        font-size: 14px;
      -                    }
      +                i {
      +                    float: left;
      +                    width: 24px;
      +                    text-align: center;
      +                }
       
      -                    i {
      -                        float: left;
      -                        width: 24px;
      -                        text-align: center;
      -                    }
      +                pre {
      +                    margin: 0px;
      +                    padding: 0px;
      +                    background: none;
      +                    color: inherit;
      +                    border: none;
      +                    display: block;
      +                    margin-left: 24px;
      +                    font-family: inherit;
      +                }
       
      -                    pre {
      -                        margin: 0px;
      -                        padding: 0px;
      -                        background: none;
      -                        color: inherit;
      -                        border: none;
      -                        display: block;
      -                        margin-left: 24px;
      -                        font-family: inherit;
      -                    }
      +                &.error {
      +                    color: #a94442;
      +                }
      +                &.input {
      +                    padding-top: 3px;
      +                    padding-bottom: 0px;
      +                    border-color: transparent;
      +                }
       
      -                    &.error {
      -                        color: #a94442;
      -                    }
      -                    &.input {
      -                        padding-top: 3px;
      -                        padding-bottom: 0px;
      -                        border-color: transparent;
      -                    }
      +                input, input:focus {
      +                    color: inherit;
      +                    background: transparent;
      +                    box-shadow: none;
      +                    font-size: 14px;
      +                    line-height: 1.4em;
      +                    padding: 0px;
      +                    margin: 0px;
      +                    height: 19px;
      +                    margin-right: 24px;
      +                    width: 90%;
      +                    border: none;
                       }
                   }
       
      -            input, input:focus {
      -                color: inherit;
      -                background: transparent;
      -                border-color: transparent;
      -                box-shadow: none;
      -                font-size: 14px;
      -                line-height: 1.4em;
      -                padding: 0px;
      -                margin: 0px;
      -                height: 23px;
      -                padding-left: 24px;
      -                position: relative;
      +            .console-body {
      +                
                   }
               }
           }
      diff --git a/addons/cb.debug/views/console.js b/addons/cb.debug/views/console.js
      index a3177bc1..9c5b58d2 100644
      --- a/addons/cb.debug/views/console.js
      +++ b/addons/cb.debug/views/console.js
      @@ -55,7 +55,10 @@ define([], function() {
       
                   
                   this.$body.appendTo(this.$container);
      -            this.$input.appendTo(this.$container);
      +            this.$input.appendTo($("
      ", { + 'class': "line input", + 'html': '' + }).appendTo(this.$container)); this.$title.appendTo(this.$el); this.$container.appendTo(this.$el); From 7b6a5d8c580fcd2bd305e053be4c1da80f91da4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 19:58:34 -0700 Subject: [PATCH 159/646] Scroll console body in debugger --- addons/cb.debug/views/console.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/cb.debug/views/console.js b/addons/cb.debug/views/console.js index 9c5b58d2..718740f5 100644 --- a/addons/cb.debug/views/console.js +++ b/addons/cb.debug/views/console.js @@ -81,6 +81,9 @@ define([], function() { $pre.appendTo($line); $line.appendTo(this.$body); + + // Scroll + this.$container.animate({ scrollTop: this.$container[0].scrollHeight}, 100); }, // Eval some code From f4faebfa1252f207438f41291b2aff00bb918c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 20:21:30 -0700 Subject: [PATCH 160/646] Add link for files in debugger --- addons/cb.debug/stylesheets/tab.less | 4 ++++ addons/cb.debug/views/backtrace.js | 3 ++- addons/cb.debug/views/breakpoints.js | 3 ++- addons/cb.debug/views/section.js | 24 +++++++++++++++++++++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less index 9fe0f75e..f25aeb6b 100644 --- a/addons/cb.debug/stylesheets/tab.less +++ b/addons/cb.debug/stylesheets/tab.less @@ -32,6 +32,10 @@ tr { td, th { border-top-color: rgba(0,0,0, 0.05); + + a { + color: inherit; + } } } } diff --git a/addons/cb.debug/views/backtrace.js b/addons/cb.debug/views/backtrace.js index c755dced..906dc216 100644 --- a/addons/cb.debug/views/backtrace.js +++ b/addons/cb.debug/views/backtrace.js @@ -11,7 +11,8 @@ define([ formats: [ { id: "filename", - title: "File" + title: "File", + type: "file" }, { id: "line", diff --git a/addons/cb.debug/views/breakpoints.js b/addons/cb.debug/views/breakpoints.js index 781cece2..15a16211 100644 --- a/addons/cb.debug/views/breakpoints.js +++ b/addons/cb.debug/views/breakpoints.js @@ -15,7 +15,8 @@ define([ }, { id: "filename", - title: "File" + title: "File", + type: "file" }, { id: "line", diff --git a/addons/cb.debug/views/section.js b/addons/cb.debug/views/section.js index 1e81f38a..9fd4cb38 100644 --- a/addons/cb.debug/views/section.js +++ b/addons/cb.debug/views/section.js @@ -3,6 +3,8 @@ define([], function() { var $ = codebox.require("hr/dom"); var hr = codebox.require("hr/hr"); + var files = codebox.require("core/files"); + var DebugSection = hr.View.extend({ className: "debug-section", defaults: { @@ -63,9 +65,25 @@ define([], function() { var $line = $("

      "); _.each(this.formats, function(key) { - $("
      ", { - 'text': item[key.id] - }).appendTo($line); + var $e; + var value = item[key.id]; + + if (key.type == "file") { + $e = $("", { + 'href': "#", + 'text': value, + 'click': function(e) { + e.preventDefault(); + files.open(value); + } + }) + } else { + $e = $("", { + 'text': value + }); + } + + $e.appendTo($("").appendTo($line)); }); $line.appendTo(this.$table); }, From 24f06ce946bfe4c9fcf36186b62fe8ffd7b4bda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 11 Mar 2014 20:21:44 -0700 Subject: [PATCH 161/646] Add log of debugger to the console --- addons/cb.debug/debugger.js | 15 ++++++++++----- addons/cb.debug/views/tab.js | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/addons/cb.debug/debugger.js b/addons/cb.debug/debugger.js index f9438803..a0b77022 100644 --- a/addons/cb.debug/debugger.js +++ b/addons/cb.debug/debugger.js @@ -114,7 +114,8 @@ define([], function() { return this.execute("start", { 'arg': arg }) - .then(function() { + .then(function(output) { + that.trigger("log", output); that.trigger("update:start"); }); }, @@ -123,7 +124,8 @@ define([], function() { stop: function() { var that = this; return this.execute("stop") - .then(function() { + .then(function(output) { + that.trigger("log", output); that.trigger("update:stop"); }); }, @@ -132,7 +134,8 @@ define([], function() { next: function() { var that = this; return this.execute("next") - .then(function() { + .then(function(output) { + that.trigger("log", output); that.trigger("update:next"); }); }, @@ -141,7 +144,8 @@ define([], function() { cont: function() { var that = this; return this.execute("cont") - .then(function() { + .then(function(output) { + that.trigger("log", output); that.trigger("update:cont"); }); }, @@ -150,7 +154,8 @@ define([], function() { restart: function() { var that = this; return this.execute("restart") - .then(function() { + .then(function(output) { + that.trigger("log", output); that.trigger("update:restart"); }); }, diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js index 27d6370c..15ca78bf 100644 --- a/addons/cb.debug/views/tab.js +++ b/addons/cb.debug/views/tab.js @@ -60,6 +60,12 @@ define([ content: err.message || err }); }); + this.listenTo(this.dbg, "log", function(message) { + this.console.addLine({ + type: "log", + content: message + }); + }); // Create grids for sections this.gridV = new GridView({ From d22c19e135635b12087a32d0d2f7cf7c85d5f57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 12 Mar 2014 18:13:40 -0700 Subject: [PATCH 162/646] Remove desktop application source code --- Gruntfile.js | 134 +- desktop/credits.html | 34 - desktop/css/style.css | 124 - desktop/icons/128.png | Bin 2487 -> 0 bytes desktop/icons/folder.png | Bin 209 -> 0 bytes desktop/icons/mac.icns | Bin 127226 -> 0 bytes desktop/ide.html | 27 - desktop/index.html | 25 - desktop/js/dirname.js | 4 - desktop/js/ide.js | 84 - desktop/js/jquery.js | 8837 ------------------------------------ desktop/js/main.js | 327 -- desktop/package.json | 18 - package.json | 3 - scripts/build_extras.sh | 40 - scripts/build_git.sh | 41 - scripts/build_linux_tar.sh | 7 - scripts/build_mac_dmg.sh | 8 - scripts/install_linux.sh | 95 - scripts/nwbuild.sh | 16 - 20 files changed, 3 insertions(+), 9821 deletions(-) delete mode 100644 desktop/credits.html delete mode 100644 desktop/css/style.css delete mode 100644 desktop/icons/128.png delete mode 100644 desktop/icons/folder.png delete mode 100644 desktop/icons/mac.icns delete mode 100644 desktop/ide.html delete mode 100644 desktop/index.html delete mode 100644 desktop/js/dirname.js delete mode 100644 desktop/js/ide.js delete mode 100644 desktop/js/jquery.js delete mode 100644 desktop/js/main.js delete mode 100644 desktop/package.json delete mode 100755 scripts/build_extras.sh delete mode 100755 scripts/build_git.sh delete mode 100755 scripts/build_linux_tar.sh delete mode 100755 scripts/build_mac_dmg.sh delete mode 100755 scripts/install_linux.sh delete mode 100755 scripts/nwbuild.sh diff --git a/Gruntfile.js b/Gruntfile.js index 72270b41..85d45d09 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -13,15 +13,14 @@ module.exports = function (grunt) { // Load grunt modules grunt.loadNpmTasks('hr.js'); grunt.loadNpmTasks('grunt-exec'); - grunt.loadNpmTasks('grunt-node-webkit-builder'); grunt.loadNpmTasks('grunt-contrib-compress'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-copy'); // Init GRUNT configuraton grunt.initConfig({ - 'pkg': grunt.file.readJSON('package.json'), - 'hr': { + pkg: grunt.file.readJSON('package.json'), + hr: { build: { // Base directory for the application "base": clientPath, @@ -98,55 +97,7 @@ module.exports = function (grunt) { } } }, - nodewebkit: { - mac: { - options: { - build_dir: './appBuilds', - mac: true, - win: false, - linux32: false, - linux64: false, - mac_icns: "./desktop/icons/mac.icns", - credits: "./desktop/credits.html", - version: NW_VERSION, - zip: false - }, - src: [ - ".tmp/**", - - // grunt.file.copy duplicates symbolic and hard links - // so we need to copy it with the shell - "!.tmp/extras/**", - ] - }, - linux: { - options: { - build_dir: './appBuilds', - mac: false, - win: false, - linux32: true, - linux64: false, - version: NW_VERSION, - zip: false - }, - src: [ - ".tmp/**" - ] - } - }, exec: { - nwbuild: { - command: "./scripts/nwbuild.sh "+NW_VERSION, - cwd: '.tmp/', - stdout: true, - stderr: true - }, - build_extras: { - command: "./scripts/build_extras.sh", - cwd: '.tmp/', - stdout: true, - stderr: true - }, publish: { command: "npm publish", cwd: '.tmp/', @@ -159,25 +110,6 @@ module.exports = function (grunt) { stdout: true, stderr: true }, - copy_extras: { - // Copy and preserve symbolic links - command: "mv .tmp/extras ./appBuilds/releases/Codebox/mac/Codebox.app/Contents/Resources/app.nw/extras", - cwd: '.', - stdout: true, - stderr: true - }, - build_mac_release: { - command: "./scripts/build_mac_dmg.sh", - cwd: './', - stdout: true, - stderr: true - }, - build_linux_release: { - command: "./scripts/build_linux_tar.sh", - cwd: './', - stdout: true, - stderr: true - }, clean_addons: { command: "rm -rf */**/addon-built.js", cwd: '.', @@ -233,38 +165,7 @@ module.exports = function (grunt) { options: { mode: true } - }, - - // Change the package.json to use node-webkit's - desktopPKG: { - cwd: './', - src: '.tmp/desktop/package.json', - dest: '.tmp/package.json', - options: { - // Change main entry point - process: function (content, srcpath) { - grunt.log.write('processing '+ srcpath + '...\n'); - var _pkg = JSON.parse(content); - _pkg.main = "desktop/index.html"; - _pkg.version = pkg.version; - return JSON.stringify(_pkg, null, 4); - } - } - }, - - // Installer for linux - linuxInstaller: { - cwd: './', - src: 'scripts/install_linux.sh', - dest: './appBuilds/releases/Codebox/linux32/Codebox/install.sh' - }, - - // Installer for linux - linuxIcon: { - cwd: './', - src: './desktop/icons/128.png', - dest: './appBuilds/releases/Codebox/linux32/Codebox/icon.png' - }, + } }, compress: { tmp: { @@ -344,35 +245,6 @@ module.exports = function (grunt) { 'compress:tmp' ]); - // Desktop app generation - grunt.registerTask('build-app-mac', [ - 'tmp', - 'copy:desktopPKG', - 'exec:nwbuild', - 'exec:build_extras', - 'nodewebkit:mac', - 'exec:copy_extras', - 'clean:tmp', - 'exec:build_mac_release' - ]); - grunt.registerTask('build-app-linux', [ - 'tmp', - 'copy:desktopPKG', - 'exec:nwbuild', - 'nodewebkit:linux', - 'copy:linuxInstaller', - 'copy:linuxIcon', - 'clean:tmp', - 'exec:build_linux_release' - ]); - - // Desktop app test - grunt.registerTask('testApps', [ - 'tmp', - 'copy:desktopPKG', - 'exec:nwbuild' - ]); - // Publish to NPM grunt.registerTask('publish', [ 'tmp', diff --git a/desktop/credits.html b/desktop/credits.html deleted file mode 100644 index 10a1fb6f..00000000 --- a/desktop/credits.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - -

      Contact information

      -

      - E-mail: contact@friendco.de
      - Website: www.codebox.io -

      - -

      License

      -

      - Copyright 2014 FriendCode Inc. -

      -

      - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

      -

      http://www.apache.org/licenses/LICENSE-2.0

      -

      - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -

      - - \ No newline at end of file diff --git a/desktop/css/style.css b/desktop/css/style.css deleted file mode 100644 index edc10d0e..00000000 --- a/desktop/css/style.css +++ /dev/null @@ -1,124 +0,0 @@ -body { - font-family: "Lucida Grande", "Lucida Sans Unicode", sans-serif; - font-size: 14px; - line-height: 1.428571429; - color: #111; - background: #EDEDED; -} - -* { - box-sizing: border-box; - margin: 0px; - padding: 0px; -} - -/* Body */ -#body { - position: fixed; - top: 0px; - bottom: 76px; - right: 0px; - left: 0px; - padding: 5px 10px; - overflow-y: auto; - background: #EDEDED; - border-bottom: 1px solid #959595; -} - -/* Alert */ -.alert { - color: #8a6d3b; - background-color: #fcf8e3; - border: 1px solid #eadbbc; - padding: 15px; - margin-bottom: 20px; - border-radius: 4px; - cursor: pointer; -} - -/* List of projects */ -#projects { - list-style: none; -} -#projects .empty-message { - text-transform: uppercase;; - text-align: center; - margin: 150px 20px; - font-size: 18px; - color: #ccc; -} - -#projects .project { - display: block; - background: #f5f5f5; - box-shadow: 0px 1px #fff inset; - border: 1px solid #ccc; - color: #333; - padding: 10px; - width: 100%; - height: 70px; - margin: 5px 0px; - overflow: hidden; - cursor: pointer; - color: #888; - border-radius: 4px; - font-size: 12px; -} -#projects .project:hover { - -} -#projects .project .project-title { - text-transform: uppercase; - font-size: 20px; - color: #555; - margin-bottom: 5px; -} -#projects .project .project-icon { - float: left; - margin: 9px 10px 0px 2px; - width: 32px; -} -#projects .project .project-path { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -} -#projects hr { - margin: 20px 10px; - border: 0px; - border-bottom: 1px solid #d8d8d8; - display: block; -} - -#footer { - position: fixed; - bottom: 0px; - right: 0px; - left: 0px; - text-align: center; -} - -#footer .btn { - margin: 10px; - display: inline-block; - padding: 12px 10px; - text-align: center; - font-size: 30px; - line-height: 30px; - color: #000; - border-radius: 4px; - border: 1px solid #9a9a9a; - background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0, #EBEBEB),color-stop(0.5, #ECECEC),color-stop(0.5, #F3F3F3),color-stop(1, #FFFFFF)); - box-shadow: inset 0 1px 0 rgba(255,255,255,1), 0 1px 0 rgba(0,0,0,0.09); - text-decoration: none; - font-weight: bold; - color: #555; -} - -#footer #open-new { - width: 62%; -} - -#footer #open-menu { - width: 20%; -} diff --git a/desktop/icons/128.png b/desktop/icons/128.png deleted file mode 100644 index 594c7c1da97d0a420f51a6a4539e9f235cc4cb64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2487 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 Tv_^)dJC2DZwfC~?fYtv2&!d4~ diff --git a/desktop/ide.html b/desktop/ide.html deleted file mode 100644 index 5c3cf157..00000000 --- a/desktop/ide.html +++ /dev/null @@ -1,27 +0,0 @@ - - - Codebox - - - - - - \ No newline at end of file diff --git a/desktop/index.html b/desktop/index.html deleted file mode 100644 index 23e8bdf9..00000000 --- a/desktop/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - Codebox - - - - - - -
      - - - -
        -
        - - - - - - - \ No newline at end of file diff --git a/desktop/js/dirname.js b/desktop/js/dirname.js deleted file mode 100644 index e9a58480..00000000 --- a/desktop/js/dirname.js +++ /dev/null @@ -1,4 +0,0 @@ -// We need to use this hack -// to workaround a shortcoming of node-webkit -exports.dirname = __dirname; - diff --git a/desktop/js/ide.js b/desktop/js/ide.js deleted file mode 100644 index e69aa64a..00000000 --- a/desktop/js/ide.js +++ /dev/null @@ -1,84 +0,0 @@ -// Port allocation -var gui = require('nw.gui'); -var lodash = require('lodash'); -var qClass = require('qpatch').qClass; -var harbor = qClass(require('harbor')); -var ports = new harbor(19000, 20000); - -// Local requires -var codebox = require('../index.js'); - -var win = gui.Window.get(); - -var startCodebox = function(path) { - console.log("start ide in ", path); - - // Claim port - return ports.claim(path) - .then(function(port) { - // Setup url - var url = "http://localhost:"+port; - return [port, url]; - }) - .spread(function (port, url) { - return codebox.start({ - 'root': path, - 'server': { - 'port': port - }, - 'addons': { - 'blacklist': ["cb.offline"] - } - }) - .then(function(box) { - return url+"?_t="+Date.now(); - }); - }) - .then(function(url) { - $("body").append(""); - }); -}; - -var queryString = function () { - // This function is anonymous, is executed immediately and - // the return value is assigned to QueryString! - var query_string = {}; - var query = window.location.search.substring(1); - var vars = query.split("&"); - for (var i=0;i type pairs - class2type = {}, - - // List of deleted data cache ids, so we can reuse them - core_deletedIds = [], - - core_version = "2.0.1", - - // Save a reference to some core methods - core_concat = core_deletedIds.concat, - core_push = core_deletedIds.push, - core_slice = core_deletedIds.slice, - core_indexOf = core_deletedIds.indexOf, - core_toString = class2type.toString, - core_hasOwn = class2type.hasOwnProperty, - core_trim = core_version.trim, - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Used for matching numbers - core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, - - // Used for splitting on whitespace - core_rnotwhite = /\S+/g, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }, - - // The ready event handler and self cleanup method - completed = function() { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - jQuery.ready(); - }; - -jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: core_version, - - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return core_slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; - }, - - slice: function() { - return this.pushStack( core_slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), - - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger("ready").off("ready"); - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray, - - isWindow: function( obj ) { - return obj != null && obj === obj.window; - }, - - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - if ( obj == null ) { - return String( obj ); - } - // Support: Safari <= 5.1 (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ core_toString.call(obj) ] || "object" : - typeof obj; - }, - - isPlainObject: function( obj ) { - // Not plain objects: - // - Any object or value whose internal [[Class]] property is not "[object Object]" - // - DOM nodes - // - window - if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - // Support: Firefox <20 - // The try/catch suppresses exceptions thrown when attempting to access - // the "constructor" property of certain host objects, ie. |window.location| - // https://bugzilla.mozilla.org/show_bug.cgi?id=814622 - try { - if ( obj.constructor && - !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - } catch ( e ) { - return false; - } - - // If the function hasn't returned already, we're confident that - // |obj| is a plain object, created by {} or constructed with new Object - return true; - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // keepScripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, keepScripts ) { - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - keepScripts = context; - context = false; - } - context = context || document; - - var parsed = rsingleTag.exec( data ), - scripts = !keepScripts && []; - - // Single tag - if ( parsed ) { - return [ context.createElement( parsed[1] ) ]; - } - - parsed = jQuery.buildFragment( [ data ], context, scripts ); - - if ( scripts ) { - jQuery( scripts ).remove(); - } - - return jQuery.merge( [], parsed.childNodes ); - }, - - parseJSON: JSON.parse, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE9 - try { - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - globalEval: function( code ) { - var script, - indirect = eval; - - code = jQuery.trim( code ); - - if ( code ) { - // If the code includes a valid, prologue position - // strict mode pragma, execute code by injecting a - // script tag into the document. - if ( code.indexOf("use strict") === 1 ) { - script = document.createElement("script"); - script.text = code; - document.head.appendChild( script ).parentNode.removeChild( script ); - } else { - // Otherwise, avoid the DOM node creation, insertion - // and removal by using an indirect global eval - indirect( code ); - } - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); - - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } - } - - return obj; - }, - - trim: function( text ) { - return text == null ? "" : core_trim.call( text ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - core_push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : core_indexOf.call( arr, elem, i ); - }, - - merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; - - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var retVal, - ret = [], - i = 0, - length = elems.length; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return core_concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = core_slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - length = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, - - now: Date.now, - - // A method for quickly swapping in/out CSS properties to get correct calculations. - // Note: this method belongs to the css module but it's needed here for the support module. - // If support gets modularized, this method should be moved back to the css module. - swap: function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - } -}); - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - } - } - return readyList.promise( obj ); -}; - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -function isArraylike( obj ) { - var length = obj.length, - type = jQuery.type( obj ); - - if ( jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || type !== "function" && - ( length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj ); -} - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); -/*! - * Sizzle CSS Selector Engine v1.9.4-pre - * http://sizzlejs.com/ - * - * Copyright 2013 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2013-05-15 - */ -(function( window, undefined ) { - -var i, - support, - cachedruns, - Expr, - getText, - isXML, - compile, - outermostContext, - sortInput, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - hasDuplicate = false, - sortOrder = function() { return 0; }, - - // General-purpose constants - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - - // Prefer arguments quoted, - // then not containing pseudos/brackets, - // then attribute selectors/non-parenthetical expressions, - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rsibling = new RegExp( whitespace + "*[+~]" ), - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rescape = /'|\\/g, - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - // BMP codepoint - high < 0 ? - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }; - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - - context = context || document; - results = results || []; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } - - if ( documentIsHTML && !seed ) { - - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && context.parentNode || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * For feature detection - * @param {Function} fn The function to test for native support - */ -function isNative( fn ) { - return rnative.test( fn + "" ); -} - -/** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key += " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ -function assert( fn ) { - var div = document.createElement("div"); - - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); - } - // release memory in IE - div = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied if the test fails - * @param {Boolean} test The result of a test. If true, null will be set as the handler in leiu of the specified handler - */ -function addHandle( attrs, handler, test ) { - attrs = attrs.split("|"); - var current, - i = attrs.length, - setHandle = test ? null : handler; - - while ( i-- ) { - // Don't override a user's handler - if ( !(current = Expr.attrHandle[ attrs[i] ]) || current === handler ) { - Expr.attrHandle[ attrs[i] ] = setHandle; - } - } -} - -/** - * Fetches boolean attributes by node - * @param {Element} elem - * @param {String} name - */ -function boolHandler( elem, name ) { - // XML does not need to be checked as this will not be assigned for XML documents - var val = elem.getAttributeNode( name ); - return val && val.specified ? - val.value : - elem[ name ] === true ? name.toLowerCase() : null; -} - -/** - * Fetches attributes without interpolation - * http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx - * @param {Element} elem - * @param {String} name - */ -function interpolationHandler( elem, name ) { - // XML does not need to be checked as this will not be assigned for XML documents - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); -} - -/** - * Uses defaultValue to retrieve value in IE6/7 - * @param {Element} elem - * @param {String} name - */ -function valueHandler( elem ) { - // Ignore the value *property* on inputs by using defaultValue - // Fallback to Sizzle.attr by returning undefined where appropriate - // XML does not need to be checked as this will not be assigned for XML documents - if ( elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns Returns -1 if a precedes b, 1 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Detect xml - * @param {Element|Object} elem An element or a document - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var doc = node ? node.ownerDocument || node : preferredDoc; - - // If no document and documentElement is available, return - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML( doc ); - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) - support.attributes = assert(function( div ) { - - // Support: IE<8 - // Prevent attribute/property "interpolation" - div.innerHTML = ""; - addHandle( "type|href|height|width", interpolationHandler, div.firstChild.getAttribute("href") === "#" ); - - // Support: IE<9 - // Use getAttributeNode to fetch booleans when getAttribute lies - addHandle( booleans, boolHandler, div.getAttribute("disabled") == null ); - - div.className = "i"; - return !div.getAttribute("className"); - }); - - // Support: IE<9 - // Retrieving value should defer to defaultValue - support.input = assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; - }); - - // IE6/7 still return empty string for value, - // but are actually retrieving the property - addHandle( "value", valueHandler, support.attributes && support.input ); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = assert(function( div ) { - div.innerHTML = "
        "; - - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = "i"; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName("i").length === 2; - }); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; - }); - - // ID find and filter - if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; - - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = isNative(doc.querySelectorAll)) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - - // Support: Opera 10-12/IE8 - // ^= $= *= and empty values - // Should not select anything - // Support: Windows 8 Native Apps - // The type attribute is restricted during .innerHTML assignment - var input = doc.createElement("input"); - input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "t", "" ); - - if ( div.querySelectorAll("[t^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = isNative( (matches = docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) - // Detached nodes confoundingly follow *each other* - support.sortDetached = assert(function( div1 ) { - // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition( doc.createElement("div") ) & 1; - }); - - // Document order sorting - sortOrder = docElem.compareDocumentPosition ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); - - if ( compare ) { - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === doc || contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } - - // Not directly comparable, sort on existence of method - return a.compareDocumentPosition ? -1 : 1; - } : - function( a, b ) { - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Parentless nodes are either documents or disconnected - } else if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return doc; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, document, null, [elem] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = ( fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined ); - - return val === undefined ? - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null : - val; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[5] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] && match[4] !== undefined ) { - match[2] = match[4]; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( tokens = [] ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -} - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var data, cache, outerCache, - dirkey = dirruns + " " + doneName; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { - if ( (data = cache[1]) === true || data === cachedruns ) { - return data === true; - } - } else { - cache = outerCache[ dir ] = [ dirkey ]; - cache[1] = matcher( elem, context, xml ) || cachedruns; - if ( cache[1] === true ) { - return true; - } - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - // A counter to specify which element is currently being matched - var matcherCachedRuns = 0, - bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, expandContext ) { - var elem, j, matcher, - setMatched = [], - matchedCount = 0, - i = "0", - unmatched = seed && [], - outermost = expandContext != null, - contextBackup = outermostContext, - // We must always have either seed elements or context - elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); - - if ( outermost ) { - outermostContext = context !== document && context; - cachedruns = matcherCachedRuns; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - for ( ; (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - cachedruns = ++matcherCachedRuns; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !group ) { - group = tokenize( selector ); - } - i = group.length; - while ( i-- ) { - cached = matcherFromTokens( group[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - } - return cached; -}; - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function select( selector, context, results, seed ) { - var i, tokens, token, type, find, - match = tokenize( selector ); - - if ( !seed ) { - // Try to minimize operations if there is only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - } - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && context.parentNode || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - } - - // Compile and execute a filtering function - // Provide `match` to avoid retokenization if we modified the selector above - compile( selector, match )( - seed, - context, - !documentIsHTML, - results, - rsibling.test( selector ) - ); - return results; -} - -// Deprecated -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Initialize against the default document -setDocument(); - -// Support: Chrome<<14 -// Always assume duplicates if they aren't passed to the comparison function -[0, 0].sort( sortOrder ); -support.detectDuplicates = hasDuplicate; - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - -})( window ); -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; - }); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); - }, - // Remove all callbacks from the list - empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( list && ( !fired || stack ) ) { - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var action = tuple[ 0 ], - fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); - return this; - }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = core_slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; - if( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); -jQuery.support = (function( support ) { - var input = document.createElement("input"), - fragment = document.createDocumentFragment(), - div = document.createElement("div"), - select = document.createElement("select"), - opt = select.appendChild( document.createElement("option") ); - - // Finish early in limited environments - if ( !input.type ) { - return support; - } - - input.type = "checkbox"; - - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere) - support.checkOn = input.value !== ""; - - // Must access the parent to make an option select properly - // Support: IE9, IE10 - support.optSelected = opt.selected; - - // Will be defined later - support.reliableMarginRight = true; - support.boxSizingReliable = true; - support.pixelPosition = false; - - // Make sure checked status is properly cloned - // Support: IE9, IE10 - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Check if an input maintains its value after becoming a radio - // Support: IE9, IE10 - input = document.createElement("input"); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; - - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "checked", "t" ); - input.setAttribute( "name", "t" ); - - fragment.appendChild( input ); - - // Support: Safari 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: Firefox, Chrome, Safari - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - support.focusinBubbles = "onfocusin" in window; - - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - // Run tests that need a body at doc ready - jQuery(function() { - var container, marginDiv, - // Support: Firefox, Android 2.3 (Prefixed box-sizing versions). - divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box", - body = document.getElementsByTagName("body")[ 0 ]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } - - container = document.createElement("div"); - container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; - - // Check box-sizing and margin behavior. - body.appendChild( container ).appendChild( div ); - div.innerHTML = ""; - // Support: Firefox, Android 2.3 (Prefixed box-sizing versions). - div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%"; - - // Workaround failing boxSizing test due to offsetWidth returning wrong value - // with some non-1 values of body zoom, ticket #13543 - jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { - support.boxSizing = div.offsetWidth === 4; - }); - - // Use window.getComputedStyle because jsdom on node.js will break without it. - if ( window.getComputedStyle ) { - support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; - support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; - - // Support: Android 2.3 - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. (#3333) - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - marginDiv = div.appendChild( document.createElement("div") ); - marginDiv.style.cssText = div.style.cssText = divReset; - marginDiv.style.marginRight = marginDiv.style.width = "0"; - div.style.width = "1px"; - - support.reliableMarginRight = - !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); - } - - body.removeChild( container ); - }); - - return support; -})( {} ); - -/* - Implementation Summary - - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ -var data_user, data_priv, - rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rmultiDash = /([A-Z])/g; - -function Data() { - // Support: Android < 4, - // Old WebKit does not have Object.preventExtensions/freeze method, - // return new empty object instead with no [[set]] accessor - Object.defineProperty( this.cache = {}, 0, { - get: function() { - return {}; - } - }); - - this.expando = jQuery.expando + Math.random(); -} - -Data.uid = 1; - -Data.accepts = function( owner ) { - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType ? - owner.nodeType === 1 || owner.nodeType === 9 : true; -}; - -Data.prototype = { - key: function( owner ) { - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return the key for a frozen object. - if ( !Data.accepts( owner ) ) { - return 0; - } - - var descriptor = {}, - // Check if the owner object already has a cache key - unlock = owner[ this.expando ]; - - // If not, create one - if ( !unlock ) { - unlock = Data.uid++; - - // Secure it in a non-enumerable, non-writable property - try { - descriptor[ this.expando ] = { value: unlock }; - Object.defineProperties( owner, descriptor ); - - // Support: Android < 4 - // Fallback to a less secure definition - } catch ( e ) { - descriptor[ this.expando ] = unlock; - jQuery.extend( owner, descriptor ); - } - } - - // Ensure the cache object - if ( !this.cache[ unlock ] ) { - this.cache[ unlock ] = {}; - } - - return unlock; - }, - set: function( owner, data, value ) { - var prop, - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - // Handle: [ owner, key, value ] args - if ( typeof data === "string" ) { - cache[ data ] = value; - - // Handle: [ owner, { properties } ] args - } else { - // Fresh assignments by object are shallow copied - if ( jQuery.isEmptyObject( cache ) ) { - jQuery.extend( this.cache[ unlock ], data ); - // Otherwise, copy the properties one-by-one to the cache object - } else { - for ( prop in data ) { - cache[ prop ] = data[ prop ]; - } - } - } - return cache; - }, - get: function( owner, key ) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. A valid owner object must be provided. - var cache = this.cache[ this.key( owner ) ]; - - return key === undefined ? - cache : cache[ key ]; - }, - access: function( owner, key, value ) { - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ((key && typeof key === "string") && value === undefined) ) { - return this.get( owner, key ); - } - - // [*]When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, name, camel, - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - if ( key === undefined ) { - this.cache[ unlock ] = {}; - - } else { - // Support array or space separated string of keys - if ( jQuery.isArray( key ) ) { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = key.concat( key.map( jQuery.camelCase ) ); - } else { - camel = jQuery.camelCase( key ); - // Try the string as a key before any manipulation - if ( key in cache ) { - name = [ key, camel ]; - } else { - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - name = camel; - name = name in cache ? - [ name ] : ( name.match( core_rnotwhite ) || [] ); - } - } - - i = name.length; - while ( i-- ) { - delete cache[ name[ i ] ]; - } - } - }, - hasData: function( owner ) { - return !jQuery.isEmptyObject( - this.cache[ owner[ this.expando ] ] || {} - ); - }, - discard: function( owner ) { - if ( owner[ this.expando ] ) { - delete this.cache[ owner[ this.expando ] ]; - } - } -}; - -// These may be used throughout the jQuery core codebase -data_user = new Data(); -data_priv = new Data(); - - -jQuery.extend({ - acceptData: Data.accepts, - - hasData: function( elem ) { - return data_user.hasData( elem ) || data_priv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return data_user.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - data_user.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to data_priv methods, these can be deprecated. - _data: function( elem, name, data ) { - return data_priv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - data_priv.remove( elem, name ); - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var attrs, name, - elem = this[ 0 ], - i = 0, - data = null; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = data_user.get( elem ); - - if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { - attrs = elem.attributes; - for ( ; i < attrs.length; i++ ) { - name = attrs[ i ].name; - - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - data_priv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - data_user.set( this, key ); - }); - } - - return jQuery.access( this, function( value ) { - var data, - camelKey = jQuery.camelCase( key ); - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - // Attempt to get data from the cache - // with the key as-is - data = data_user.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to get data from the cache - // with the key camelized - data = data_user.get( elem, camelKey ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, camelKey, undefined ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each(function() { - // First, attempt to store a copy or reference of any - // data that might've been store with a camelCased key. - var data = data_user.get( this, camelKey ); - - // For HTML5 data-* attribute interop, we have to - // store property names with dashes in a camelCase form. - // This might not apply to all properties...* - data_user.set( this, camelKey, value ); - - // *... In the case of properties that might _actually_ - // have dashes, we need to also store a copy of that - // unchanged property. - if ( key.indexOf("-") !== -1 && data !== undefined ) { - data_user.set( this, key, value ); - } - }); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each(function() { - data_user.remove( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? JSON.parse( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - data_user.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = data_priv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray( data ) ) { - queue = data_priv.access( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - hooks.cur = fn; - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return data_priv.get( elem, key ) || data_priv.access( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - data_priv.remove( elem, [ type + "queue", key ] ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while( i-- ) { - tmp = data_priv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var nodeHook, boolHook, - rclass = /[\t\r\n\f]/g, - rreturn = /\r/g, - rfocusable = /^(?:input|select|textarea|button)$/i; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each(function() { - delete this[ jQuery.propFix[ name ] || name ]; - }); - }, - - addClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call( this, j, this.className ) ); - }); - } - - if ( proceed ) { - // The disjunction here is for better compressibility (see removeClass) - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - " " - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - elem.className = jQuery.trim( cur ); - - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = arguments.length === 0 || typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call( this, j, this.className ) ); - }); - } - if ( proceed ) { - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - "" - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - elem.className = value ? jQuery.trim( cur ) : ""; - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.match( core_rnotwhite ) || []; - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - // Toggle whole class name - } else if ( type === core_strundefined || type === "boolean" ) { - if ( this.className ) { - // store className if set - data_priv.set( this, "__className__", this.className ); - } - - // If the element has a class name or if we're passed "false", - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, option, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one" || index < 0, - values = one ? null : [], - max = one ? index + 1 : options.length, - i = index < 0 ? - max : - one ? index : 0; - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // IE6-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - // Don't return options that are disabled or in a disabled optgroup - ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && - ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { - optionSet = true; - } - } - - // force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attr: function( elem, name, value ) { - var hooks, ret, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === core_strundefined ) { - return jQuery.prop( elem, name, value ); - } - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - - } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, value + "" ); - return value; - } - - } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var name, propName, - i = 0, - attrNames = value && value.match( core_rnotwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( (name = attrNames[i++]) ) { - propName = jQuery.propFix[ name ] || name; - - // Boolean attributes get special treatment (#10870) - if ( jQuery.expr.match.bool.test( name ) ) { - // Set corresponding property to false - elem[ propName ] = false; - } - - elem.removeAttribute( name ); - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to default in case type is set after value during creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? - ret : - ( elem[ name ] = value ); - - } else { - return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? - ret : - elem[ name ]; - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? - elem.tabIndex : - -1; - } - } - } -}); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { - var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; - - jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) { - var fn = jQuery.expr.attrHandle[ name ], - ret = isXML ? - undefined : - /* jshint eqeqeq: false */ - // Temporarily disable this handler to check existence - (jQuery.expr.attrHandle[ name ] = undefined) != - getter( elem, name, isXML ) ? - - name.toLowerCase() : - null; - - // Restore handler - jQuery.expr.attrHandle[ name ] = fn; - - return ret; - }; -}); - -// Support: IE9+ -// Selectedness for an option in an optgroup can be inaccurate -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - } - }; -} - -jQuery.each([ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -}); - -// Radios and checkboxes getter/setter -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }; - if ( !jQuery.support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - // Support: Webkit - // "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - }; - } -}); -var rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { - events = elemData.events = {}; - } - if ( !(eventHandle = elemData.handle) ) { - eventHandle = elemData.handle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( core_rnotwhite ) || [""]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.hasData( elem ) && data_priv.get( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( core_rnotwhite ) || [""]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - data_priv.remove( elem, "events" ); - } - }, - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = core_hasOwn.call( event, "type" ) ? event.type : event, - namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { - event.preventDefault(); - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); - - var i, j, ret, matched, handleObj, - handlerQueue = [], - args = core_slice.call( arguments ), - handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, matches, sel, handleObj, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matches[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } - - return handlerQueue; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; - - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: Cordova 2.5 (WebKit) (#13255) - // All events should have a target; Cordova deviceready doesn't - if ( !event.target ) { - event.target = document; - } - - // Support: Safari 6.0+, Chrome < 28 - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined ) { - event.originalEvent.returnValue = event.result; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -jQuery.removeEvent = function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } -}; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = ( src.defaultPrevented || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && e.preventDefault ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && e.stopPropagation ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - } -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -// Support: Chrome 15+ -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// Create "bubbling" focus and blur events -// Support: Firefox, Chrome, Safari -if ( !jQuery.support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -}); -var isSimple = /^.[^:#\[\.,]*$/, - rparentsprev = /^(?:parents|prev(?:Until|All))/, - rneedsContext = jQuery.expr.match.needsContext, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend({ - find: function( selector ) { - var i, - ret = [], - self = this, - len = self.length; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }) ); - } - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; - }, - - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter(function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); - }, - - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = ( rneedsContext.test( selectors ) || typeof selectors !== "string" ) ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { - - cur = matched.push( cur ); - break; - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return core_indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return core_indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( jQuery.unique(all) ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -function sibling( cur, dir ) { - while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} - - return cur; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.unique( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - })); - }, - - dir: function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; - }, - - sibling: function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ - return !!qualifier.call( elem, i, elem ) !== not; - }); - - } - - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); - - } - - if ( typeof qualifier === "string" ) { - if ( isSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); - } - - return jQuery.grep( elements, function( elem ) { - return ( core_indexOf.call( qualifier, elem ) >= 0 ) !== not; - }); -} -var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rhtml = /<|&#?\w+;/, - rnoInnerhtml = /<(?:script|style|link)/i, - manipulation_rcheckableType = /^(?:checkbox|radio)$/i, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /^$|\/(?:java|ecma)script/i, - rscriptTypeMasked = /^true\/(.*)/, - rcleanScript = /^\s*\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - - // Support: IE 9 - option: [ 1, "" ], - - thead: [ 1, "", "
        " ], - col: [ 2, "", "
        " ], - tr: [ 2, "", "
        " ], - td: [ 3, "", "
        " ], - - _default: [ 0, "", "" ] - }; - -// Support: IE 9 -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -jQuery.fn.extend({ - text: function( value ) { - return jQuery.access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) ); - }, null, value, arguments.length ); - }, - - append: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - }); - }, - - before: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - // keepData is for internal use only--do not document - remove: function( selector, keepData ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function () { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return jQuery.access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var - // Snapshot the DOM in case .domManip sweeps something relevant into its fragment - args = jQuery.map( this, function( elem ) { - return [ elem.nextSibling, elem.parentNode ]; - }), - i = 0; - - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - var next = args[ i++ ], - parent = args[ i++ ]; - - if ( parent ) { - // Don't use the snapshot next if it has moved (#13810) - if ( next && next.parentNode !== parent ) { - next = this.nextSibling; - } - jQuery( this ).remove(); - parent.insertBefore( elem, next ); - } - // Allow new content to include elements from the context set - }, true ); - - // Force removal if there was no new content (e.g., from empty arguments) - return i ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback, allowIntersection ) { - - // Flatten any nested arrays - args = core_concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - self.domManip( args, callback, allowIntersection ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Hope ajax is available... - jQuery._evalUrl( node.src ); - } else { - jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); - } - } - } - } - } - } - - return this; - } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: QtWebKit - // .get() because core_push.apply(_, arraylike) throws - core_push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -}); - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); - - // Support: IE >= 9 - // Fix Cloning issues - if ( !jQuery.support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - buildFragment: function( elems, context, scripts, selection ) { - var elem, tmp, tag, wrap, contains, j, - i = 0, - l = elems.length, - fragment = context.createDocumentFragment(), - nodes = []; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.firstChild; - } - - // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Fixes #12346 - // Support: Webkit, IE - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; - }, - - cleanData: function( elems ) { - var data, elem, events, type, key, j, - special = jQuery.event.special, - i = 0; - - for ( ; (elem = elems[ i ]) !== undefined; i++ ) { - if ( Data.accepts( elem ) ) { - key = elem[ data_priv.expando ]; - - if ( key && (data = data_priv.cache[ key ]) ) { - events = Object.keys( data.events || {} ); - if ( events.length ) { - for ( j = 0; (type = events[j]) !== undefined; j++ ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - if ( data_priv.cache[ key ] ) { - // Discard any remaining `private` data - delete data_priv.cache[ key ]; - } - } - } - // Discard any remaining `user` data - delete data_user.cache[ elem[ data_user.expando ] ]; - } - }, - - _evalUrl: function( url ) { - return jQuery.ajax({ - url: url, - type: "GET", - dataType: "script", - async: false, - global: false, - "throws": true - }); - } -}); - -// Support: 1.x compatibility -// Manipulating tables requires a tbody -function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? - - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute("type"); - } - - return elem; -} - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var l = elems.length, - i = 0; - - for ( ; i < l; i++ ) { - data_priv.set( - elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) - ); - } -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( data_priv.hasData( src ) ) { - pdataOld = data_priv.access( src ); - pdataCur = data_priv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( data_user.hasData( src ) ) { - udataOld = data_user.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - data_user.set( dest, udataCur ); - } -} - - -function getAll( context, tag ) { - var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : - context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : - []; - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], ret ) : - ret; -} - -// Support: IE >= 9 -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} -jQuery.fn.extend({ - wrapAll: function( html ) { - var wrap; - - if ( jQuery.isFunction( html ) ) { - return this.each(function( i ) { - jQuery( this ).wrapAll( html.call(this, i) ); - }); - } - - if ( this[ 0 ] ) { - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map(function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - }).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function( i ) { - jQuery( this ).wrapInner( html.call(this, i) ); - }); - } - - return this.each(function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - }); - }, - - wrap: function( html ) { - var isFunction = jQuery.isFunction( html ); - - return this.each(function( i ) { - jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); - }); - }, - - unwrap: function() { - return this.parent().each(function() { - if ( !jQuery.nodeName( this, "body" ) ) { - jQuery( this ).replaceWith( this.childNodes ); - } - }).end(); - } -}); -var curCSS, iframe, - // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" - // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rmargin = /^margin/, - rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), - rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), - rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), - elemdisplay = { BODY: "block" }, - - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: 0, - fontWeight: 400 - }, - - cssExpand = [ "Top", "Right", "Bottom", "Left" ], - cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; - -// return a css property mapped to a potentially vendor prefixed property -function vendorPropName( style, name ) { - - // shortcut for names that are not vendor prefixed - if ( name in style ) { - return name; - } - - // check for vendor prefixed names - var capName = name.charAt(0).toUpperCase() + name.slice(1), - origName = name, - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in style ) { - return name; - } - } - - return origName; -} - -function isHidden( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); -} - -// NOTE: we've included the "window" in window.getComputedStyle -// because jsdom on node.js will break without it. -function getStyles( elem ) { - return window.getComputedStyle( elem, null ); -} - -function showHide( elements, show ) { - var display, elem, hidden, - values = [], - index = 0, - length = elements.length; - - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - values[ index ] = data_priv.get( elem, "olddisplay" ); - display = elem.style.display; - if ( show ) { - // Reset the inline display of this element to learn if it is - // being hidden by cascaded rules or not - if ( !values[ index ] && display === "none" ) { - elem.style.display = ""; - } - - // Set elements which have been overridden with display: none - // in a stylesheet to whatever the default browser style is - // for such an element - if ( elem.style.display === "" && isHidden( elem ) ) { - values[ index ] = data_priv.access( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); - } - } else { - - if ( !values[ index ] ) { - hidden = isHidden( elem ); - - if ( display && display !== "none" || !hidden ) { - data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css(elem, "display") ); - } - } - } - } - - // Set the display of most of the elements in a second loop - // to avoid the constant reflow - for ( index = 0; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - if ( !show || elem.style.display === "none" || elem.style.display === "" ) { - elem.style.display = show ? values[ index ] || "" : "none"; - } - } - - return elements; -} - -jQuery.fn.extend({ - css: function( name, value ) { - return jQuery.access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( jQuery.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - }, - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - var bool = typeof state === "boolean"; - - return this.each(function() { - if ( bool ? state : isHidden( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - }); - } -}); - -jQuery.extend({ - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - "columnCount": true, - "fillOpacity": true, - "fontWeight": true, - "lineHeight": true, - "opacity": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: { - // normalize float css property - "float": "cssFloat" - }, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = jQuery.camelCase( name ), - style = elem.style; - - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // convert relative number strings (+= or -=) to relative numbers. #7345 - if ( type === "string" && (ret = rrelNum.exec( value )) ) { - value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); - // Fixes bug #9237 - type = "number"; - } - - // Make sure that NaN and null values aren't set. See: #7116 - if ( value == null || type === "number" && isNaN( value ) ) { - return; - } - - // If a number was passed in, add 'px' to the (except for certain CSS properties) - if ( type === "number" && !jQuery.cssNumber[ origName ] ) { - value += "px"; - } - - // Fixes #8908, it can be done more correctly by specifying setters in cssHooks, - // but it would mean to define eight (for every problematic property) identical functions - if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { - style[ name ] = value; - } - - } else { - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = jQuery.camelCase( name ); - - // Make sure that we're working with the right name - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - //convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Return, converting to number if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; - } - return val; - } -}); - -curCSS = function( elem, name, _computed ) { - var width, minWidth, maxWidth, - computed = _computed || getStyles( elem ), - - // Support: IE9 - // getPropertyValue is only needed for .css('filter') in IE9, see #12537 - ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, - style = elem.style; - - if ( computed ) { - - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { - ret = jQuery.style( elem, name ); - } - - // Support: Safari 5.1 - // A tribute to the "awesome hack by Dean Edwards" - // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels - // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values - if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret; -}; - - -function setPositiveNumber( elem, value, subtract ) { - var matches = rnumsplit.exec( value ); - return matches ? - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : - value; -} - -function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { - var i = extra === ( isBorderBox ? "border" : "content" ) ? - // If we already have the right measurement, avoid augmentation - 4 : - // Otherwise initialize for horizontal or vertical properties - name === "width" ? 1 : 0, - - val = 0; - - for ( ; i < 4; i += 2 ) { - // both box models exclude margin, so add it if we want it - if ( extra === "margin" ) { - val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); - } - - if ( isBorderBox ) { - // border-box includes padding, so remove it if we want content - if ( extra === "content" ) { - val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // at this point, extra isn't border nor margin, so remove border - if ( extra !== "margin" ) { - val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } else { - // at this point, extra isn't content, so add padding - val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // at this point, extra isn't content nor padding, so add border - if ( extra !== "padding" ) { - val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - return val; -} - -function getWidthOrHeight( elem, name, extra ) { - - // Start with offset property, which is equivalent to the border-box value - var valueIsBorderBox = true, - val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - styles = getStyles( elem ), - isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // some non-html elements return undefined for offsetWidth, so check for null/undefined - // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 - // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 - if ( val <= 0 || val == null ) { - // Fall back to computed then uncomputed css if necessary - val = curCSS( elem, name, styles ); - if ( val < 0 || val == null ) { - val = elem.style[ name ]; - } - - // Computed unit is not pixels. Stop here and return. - if ( rnumnonpx.test(val) ) { - return val; - } - - // we need the check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); - - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - } - - // use the active box-sizing model to add/subtract irrelevant styles - return ( val + - augmentWidthOrHeight( - elem, - name, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles - ) - ) + "px"; -} - -// Try to determine the default display value of an element -function css_defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - // Use the already-created iframe if possible - iframe = ( iframe || - jQuery(" + \ No newline at end of file diff --git a/addons/cb.files.preview/views/preview.js b/addons/cb.files.preview/views/preview.js new file mode 100644 index 00000000..11085297 --- /dev/null +++ b/addons/cb.files.preview/views/preview.js @@ -0,0 +1,19 @@ +define([ + "text!templates/preview.html", + "less!stylesheets/preview.less" +], function(templateFile) { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var hr = codebox.require("hr/hr"); + var Dialogs = codebox.require("utils/dialogs"); + var FilesBaseView = codebox.require("views/files/base"); + + var PreviewView = FilesBaseView.extend({ + className: "addon-files-previewviewer", + templateLoader: "text", + template: templateFile, + events: {} + }); + + return PreviewView; +}); \ No newline at end of file From 7d5caaab9a7d5671a924bcfb6230fedfe3e77fb4 Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Wed, 14 May 2014 10:29:35 +0200 Subject: [PATCH 298/646] Added icon to preview menu entry --- addons/cb.files.preview/client.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/cb.files.preview/client.js b/addons/cb.files.preview/client.js index d3468ee1..78adc544 100644 --- a/addons/cb.files.preview/client.js +++ b/addons/cb.files.preview/client.js @@ -10,6 +10,7 @@ define([ files.addHandler("preview", { name: "Preview", + icon: "eye", position: 10, View: PreviewView, valid: function(file) { From a1c0ed8e686c8ab56719b60abe85250f213700cf Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Wed, 14 May 2014 10:30:19 +0200 Subject: [PATCH 299/646] Added automatic refreshing of open preview tabs on file save --- addons/cb.files.preview/views/preview.js | 40 +++++++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/addons/cb.files.preview/views/preview.js b/addons/cb.files.preview/views/preview.js index 11085297..84b831f5 100644 --- a/addons/cb.files.preview/views/preview.js +++ b/addons/cb.files.preview/views/preview.js @@ -4,15 +4,45 @@ define([ ], function(templateFile) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var Dialogs = codebox.require("utils/dialogs"); - var FilesBaseView = codebox.require("views/files/base"); + var box = codebox.require("core/box"); + var FilesTabView = codebox.require("views/files/tab"); - var PreviewView = FilesBaseView.extend({ + var PreviewView = FilesTabView.extend({ className: "addon-files-previewviewer", templateLoader: "text", template: templateFile, - events: {} + events: {}, + + initialize: function() { + PreviewView.__super__.initialize.apply(this, arguments); + var that = this; + + // add refresh menu option + this.tab.menu.menuSection([ + { + 'type': "action", + 'title': "Refresh", + 'shortcuts': [ + "mod+r" + ], + 'bindKeyboard': true, + 'action': function() { + that.refresh(); + } + } + ]); + + // bind save event + box.on("box:watch:change:update", function() { + that.refresh(); + }, this); + + return this; + }, + + refresh: function() { + $(this.$el).find("iframe").attr('src', function ( i, val ) { return val; }); + } }); return PreviewView; From dc680dcb9e1e734a7a5b9dbb8c40ead7345c8500 Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Wed, 14 May 2014 10:46:26 +0200 Subject: [PATCH 300/646] Added Reload-On-Save to settings --- addons/cb.files.preview/settings.js | 17 +++++++++++++++++ addons/cb.files.preview/views/preview.js | 9 ++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 addons/cb.files.preview/settings.js diff --git a/addons/cb.files.preview/settings.js b/addons/cb.files.preview/settings.js new file mode 100644 index 00000000..d1b4a1fa --- /dev/null +++ b/addons/cb.files.preview/settings.js @@ -0,0 +1,17 @@ +define([], function() { + var settings = codebox.require("core/settings"); + + return settings.add({ + 'namespace': "preview", + 'title': "Preview", + 'defaults': { + 'refresh': true + }, + 'fields': { + 'refresh': { + 'label': "Reload on Save", + 'type': "checkbox" + } + } + }); +}); \ No newline at end of file diff --git a/addons/cb.files.preview/views/preview.js b/addons/cb.files.preview/views/preview.js index 84b831f5..eff2fdbe 100644 --- a/addons/cb.files.preview/views/preview.js +++ b/addons/cb.files.preview/views/preview.js @@ -1,7 +1,8 @@ define([ + "../settings", "text!templates/preview.html", "less!stylesheets/preview.less" -], function(templateFile) { +], function(settings, templateFile) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var box = codebox.require("core/box"); @@ -34,7 +35,9 @@ define([ // bind save event box.on("box:watch:change:update", function() { - that.refresh(); + if (settings.user.get("refresh")) { + that.refresh(); + } }, this); return this; @@ -46,4 +49,4 @@ define([ }); return PreviewView; -}); \ No newline at end of file +}); From b71109b789f9214e43370483b79a1388bf24aa02 Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Thu, 15 May 2014 12:20:46 +0200 Subject: [PATCH 301/646] Renamed preview to html_preview --- addons/cb.files.preview/client.js | 6 +++--- .../templates/{preview.html => preview_html.html} | 0 .../cb.files.preview/views/{preview.js => preview_html.js} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename addons/cb.files.preview/templates/{preview.html => preview_html.html} (100%) rename addons/cb.files.preview/views/{preview.js => preview_html.js} (96%) diff --git a/addons/cb.files.preview/client.js b/addons/cb.files.preview/client.js index 78adc544..3053f925 100644 --- a/addons/cb.files.preview/client.js +++ b/addons/cb.files.preview/client.js @@ -1,6 +1,6 @@ define([ - "views/preview" -], function(PreviewView) { + "views/preview_html" +], function(PreviewHtml) { var _ = codebox.require("hr/utils"); var files = codebox.require("core/files"); @@ -12,7 +12,7 @@ define([ name: "Preview", icon: "eye", position: 10, - View: PreviewView, + View: PreviewHtml, valid: function(file) { return (!file.isDirectory() && _.contains(htmlExts, file.extension())); } diff --git a/addons/cb.files.preview/templates/preview.html b/addons/cb.files.preview/templates/preview_html.html similarity index 100% rename from addons/cb.files.preview/templates/preview.html rename to addons/cb.files.preview/templates/preview_html.html diff --git a/addons/cb.files.preview/views/preview.js b/addons/cb.files.preview/views/preview_html.js similarity index 96% rename from addons/cb.files.preview/views/preview.js rename to addons/cb.files.preview/views/preview_html.js index eff2fdbe..9fc20ef9 100644 --- a/addons/cb.files.preview/views/preview.js +++ b/addons/cb.files.preview/views/preview_html.js @@ -1,6 +1,6 @@ define([ - "../settings", - "text!templates/preview.html", + "settings", + "text!templates/preview_html.html", "less!stylesheets/preview.less" ], function(settings, templateFile) { var _ = codebox.require("hr/utils"); From ae235938a737d8da3fb0ba38a3b67302522ef1f8 Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Thu, 15 May 2014 12:21:11 +0200 Subject: [PATCH 302/646] Added dependency for markdown-js --- addons/cb.files.preview/package.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/cb.files.preview/package.json b/addons/cb.files.preview/package.json index b55c2d7e..9c5dea70 100644 --- a/addons/cb.files.preview/package.json +++ b/addons/cb.files.preview/package.json @@ -1,19 +1,22 @@ { "name": "cb.files.preview", "version": "0.0.1", - "title": "HTML Preview", - "description": "Adds option to preview file in a new tab.", + "title": "HTML / Markdown Preview", + "description": "Adds option to preview files in a new tab.", "homepage": "https://github.com/invokr/codebox", "license": "Apache", "author": { "name": "Robin Dietrich", "email": "me@invokr.org", - "url": "" + "url": "https://github.com/invokr" }, "client": { "main": "client" }, "engines": { "codebox": ">=0.7.0" + }, + "dependencies": { + "markdown": "git+https://github.com/evilstreak/markdown-js" } } From 6fead0768f360d21e29ac93100f71b44e513359e Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Thu, 15 May 2014 13:09:18 +0200 Subject: [PATCH 303/646] Added markdown preview handler --- addons/cb.files.preview/client.js | 19 ++++++- .../templates/preview_markdown.html | 1 + .../views/preview_markdown.js | 56 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 addons/cb.files.preview/templates/preview_markdown.html create mode 100644 addons/cb.files.preview/views/preview_markdown.js diff --git a/addons/cb.files.preview/client.js b/addons/cb.files.preview/client.js index 3053f925..42cd79c4 100644 --- a/addons/cb.files.preview/client.js +++ b/addons/cb.files.preview/client.js @@ -1,6 +1,7 @@ define([ - "views/preview_html" -], function(PreviewHtml) { + "views/preview_html", + "views/preview_markdown" +], function(PreviewHtml, PreviewMarkdown) { var _ = codebox.require("hr/utils"); var files = codebox.require("core/files"); @@ -17,4 +18,18 @@ define([ return (!file.isDirectory() && _.contains(htmlExts, file.extension())); } }); + + var markdownExts = [ + ".md", ".markdown", ".txt" + ]; + + files.addHandler("preview-markdown", { + name: "Preview", + icon: "eye", + position: 10, + View: PreviewMarkdown, + valid: function(file) { + return (!file.isDirectory() && _.contains(markdownExts, file.extension())); + } + }); }); \ No newline at end of file diff --git a/addons/cb.files.preview/templates/preview_markdown.html b/addons/cb.files.preview/templates/preview_markdown.html new file mode 100644 index 00000000..6595a5bb --- /dev/null +++ b/addons/cb.files.preview/templates/preview_markdown.html @@ -0,0 +1 @@ +
        \ No newline at end of file diff --git a/addons/cb.files.preview/views/preview_markdown.js b/addons/cb.files.preview/views/preview_markdown.js new file mode 100644 index 00000000..e7756b23 --- /dev/null +++ b/addons/cb.files.preview/views/preview_markdown.js @@ -0,0 +1,56 @@ +define([ + "settings", + "node_modules/markdown/src/markdown", + "text!templates/preview_markdown.html", + "less!stylesheets/preview.less" +], function(settings, markdown, templateFile) { + var _ = codebox.require("hr/utils"); + var $ = codebox.require("hr/dom"); + var box = codebox.require("core/box"); + var FilesTabView = codebox.require("views/files/tab"); + + var MarkdownView = FilesTabView.extend({ + className: "addon-files-previewviewer", + templateLoader: "text", + template: templateFile, + events: {}, + + initialize: function() { + MarkdownView.__super__.initialize.apply(this, arguments); + var that = this; + + // add refresh menu option + this.tab.menu.menuSection([ + { + 'type': "action", + 'title': "Refresh", + 'shortcuts': [ + "mod+r" + ], + 'bindKeyboard': true, + 'action': function() { + that.refresh(); + } + } + ]); + + this.model.on("file:change:update", function() { + if (settings.user.get("refresh")) { + that.refresh(); + } + }, this); + + this.refresh(); + return this; + }, + + refresh: function() { + var that = this; + this.model.download().then(function (content) { + that.$el.html("
        "+markdown.toHTML(content)+"
        "); + }); + } + }); + + return MarkdownView; +}); From 606f4e70f37acadd4dd7e0e2457e3d0ca76dd8fd Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Thu, 15 May 2014 13:10:04 +0200 Subject: [PATCH 304/646] Adjusted CSS to accommodate markdown changes --- addons/cb.files.preview/stylesheets/preview.less | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/addons/cb.files.preview/stylesheets/preview.less b/addons/cb.files.preview/stylesheets/preview.less index 0d1f11b5..bc878aa7 100644 --- a/addons/cb.files.preview/stylesheets/preview.less +++ b/addons/cb.files.preview/stylesheets/preview.less @@ -1,8 +1,19 @@ .addon-files-previewviewer { + height: 100%; + iframe { border: 0px; width: 100%; height: 100%; background-color: #fff; } + + .markdown { + border: 0px; + width: 100%; + height: 100%; + background-color: #fff; + margin-top: 1px; + padding: 3px; + } } \ No newline at end of file From df716743da50b61cc4ac8871124026fc4b1779d6 Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Thu, 15 May 2014 13:36:48 +0200 Subject: [PATCH 305/646] Updated preview FileHandler names --- addons/cb.files.preview/client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/cb.files.preview/client.js b/addons/cb.files.preview/client.js index 42cd79c4..d6c621ba 100644 --- a/addons/cb.files.preview/client.js +++ b/addons/cb.files.preview/client.js @@ -10,7 +10,7 @@ define([ ]; files.addHandler("preview", { - name: "Preview", + name: "HTML Preview", icon: "eye", position: 10, View: PreviewHtml, @@ -24,7 +24,7 @@ define([ ]; files.addHandler("preview-markdown", { - name: "Preview", + name: "Markdown Preview", icon: "eye", position: 10, View: PreviewMarkdown, From ce7889668aac755ecf1bba9a303dff490239be82 Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Thu, 15 May 2014 15:49:39 +0200 Subject: [PATCH 306/646] Added option to strip trailing spaces on save --- addons/cb.files.editor/editor/settings.js | 5 +++++ addons/cb.files.editor/editor/view.js | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/addons/cb.files.editor/editor/settings.js b/addons/cb.files.editor/editor/settings.js index 1765a6b2..61e696dd 100644 --- a/addons/cb.files.editor/editor/settings.js +++ b/addons/cb.files.editor/editor/settings.js @@ -15,6 +15,7 @@ define([], function() { 'wraplimitrange': 80, 'enablesoftwrap': false, 'enablesofttabs': true, + 'stripspaces': false, 'autocollaboration': true, 'tabsize': 4, 'keyboard': "textinput" @@ -74,6 +75,10 @@ define([], function() { 'label': "Use Soft Tabs", 'type': "checkbox" }, + 'stripspaces': { + 'label': "Strip Whitespaces", + 'type': "checkbox" + }, 'tabsize': { 'label': "Tab Size", 'type': "number", diff --git a/addons/cb.files.editor/editor/view.js b/addons/cb.files.editor/editor/view.js index d6c1b358..1defa14c 100644 --- a/addons/cb.files.editor/editor/view.js +++ b/addons/cb.files.editor/editor/view.js @@ -321,11 +321,11 @@ define([ this.markersC[cId] = this.editor.getSession().addMarker(range, "marker-cursor marker-"+c.color.replace("#", ""), function(html, range, left, top, config){ html.push("
        " + "
         "+name+" 
        " - + "
         
        "); + + " "); }, true); }, this); - // Participant selection + // Participant selection this.sync.on("selection:move", function(cId, c) { var range = new aceRange.Range(c.start.y, c.start.x, c.end.y, c.end.x); if (this.markersS[cId]) this.editor.getSession().removeMarker(this.markersS[cId]); @@ -510,6 +510,19 @@ define([ // (action) Save file saveFile: function(e) { if (e) e.preventDefault(); + + if (editorSettings.user.get("stripspaces")) { + // strip all whitespaces from the current document + var doc = this.editor.session.doc; + var lines = doc.getAllLines(); + + for (var i = 0; i < lines.length; ++i) { + var index = lines[i].search(/\s+$/); + if (index !== -1 && index != 0) + doc.removeInLine(i, index, lines[i].length); + } + } + this.sync.save(); }, @@ -555,4 +568,4 @@ define([ }); return FileEditorView; -}); \ No newline at end of file +}); From a87856f6644a4be484f11494a8b43fd29d4c9ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 16 May 2014 23:54:00 +0200 Subject: [PATCH 307/646] Fix markdown previewer dependency --- addons/cb.files.preview/package.json | 2 +- addons/cb.files.preview/views/preview_markdown.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/cb.files.preview/package.json b/addons/cb.files.preview/package.json index 9c5dea70..a9f506a6 100644 --- a/addons/cb.files.preview/package.json +++ b/addons/cb.files.preview/package.json @@ -17,6 +17,6 @@ "codebox": ">=0.7.0" }, "dependencies": { - "markdown": "git+https://github.com/evilstreak/markdown-js" + "markdown": "0.5.0" } } diff --git a/addons/cb.files.preview/views/preview_markdown.js b/addons/cb.files.preview/views/preview_markdown.js index e7756b23..8ab7eba4 100644 --- a/addons/cb.files.preview/views/preview_markdown.js +++ b/addons/cb.files.preview/views/preview_markdown.js @@ -1,6 +1,6 @@ define([ "settings", - "node_modules/markdown/src/markdown", + "node_modules/markdown/lib/markdown", "text!templates/preview_markdown.html", "less!stylesheets/preview.less" ], function(settings, markdown, templateFile) { @@ -14,11 +14,11 @@ define([ templateLoader: "text", template: templateFile, events: {}, - + initialize: function() { MarkdownView.__super__.initialize.apply(this, arguments); var that = this; - + // add refresh menu option this.tab.menu.menuSection([ { @@ -33,17 +33,17 @@ define([ } } ]); - + this.model.on("file:change:update", function() { if (settings.user.get("refresh")) { that.refresh(); } }, this); - + this.refresh(); return this; }, - + refresh: function() { var that = this; this.model.download().then(function (content) { From 530b00a71b5d68d119992ce122fd5e193a443fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sat, 17 May 2014 00:00:30 +0200 Subject: [PATCH 308/646] Fix markdown previewer --- addons/cb.files.preview/views/preview_markdown.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/cb.files.preview/views/preview_markdown.js b/addons/cb.files.preview/views/preview_markdown.js index 8ab7eba4..7fe530f5 100644 --- a/addons/cb.files.preview/views/preview_markdown.js +++ b/addons/cb.files.preview/views/preview_markdown.js @@ -3,12 +3,14 @@ define([ "node_modules/markdown/lib/markdown", "text!templates/preview_markdown.html", "less!stylesheets/preview.less" -], function(settings, markdown, templateFile) { +], function(settings, _markdown, templateFile) { var _ = codebox.require("hr/utils"); var $ = codebox.require("hr/dom"); var box = codebox.require("core/box"); var FilesTabView = codebox.require("views/files/tab"); + var markdown = window.markdown; + var MarkdownView = FilesTabView.extend({ className: "addon-files-previewviewer", templateLoader: "text", From 89769cd0b78bdaa749e0e98fc9c1e63a159e549a Mon Sep 17 00:00:00 2001 From: Robin Dietrich Date: Sat, 17 May 2014 20:12:08 +0200 Subject: [PATCH 309/646] Added stripspaces function and corresponding menu entry --- addons/cb.files.editor/editor/view.js | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/addons/cb.files.editor/editor/view.js b/addons/cb.files.editor/editor/view.js index 1defa14c..fd723f82 100644 --- a/addons/cb.files.editor/editor/view.js +++ b/addons/cb.files.editor/editor/view.js @@ -138,6 +138,12 @@ define([ 'action': function() { aceWhitespace.convertIndentation(that.editor.session, "\t", 1); } + }, + { + 'title':"Strip Whitespaces", + 'action': function() { + that.stripspaces(); + } } ]).menuSection([ this.collaboratorsMenu @@ -512,19 +518,25 @@ define([ if (e) e.preventDefault(); if (editorSettings.user.get("stripspaces")) { - // strip all whitespaces from the current document - var doc = this.editor.session.doc; - var lines = doc.getAllLines(); - - for (var i = 0; i < lines.length; ++i) { - var index = lines[i].search(/\s+$/); - if (index !== -1 && index != 0) - doc.removeInLine(i, index, lines[i].length); - } + this.stripspaces(); } this.sync.save(); }, + + stripspaces: function(e) { + if (e) e.preventDefault(); + + // strip all whitespaces from the current document + var doc = this.editor.session.doc; + var lines = doc.getAllLines(); + + for (var i = 0; i < lines.length; ++i) { + var index = lines[i].search(/\s+$/); + if (index !== -1 && index != 0) + doc.removeInLine(i, index, lines[i].length); + } + }, // (action) Run this file runFile: function(e) { From 8dc1c460f80b36b1aa7936148a1c6ada58fabe38 Mon Sep 17 00:00:00 2001 From: Carlos Arturo Prieto Date: Tue, 3 Jun 2014 21:17:55 -0500 Subject: [PATCH 310/646] Running app engine apps on virtual machine with this correction it is possible to run App Engine apps on a different machine that runs Codebox such as virtual machines --- core/cb.project/appengine/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/cb.project/appengine/run.sh b/core/cb.project/appengine/run.sh index 318ece90..d68cdf37 100755 --- a/core/cb.project/appengine/run.sh +++ b/core/cb.project/appengine/run.sh @@ -4,4 +4,4 @@ WORKSPACE=$1 PORT=$2 -cd $WORKSPACE && dev_appserver.py ./ --port=${PORT} \ No newline at end of file +cd $WORKSPACE && dev_appserver.py ./ --port=${PORT} --host=0.0.0.0 From 924bcdbb78258017c91ab1aec79262928c299eaa Mon Sep 17 00:00:00 2001 From: Jan Henrik Date: Wed, 11 Jun 2014 23:52:03 +0200 Subject: [PATCH 311/646] Fixed typo --- bin/codebox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/codebox.js b/bin/codebox.js index aef2cb61..5051d6e6 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -20,7 +20,7 @@ cli.option('-t, --title [project title]', 'Title for the project.'); cli.option('-s, --sample [project type]', 'Replace directory content by a sample (warning: erase content).'); cli.option('-o, --open', 'Open the IDE in your favorite browser'); cli.option('-e, --email [email address]', 'Email address to use as a default authentication'); -cli.option('-u, --users [list users]', 'List of coma seperated users and password (formatted as "username:passowrd")'); +cli.option('-u, --users [list users]', 'List of coma seperated users and password (formatted as "username:password")'); // An authentication hook that uses a dictionary of users From 0227cd8165f72bb4bfb9cb57b8ee758cfef1e4fc Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Wed, 25 Jun 2014 00:15:03 -0700 Subject: [PATCH 312/646] Remove useless "./utils" require, fixes #397 --- core/codebox.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/codebox.js b/core/codebox.js index f2c9c8b4..219f956b 100644 --- a/core/codebox.js +++ b/core/codebox.js @@ -2,7 +2,6 @@ var Q = require('q'); var _ = require('lodash'); -var urils = require('./utils'); var os = require('os'); var path = require('path'); var Gittle = require('gittle'); @@ -291,4 +290,4 @@ var start = function(config) { module.exports = { 'start': start -}; \ No newline at end of file +}; From 53dd064993fe2fe2e957c539265918e3389f0c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 15 Jul 2014 19:46:31 -0700 Subject: [PATCH 313/646] Base for new version --- .gitignore | 3 +- Gruntfile.js | 261 +- README.md | 57 +- addons/cb.debug/client.js | 72 - addons/cb.debug/package.json | 19 - addons/cb.debug/settings.js | 42 - addons/cb.debug/stylesheets/tab.less | 99 - addons/cb.debug/views/backtrace.js | 35 - addons/cb.debug/views/breakpoints.js | 39 - addons/cb.debug/views/console.js | 107 - addons/cb.debug/views/locals.js | 39 - addons/cb.debug/views/section.js | 100 - addons/cb.debug/views/tab.js | 201 - addons/cb.deploy/client.js | 240 - addons/cb.deploy/package.json | 19 - addons/cb.files.editor/ace.js | 10 - addons/cb.files.editor/build.sh | 55 - addons/cb.files.editor/client.js | 28 - addons/cb.files.editor/download_ace.sh | 28 - addons/cb.files.editor/editor/breakpoints.js | 102 - addons/cb.files.editor/editor/codecomplete.js | 31 - addons/cb.files.editor/editor/jshint.js | 48 - addons/cb.files.editor/editor/settings.js | 93 - addons/cb.files.editor/editor/view.js | 583 -- addons/cb.files.editor/package.json | 28 - addons/cb.files.editor/stylesheets/file.less | 114 - addons/cb.files.editor/templates/file.html | 3 - addons/cb.files.editor/theme/textmate.js | 10 - addons/cb.files.editor/theme/textmate.less | 182 - addons/cb.files.image/client.js | 19 - addons/cb.files.image/package.json | 19 - addons/cb.files.image/stylesheets/image.less | 5 - addons/cb.files.image/templates/image.html | 1 - addons/cb.files.image/views/image.js | 19 - addons/cb.files.preview/client.js | 35 - addons/cb.files.preview/package.json | 22 - addons/cb.files.preview/settings.js | 17 - .../cb.files.preview/stylesheets/preview.less | 19 - .../templates/preview_html.html | 3 - .../templates/preview_markdown.html | 1 - addons/cb.files.preview/views/preview_html.js | 52 - .../views/preview_markdown.js | 58 - addons/cb.git/client.js | 251 - addons/cb.git/node/main.js | 29 - addons/cb.git/node/service.js | 150 - addons/cb.git/package.json | 31 - addons/cb.git/stylesheets/git.less | 64 - addons/cb.git/templates/dialog.html | 28 - addons/cb.git/views/dialog.js | 75 - addons/cb.help/client.js | 80 - addons/cb.help/package.json | 19 - addons/cb.help/welcome.md | 22 - addons/cb.offline/client.js | 80 - addons/cb.offline/menus.js | 87 - addons/cb.offline/package.json | 19 - addons/cb.offline/settings.js | 30 - addons/cb.panel.files/client.js | 85 - addons/cb.panel.files/package.json | 19 - addons/cb.panel.files/settings.js | 25 - addons/cb.panel.files/stylesheets/files.less | 97 - addons/cb.panel.files/stylesheets/panel.less | 9 - addons/cb.panel.files/templates/item.html | 14 - addons/cb.panel.files/views/panel.js | 58 - addons/cb.panel.files/views/tree.js | 168 - addons/cb.panel.outline/client.js | 30 - addons/cb.panel.outline/package.json | 19 - addons/cb.panel.outline/settings.js | 23 - .../cb.panel.outline/stylesheets/panel.less | 107 - addons/cb.panel.outline/views/panel.js | 13 - addons/cb.panel.outline/views/tags.js | 182 - addons/cb.project/autorun.js | 146 - addons/cb.project/client.js | 77 - addons/cb.project/package.json | 19 - addons/cb.project/ports.js | 43 - addons/cb.project/runner.js | 42 - addons/cb.project/samples.js | 49 - addons/cb.project/settings.js | 19 - addons/cb.settings/client.js | 29 - addons/cb.settings/package.json | 19 - addons/cb.settings/stylesheets/dialog.less | 57 - addons/cb.settings/templates/dialog.html | 34 - addons/cb.settings/views/dialog.js | 66 - addons/cb.terminal/client.js | 137 - addons/cb.terminal/package.json | 22 - addons/cb.terminal/stylesheets/tab.less | 76 - addons/cb.terminal/views/tab.js | 177 - addons/cb.theme.dark/ace/theme.js | 10 - addons/cb.theme.dark/ace/theme.less | 206 - addons/cb.theme.dark/main.js | 128 - addons/cb.theme.dark/package.json | 19 - bin/codebox.js | 161 +- client/collections/addons.js | 233 - client/collections/changes.js | 27 - client/collections/commands.js | 16 - client/collections/files.js | 11 - client/collections/operations.js | 66 - client/collections/tabs.js | 25 - client/collections/users.js | 27 - client/core/addons.js | 59 - client/core/app.js | 252 - client/core/backends/vfs.js | 176 - client/core/box.js | 20 - client/core/collaborators.js | 19 - client/core/commands/menu.js | 50 - client/core/commands/palette.js | 26 - client/core/commands/statusbar.js | 20 - client/core/commands/toolbar.js | 11 - client/core/debug/breakpoints.js | 47 - client/core/debug/manager.js | 60 - client/core/debug/session.js | 198 - client/core/files.js | 255 - client/core/globals.js | 7 - client/core/localfs.js | 519 -- client/core/operations.js | 6 - client/core/panels.js | 6 - client/core/search.js | 121 - client/core/search/addons.js | 111 - client/core/search/code.js | 155 - client/core/search/commands.js | 29 - client/core/search/files.js | 54 - client/core/search/tags.js | 40 - client/core/session.js | 66 - client/core/settings.js | 78 - client/core/tabs.js | 6 - client/core/themes.js | 161 - client/core/user.js | 8 - client/index.html | 14 - client/main.js | 20 - client/models/addon.js | 98 - client/models/box.js | 269 - client/models/change.js | 85 - client/models/command.js | 222 - client/models/file.js | 897 --- client/models/operation.js | 44 - client/models/shell.js | 109 - client/models/tab.js | 142 - client/models/user.js | 86 - .../fonts/fontawesome/FontAwesome.otf | Bin 63008 -> 0 bytes .../fonts/fontawesome/fontawesome-webfont.eot | Bin 38239 -> 0 bytes .../fonts/fontawesome/fontawesome-webfont.svg | 414 - .../fonts/fontawesome/fontawesome-webfont.ttf | Bin 80776 -> 0 bytes .../fontawesome/fontawesome-webfont.woff | Bin 44476 -> 0 bytes client/resources/fonts/helvetica/normal.eot | Bin 50032 -> 0 bytes client/resources/fonts/helvetica/normal.svg | 240 - client/resources/fonts/helvetica/normal.ttf | Bin 49832 -> 0 bytes client/resources/fonts/helvetica/normal.woff | Bin 25784 -> 0 bytes .../resources/fonts/helvetica/ultralight.eot | Bin 50360 -> 0 bytes .../resources/fonts/helvetica/ultralight.svg | 239 - .../resources/fonts/helvetica/ultralight.ttf | Bin 50148 -> 0 bytes .../resources/fonts/helvetica/ultralight.woff | Bin 26020 -> 0 bytes client/resources/images/icons/128.png | Bin 2487 -> 0 bytes client/resources/images/icons/32.png | Bin 863 -> 0 bytes client/resources/images/icons/48.png | Bin 1581 -> 0 bytes client/resources/images/icons/512.png | Bin 5925 -> 0 bytes client/resources/images/icons/72.png | Bin 1897 -> 0 bytes client/resources/images/icons/ios.png | Bin 3159 -> 0 bytes client/resources/resources.js | 10 - .../stylesheets/bootstrap/alerts.less | 67 - .../stylesheets/bootstrap/badges.less | 51 - .../stylesheets/bootstrap/bootstrap.less | 59 - .../stylesheets/bootstrap/breadcrumbs.less | 23 - .../stylesheets/bootstrap/button-groups.less | 248 - .../stylesheets/bootstrap/buttons.less | 160 - .../stylesheets/bootstrap/carousel.less | 209 - .../stylesheets/bootstrap/close.less | 33 - .../resources/stylesheets/bootstrap/code.less | 56 - .../bootstrap/component-animations.less | 29 - .../stylesheets/bootstrap/dropdowns.less | 193 - .../stylesheets/bootstrap/forms.less | 353 - .../stylesheets/bootstrap/glyphicons.less | 232 - .../resources/stylesheets/bootstrap/grid.less | 346 - .../stylesheets/bootstrap/input-groups.less | 127 - .../stylesheets/bootstrap/jumbotron.less | 40 - .../stylesheets/bootstrap/labels.less | 58 - .../stylesheets/bootstrap/list-group.less | 88 - .../stylesheets/bootstrap/media.less | 56 - .../stylesheets/bootstrap/mixins.less | 723 -- .../stylesheets/bootstrap/modals.less | 141 - .../stylesheets/bootstrap/navbar.less | 621 -- .../resources/stylesheets/bootstrap/navs.less | 229 - .../stylesheets/bootstrap/normalize.less | 396 - .../stylesheets/bootstrap/pager.less | 55 - .../stylesheets/bootstrap/pagination.less | 83 - .../stylesheets/bootstrap/panels.less | 148 - .../stylesheets/bootstrap/popovers.less | 133 - .../stylesheets/bootstrap/print.less | 100 - .../stylesheets/bootstrap/progress-bars.less | 95 - .../bootstrap/responsive-utilities.less | 220 - .../stylesheets/bootstrap/scaffolding.less | 130 - .../stylesheets/bootstrap/tables.less | 236 - .../stylesheets/bootstrap/theme.less | 232 - .../stylesheets/bootstrap/thumbnails.less | 31 - .../stylesheets/bootstrap/tooltip.less | 95 - .../resources/stylesheets/bootstrap/type.less | 238 - .../stylesheets/bootstrap/utilities.less | 42 - .../stylesheets/bootstrap/variables.less | 620 -- .../stylesheets/bootstrap/wells.less | 29 - .../fontawesome/bordered-pulled.less | 16 - .../stylesheets/fontawesome/core.less | 12 - .../stylesheets/fontawesome/fixed-width.less | 6 - .../stylesheets/fontawesome/font-awesome.less | 38 - .../stylesheets/fontawesome/icons.less | 412 - .../stylesheets/fontawesome/larger.less | 13 - .../stylesheets/fontawesome/list.less | 19 - .../stylesheets/fontawesome/mixins.less | 20 - .../stylesheets/fontawesome/path.less | 14 - .../fontawesome/rotated-flipped.less | 9 - .../stylesheets/fontawesome/spinning.less | 30 - .../stylesheets/fontawesome/stacked.less | 20 - .../stylesheets/fontawesome/variables.less | 382 - client/resources/stylesheets/fonts.less | 17 - client/resources/stylesheets/main.less | 74 - client/resources/stylesheets/tabs/base.less | 96 - .../resources/stylesheets/tabs/manager.less | 6 - .../resources/stylesheets/tabs/section.less | 50 - client/resources/stylesheets/tabs/tab.less | 105 - client/resources/stylesheets/ui/alert.less | 21 - client/resources/stylesheets/ui/body.less | 13 - client/resources/stylesheets/ui/grid.less | 46 - .../resources/stylesheets/ui/lateralbar.less | 23 - client/resources/stylesheets/ui/loading.less | 68 - client/resources/stylesheets/ui/login.less | 46 - client/resources/stylesheets/ui/menu.less | 97 - client/resources/stylesheets/ui/menubar.less | 64 - .../resources/stylesheets/ui/operations.less | 28 - client/resources/stylesheets/ui/palette.less | 76 - client/resources/stylesheets/ui/panels.less | 52 - .../resources/stylesheets/ui/statusbar.less | 84 - client/resources/stylesheets/ui/toolbar.less | 47 - client/resources/stylesheets/variables.less | 55 - .../resources/templates/commands/command.html | 3 - .../templates/commands/palette/command.html | 14 - .../templates/commands/palette/input.html | 2 - client/resources/templates/dialogs/alert.html | 14 - .../resources/templates/dialogs/confirm.html | 15 - .../resources/templates/dialogs/fields.html | 54 - .../resources/templates/dialogs/prompt.html | 15 - .../resources/templates/dialogs/select.html | 19 - client/resources/templates/main.html | 61 - .../templates/operations/operation.html | 5 - client/resources/templates/settings/base.html | 39 - client/utils/alerts.js | 44 - client/utils/clipboard.js | 90 - client/utils/contextmenu.js | 138 - client/utils/css.js | 62 - client/utils/dialogs.js | 148 - client/utils/filesync.js | 876 --- client/utils/gravatar.js | 19 - client/utils/hash.js | 270 - client/utils/keyboard.js | 119 - client/utils/languages.js | 1533 ---- client/utils/loading.js | 34 - client/utils/string.js | 100 - client/utils/uploader.js | 178 - client/utils/url.js | 74 - client/vendors/bootstrap/affix.js | 126 - client/vendors/bootstrap/alert.js | 98 - client/vendors/bootstrap/button.js | 109 - client/vendors/bootstrap/carousel.js | 217 - client/vendors/bootstrap/collapse.js | 179 - client/vendors/bootstrap/dropdown.js | 154 - client/vendors/bootstrap/modal.js | 246 - client/vendors/bootstrap/popover.js | 117 - client/vendors/bootstrap/scrollspy.js | 158 - client/vendors/bootstrap/tab.js | 135 - client/vendors/bootstrap/tooltip.js | 386 - client/vendors/bootstrap/transition.js | 56 - client/vendors/crypto.js | 16 - client/vendors/diff_match_patch.js | 2196 ------ client/vendors/filer.js | 854 -- client/vendors/idb.filesystem.js | 916 --- client/vendors/moment.js | 6 - client/vendors/mousetrap.js | 953 --- client/vendors/socket.io.js | 3781 --------- client/vendors/taphold.js | 92 - client/views/commands/manager.js | 69 - client/views/commands/menu.js | 156 - client/views/commands/menubar.js | 74 - client/views/commands/palette.js | 308 - client/views/commands/statusbar.js | 11 - client/views/commands/toolbar.js | 59 - client/views/dialogs/base.js | 162 - client/views/files/base.js | 70 - client/views/files/tab.js | 29 - client/views/operations/manager.js | 38 - client/views/panels/base.js | 92 - client/views/panels/file.js | 104 - client/views/panels/manager.js | 138 - client/views/settings/base.js | 87 - client/views/tabs/base.js | 213 - client/views/tabs/file.js | 98 - client/views/tabs/manager.js | 355 - client/views/tabs/section.js | 144 - client/views/tabs/tab.js | 169 - core/cb.addons/addon.js | 234 - core/cb.addons/main.js | 239 - core/cb.addons/manager.js | 95 - core/cb.addons/package.json | 16 - core/cb.addons/registry.js | 27 - .../require-tools/css/css-builder.js | 162 - core/cb.addons/require-tools/css/css.js | 131 - core/cb.addons/require-tools/css/normalize.js | 137 - .../require-tools/less/less-builder.js | 121 - core/cb.addons/require-tools/less/less.js | 78 - core/cb.addons/require-tools/less/lessc.js | 6914 ----------------- .../cb.addons/require-tools/less/normalize.js | 138 - core/cb.addons/require-tools/text/text.js | 386 - core/cb.codecomplete.ctags/main.js | 25 - core/cb.codecomplete.ctags/package.json | 15 - core/cb.codecomplete.ctags/tags.js | 62 - core/cb.codecomplete/codecomplete.js | 168 - core/cb.codecomplete/main.js | 19 - core/cb.codecomplete/package.json | 19 - core/cb.core/main.js | 28 - core/cb.core/package.json | 16 - core/cb.core/user.js | 46 - core/cb.core/workspace.js | 166 - core/cb.deploy.appengine/main.js | 67 - core/cb.deploy.appengine/package.json | 10 - core/cb.deploy.ftp/main.js | 74 - core/cb.deploy.ftp/package.json | 10 - core/cb.deploy.ftp/upload.sh | 21 - core/cb.deploy.ghpages/main.js | 46 - core/cb.deploy.ghpages/package.json | 10 - core/cb.deploy.heroku/api.js | 29 - core/cb.deploy.heroku/main.js | 76 - core/cb.deploy.heroku/package.json | 10 - core/cb.deploy.parse/main.js | 67 - core/cb.deploy.parse/package.json | 10 - core/cb.deploy/main.js | 40 - core/cb.deploy/package.json | 16 - core/cb.deploy/solution.js | 40 - core/cb.events.log/main.js | 24 - core/cb.events.log/package.json | 16 - core/cb.events.socketio/main.js | 36 - core/cb.events.socketio/package.json | 15 - core/cb.events.webhook/main.js | 61 - core/cb.events.webhook/package.json | 17 - core/cb.events/main.js | 18 - core/cb.events/package.json | 14 - core/cb.export/main.js | 28 - core/cb.export/package.json | 18 - core/cb.files.service/main.js | 23 - core/cb.files.service/package.json | 17 - core/cb.files.service/service.js | 113 - core/cb.files.sync/environment.js | 294 - core/cb.files.sync/main.js | 28 - core/cb.files.sync/manager.js | 138 - core/cb.files.sync/models/cursor.js | 7 - core/cb.files.sync/models/document.js | 115 - core/cb.files.sync/models/selection.js | 11 - core/cb.files.sync/models/user.js | 94 - core/cb.files.sync/package.json | 16 - core/cb.files.sync/utils.js | 32 - core/cb.files.sync/validators.js | 71 - core/cb.hooks/main.js | 131 - core/cb.hooks/package.json | 16 - core/cb.logger/main.js | 53 - core/cb.logger/package.json | 16 - core/cb.main/main.js | 31 - core/cb.main/package.json | 17 - core/cb.offline/main.js | 71 - core/cb.offline/manifest.js | 75 - core/cb.offline/package.json | 16 - core/cb.proc/http.js | 143 - core/cb.proc/main.js | 31 - core/cb.proc/package.json | 16 - core/cb.project/appengine/detector.sh | 8 - core/cb.project/appengine/index.js | 15 - core/cb.project/appengine/run.sh | 7 - core/cb.project/appengine/sample/app.yaml | 9 - .../cb.project/appengine/sample/helloworld.py | 13 - core/cb.project/c/detector.sh | 8 - core/cb.project/c/index.js | 9 - core/cb.project/c/sample/Makefile | 10 - core/cb.project/c/sample/main.c | 7 - core/cb.project/clojure/detector.sh | 13 - core/cb.project/clojure/index.js | 15 - core/cb.project/clojure/run.sh | 7 - core/cb.project/clojure/sample/.gitignore | 10 - core/cb.project/clojure/sample/project.clj | 8 - .../clojure/sample/src/helloworld.clj | 13 - core/cb.project/d/detector.sh | 7 - core/cb.project/d/index.js | 15 - core/cb.project/d/run.sh | 30 - core/cb.project/d/sample/main.d | 10 - core/cb.project/dart/detector.sh | 8 - core/cb.project/dart/index.js | 30 - core/cb.project/dart/run_build.sh | 6 - core/cb.project/dart/run_clean.sh | 6 - core/cb.project/dart/run_serve.sh | 7 - core/cb.project/dart/sample/pubspec.lock | 7 - core/cb.project/dart/sample/pubspec.yaml | 4 - .../cb.project/dart/sample/web/helloworld.css | 27 - .../dart/sample/web/helloworld.dart | 16 - core/cb.project/dart/sample/web/index.html | 21 - core/cb.project/django/detector.sh | 10 - core/cb.project/django/index.js | 14 - core/cb.project/django/run.sh | 11 - core/cb.project/go/detector.sh | 8 - core/cb.project/go/index.js | 15 - core/cb.project/go/run.sh | 30 - core/cb.project/go/sample/main.go | 7 - core/cb.project/gradle/detector.sh | 8 - core/cb.project/gradle/index.js | 15 - core/cb.project/gradle/run.sh | 6 - core/cb.project/gradle/sample/build.gradle | 13 - core/cb.project/grails/detector.sh | 8 - core/cb.project/grails/index.js | 14 - core/cb.project/grails/run.sh | 6 - core/cb.project/java/detector.sh | 20 - core/cb.project/java/index.js | 15 - core/cb.project/java/run.sh | 33 - core/cb.project/java/sample/HelloWorld.java | 12 - core/cb.project/logo/detector.sh | 18 - core/cb.project/logo/index.js | 8 - core/cb.project/lua/detector.sh | 8 - core/cb.project/lua/index.js | 15 - core/cb.project/lua/run.sh | 30 - core/cb.project/lua/sample/main.lua | 13 - core/cb.project/main.js | 175 - core/cb.project/makefile/detector.sh | 8 - core/cb.project/makefile/index.js | 20 - core/cb.project/makefile/run_all.sh | 6 - core/cb.project/makefile/run_clean.sh | 6 - core/cb.project/maven/detector.sh | 10 - core/cb.project/maven/index.js | 18 - core/cb.project/maven/install.sh | 7 - core/cb.project/maven/run.sh | 7 - core/cb.project/meteor/detector.sh | 8 - core/cb.project/meteor/index.js | 31 - core/cb.project/meteor/mrt_install.sh | 7 - core/cb.project/meteor/mrt_update.sh | 7 - core/cb.project/meteor/run.sh | 7 - .../meteor/sample/.meteor/.gitignore | 1 - .../cb.project/meteor/sample/.meteor/packages | 9 - core/cb.project/meteor/sample/.meteor/release | 1 - core/cb.project/meteor/sample/hello.css | 1 - core/cb.project/meteor/sample/hello.html | 13 - core/cb.project/meteor/sample/hello.js | 19 - core/cb.project/node/detector.sh | 8 - core/cb.project/node/index.js | 25 - core/cb.project/node/install.sh | 6 - core/cb.project/node/run.sh | 7 - core/cb.project/node/sample/package.json | 13 - core/cb.project/node/sample/web.js | 12 - core/cb.project/package.json | 18 - core/cb.project/parse/detector.sh | 8 - core/cb.project/parse/index.js | 16 - core/cb.project/parse/run.sh | 12 - core/cb.project/parse/sample/cloud/main.js | 6 - .../parse/sample/config/global.json | 14 - .../cb.project/parse/sample/public/index.html | 9 - core/cb.project/php/_waitfile.sh | 29 - core/cb.project/php/detector.sh | 8 - core/cb.project/php/index.js | 22 - core/cb.project/php/run.sh | 18 - core/cb.project/php/run_apache.sh | 221 - core/cb.project/php/sample/index.php | 3 - core/cb.project/play/detector.sh | 12 - core/cb.project/play/index.js | 14 - core/cb.project/play/run.sh | 6 - core/cb.project/procfile/detector.sh | 8 - core/cb.project/procfile/index.js | 14 - core/cb.project/procfile/run.sh | 140 - core/cb.project/project.js | 215 - core/cb.project/python/detector.sh | 22 - core/cb.project/python/index.js | 15 - core/cb.project/python/run.sh | 30 - core/cb.project/python/sample/app.py | 14 - .../cb.project/python/sample/requirements.txt | 6 - core/cb.project/ruby/detector.sh | 8 - core/cb.project/ruby/index.js | 15 - core/cb.project/ruby/run.sh | 30 - core/cb.project/ruby/sample/Gemfile | 2 - core/cb.project/ruby/sample/main.rb | 11 - core/cb.project/scala/detector.sh | 25 - core/cb.project/scala/index.js | 9 - core/cb.project/scala/sample/main.scala | 7 - core/cb.project/static/detector.sh | 9 - core/cb.project/static/index.js | 15 - core/cb.project/static/run.sh | 11 - core/cb.project/static/sample/index.html | 13 - core/cb.rpc.addons/main.js | 21 - core/cb.rpc.addons/package.json | 15 - core/cb.rpc.addons/service.js | 45 - core/cb.rpc.auth/main.js | 23 - core/cb.rpc.auth/package.json | 16 - core/cb.rpc.auth/service.js | 47 - core/cb.rpc.box/main.js | 23 - core/cb.rpc.box/package.json | 15 - core/cb.rpc.box/service.js | 46 - core/cb.rpc.codecomplete/main.js | 20 - core/cb.rpc.codecomplete/package.json | 17 - core/cb.rpc.codecomplete/service.js | 17 - core/cb.rpc.debug/main.js | 23 - core/cb.rpc.debug/package.json | 16 - core/cb.rpc.debug/service.js | 218 - core/cb.rpc.deploy/main.js | 22 - core/cb.rpc.deploy/package.json | 16 - core/cb.rpc.deploy/service.js | 48 - core/cb.rpc.proc/main.js | 21 - core/cb.rpc.proc/package.json | 15 - core/cb.rpc.proc/service.js | 16 - core/cb.rpc.project/main.js | 22 - core/cb.rpc.project/package.json | 15 - core/cb.rpc.project/service.js | 39 - core/cb.rpc.run/main.js | 31 - core/cb.rpc.run/package.json | 20 - core/cb.rpc.run/service.js | 46 - core/cb.rpc.search/main.js | 21 - core/cb.rpc.search/package.json | 15 - core/cb.rpc.search/service.js | 24 - core/cb.rpc.shells/main.js | 23 - core/cb.rpc.shells/package.json | 17 - core/cb.rpc.shells/service.js | 46 - core/cb.rpc.users/main.js | 20 - core/cb.rpc.users/package.json | 15 - core/cb.rpc.users/service.js | 22 - core/cb.rpc/main.js | 27 - core/cb.rpc/manager.js | 117 - core/cb.rpc/package.json | 19 - core/cb.run.file/commands.json | 22 - core/cb.run.file/main.js | 68 - core/cb.run.file/package.json | 19 - core/cb.run.ports/main.js | 20 - core/cb.run.ports/package.json | 14 - core/cb.run.project/main.js | 22 - core/cb.run.project/package.json | 22 - core/cb.run.project/project.js | 172 - core/cb.search/code.js | 152 - core/cb.search/files.js | 39 - core/cb.search/main.js | 21 - core/cb.search/package.json | 17 - core/cb.search/types.js | 120 - core/cb.server/main.js | 151 - core/cb.server/package.json | 16 - core/cb.settings/main.js | 59 - core/cb.settings/package.json | 18 - core/cb.shells.stream/main.js | 123 - core/cb.shells.stream/package.json | 17 - core/cb.shells/main.js | 107 - core/cb.shells/package.json | 17 - core/cb.socket.io/main.js | 24 - core/cb.socket.io/package.json | 16 - core/cb.vfs.http/main.js | 16 - core/cb.vfs.http/package.json | 14 - core/cb.vfs/main.js | 20 - core/cb.vfs/package.json | 16 - core/cb.watch/init.js | 129 - core/cb.watch/main.js | 19 - core/cb.watch/package.json | 16 - core/codebox.js | 293 - core/utils.js | 189 - editor/collections/commands.js | 32 + editor/collections/packages.js | 28 + editor/core/application.js | 38 + editor/core/commands.js | 7 + editor/core/packages.js | 7 + {client/core/backends => editor/core}/rpc.js | 12 +- editor/core/statusbar.js | 12 + editor/main.js | 20 + editor/models/command.js | 50 + editor/models/package.js | 70 + editor/resources/init.js | 12 + editor/resources/stylesheets/main.less | 0 editor/resources/stylesheets/variables.less | 0 editor/utils/dialogs.js | 6 + {client => editor}/utils/dragdrop.js | 18 +- {client => editor}/views/grid.js | 4 +- index.js | 1 - init.sh | 215 - lib/index.js | 28 + lib/packages.js | 22 + lib/rpc.js | 49 + lib/services/fs.js | 6 + lib/services/index.js | 4 + lib/services/packages.js | 13 + package.json | 85 +- screenshot.png | Bin 331938 -> 0 bytes tasks/grunt-build-addons.js | 41 - 581 files changed, 467 insertions(+), 55563 deletions(-) delete mode 100644 addons/cb.debug/client.js delete mode 100644 addons/cb.debug/package.json delete mode 100644 addons/cb.debug/settings.js delete mode 100644 addons/cb.debug/stylesheets/tab.less delete mode 100644 addons/cb.debug/views/backtrace.js delete mode 100644 addons/cb.debug/views/breakpoints.js delete mode 100644 addons/cb.debug/views/console.js delete mode 100644 addons/cb.debug/views/locals.js delete mode 100644 addons/cb.debug/views/section.js delete mode 100644 addons/cb.debug/views/tab.js delete mode 100644 addons/cb.deploy/client.js delete mode 100644 addons/cb.deploy/package.json delete mode 100644 addons/cb.files.editor/ace.js delete mode 100755 addons/cb.files.editor/build.sh delete mode 100644 addons/cb.files.editor/client.js delete mode 100755 addons/cb.files.editor/download_ace.sh delete mode 100644 addons/cb.files.editor/editor/breakpoints.js delete mode 100644 addons/cb.files.editor/editor/codecomplete.js delete mode 100644 addons/cb.files.editor/editor/jshint.js delete mode 100644 addons/cb.files.editor/editor/settings.js delete mode 100644 addons/cb.files.editor/editor/view.js delete mode 100644 addons/cb.files.editor/package.json delete mode 100644 addons/cb.files.editor/stylesheets/file.less delete mode 100644 addons/cb.files.editor/templates/file.html delete mode 100644 addons/cb.files.editor/theme/textmate.js delete mode 100644 addons/cb.files.editor/theme/textmate.less delete mode 100644 addons/cb.files.image/client.js delete mode 100644 addons/cb.files.image/package.json delete mode 100644 addons/cb.files.image/stylesheets/image.less delete mode 100644 addons/cb.files.image/templates/image.html delete mode 100644 addons/cb.files.image/views/image.js delete mode 100644 addons/cb.files.preview/client.js delete mode 100644 addons/cb.files.preview/package.json delete mode 100644 addons/cb.files.preview/settings.js delete mode 100644 addons/cb.files.preview/stylesheets/preview.less delete mode 100644 addons/cb.files.preview/templates/preview_html.html delete mode 100644 addons/cb.files.preview/templates/preview_markdown.html delete mode 100644 addons/cb.files.preview/views/preview_html.js delete mode 100644 addons/cb.files.preview/views/preview_markdown.js delete mode 100644 addons/cb.git/client.js delete mode 100644 addons/cb.git/node/main.js delete mode 100644 addons/cb.git/node/service.js delete mode 100644 addons/cb.git/package.json delete mode 100644 addons/cb.git/stylesheets/git.less delete mode 100644 addons/cb.git/templates/dialog.html delete mode 100644 addons/cb.git/views/dialog.js delete mode 100644 addons/cb.help/client.js delete mode 100644 addons/cb.help/package.json delete mode 100644 addons/cb.help/welcome.md delete mode 100644 addons/cb.offline/client.js delete mode 100644 addons/cb.offline/menus.js delete mode 100644 addons/cb.offline/package.json delete mode 100644 addons/cb.offline/settings.js delete mode 100644 addons/cb.panel.files/client.js delete mode 100644 addons/cb.panel.files/package.json delete mode 100644 addons/cb.panel.files/settings.js delete mode 100644 addons/cb.panel.files/stylesheets/files.less delete mode 100644 addons/cb.panel.files/stylesheets/panel.less delete mode 100644 addons/cb.panel.files/templates/item.html delete mode 100644 addons/cb.panel.files/views/panel.js delete mode 100644 addons/cb.panel.files/views/tree.js delete mode 100644 addons/cb.panel.outline/client.js delete mode 100644 addons/cb.panel.outline/package.json delete mode 100644 addons/cb.panel.outline/settings.js delete mode 100644 addons/cb.panel.outline/stylesheets/panel.less delete mode 100644 addons/cb.panel.outline/views/panel.js delete mode 100644 addons/cb.panel.outline/views/tags.js delete mode 100644 addons/cb.project/autorun.js delete mode 100644 addons/cb.project/client.js delete mode 100644 addons/cb.project/package.json delete mode 100644 addons/cb.project/ports.js delete mode 100644 addons/cb.project/runner.js delete mode 100644 addons/cb.project/samples.js delete mode 100644 addons/cb.project/settings.js delete mode 100644 addons/cb.settings/client.js delete mode 100644 addons/cb.settings/package.json delete mode 100644 addons/cb.settings/stylesheets/dialog.less delete mode 100644 addons/cb.settings/templates/dialog.html delete mode 100644 addons/cb.settings/views/dialog.js delete mode 100644 addons/cb.terminal/client.js delete mode 100644 addons/cb.terminal/package.json delete mode 100644 addons/cb.terminal/stylesheets/tab.less delete mode 100644 addons/cb.terminal/views/tab.js delete mode 100644 addons/cb.theme.dark/ace/theme.js delete mode 100644 addons/cb.theme.dark/ace/theme.less delete mode 100644 addons/cb.theme.dark/main.js delete mode 100644 addons/cb.theme.dark/package.json delete mode 100644 client/collections/addons.js delete mode 100644 client/collections/changes.js delete mode 100644 client/collections/commands.js delete mode 100644 client/collections/files.js delete mode 100644 client/collections/operations.js delete mode 100644 client/collections/tabs.js delete mode 100644 client/collections/users.js delete mode 100644 client/core/addons.js delete mode 100644 client/core/app.js delete mode 100644 client/core/backends/vfs.js delete mode 100644 client/core/box.js delete mode 100644 client/core/collaborators.js delete mode 100644 client/core/commands/menu.js delete mode 100644 client/core/commands/palette.js delete mode 100644 client/core/commands/statusbar.js delete mode 100644 client/core/commands/toolbar.js delete mode 100644 client/core/debug/breakpoints.js delete mode 100644 client/core/debug/manager.js delete mode 100644 client/core/debug/session.js delete mode 100644 client/core/files.js delete mode 100644 client/core/globals.js delete mode 100644 client/core/localfs.js delete mode 100644 client/core/operations.js delete mode 100644 client/core/panels.js delete mode 100644 client/core/search.js delete mode 100644 client/core/search/addons.js delete mode 100644 client/core/search/code.js delete mode 100644 client/core/search/commands.js delete mode 100644 client/core/search/files.js delete mode 100644 client/core/search/tags.js delete mode 100644 client/core/session.js delete mode 100644 client/core/settings.js delete mode 100644 client/core/tabs.js delete mode 100644 client/core/themes.js delete mode 100644 client/core/user.js delete mode 100644 client/index.html delete mode 100644 client/main.js delete mode 100644 client/models/addon.js delete mode 100644 client/models/box.js delete mode 100644 client/models/change.js delete mode 100644 client/models/command.js delete mode 100644 client/models/file.js delete mode 100644 client/models/operation.js delete mode 100644 client/models/shell.js delete mode 100644 client/models/tab.js delete mode 100644 client/models/user.js delete mode 100644 client/resources/fonts/fontawesome/FontAwesome.otf delete mode 100755 client/resources/fonts/fontawesome/fontawesome-webfont.eot delete mode 100755 client/resources/fonts/fontawesome/fontawesome-webfont.svg delete mode 100755 client/resources/fonts/fontawesome/fontawesome-webfont.ttf delete mode 100755 client/resources/fonts/fontawesome/fontawesome-webfont.woff delete mode 100644 client/resources/fonts/helvetica/normal.eot delete mode 100644 client/resources/fonts/helvetica/normal.svg delete mode 100644 client/resources/fonts/helvetica/normal.ttf delete mode 100644 client/resources/fonts/helvetica/normal.woff delete mode 100644 client/resources/fonts/helvetica/ultralight.eot delete mode 100644 client/resources/fonts/helvetica/ultralight.svg delete mode 100644 client/resources/fonts/helvetica/ultralight.ttf delete mode 100644 client/resources/fonts/helvetica/ultralight.woff delete mode 100644 client/resources/images/icons/128.png delete mode 100644 client/resources/images/icons/32.png delete mode 100644 client/resources/images/icons/48.png delete mode 100644 client/resources/images/icons/512.png delete mode 100644 client/resources/images/icons/72.png delete mode 100644 client/resources/images/icons/ios.png delete mode 100644 client/resources/resources.js delete mode 100755 client/resources/stylesheets/bootstrap/alerts.less delete mode 100755 client/resources/stylesheets/bootstrap/badges.less delete mode 100755 client/resources/stylesheets/bootstrap/bootstrap.less delete mode 100755 client/resources/stylesheets/bootstrap/breadcrumbs.less delete mode 100755 client/resources/stylesheets/bootstrap/button-groups.less delete mode 100755 client/resources/stylesheets/bootstrap/buttons.less delete mode 100755 client/resources/stylesheets/bootstrap/carousel.less delete mode 100755 client/resources/stylesheets/bootstrap/close.less delete mode 100755 client/resources/stylesheets/bootstrap/code.less delete mode 100755 client/resources/stylesheets/bootstrap/component-animations.less delete mode 100755 client/resources/stylesheets/bootstrap/dropdowns.less delete mode 100755 client/resources/stylesheets/bootstrap/forms.less delete mode 100755 client/resources/stylesheets/bootstrap/glyphicons.less delete mode 100755 client/resources/stylesheets/bootstrap/grid.less delete mode 100755 client/resources/stylesheets/bootstrap/input-groups.less delete mode 100755 client/resources/stylesheets/bootstrap/jumbotron.less delete mode 100755 client/resources/stylesheets/bootstrap/labels.less delete mode 100755 client/resources/stylesheets/bootstrap/list-group.less delete mode 100755 client/resources/stylesheets/bootstrap/media.less delete mode 100755 client/resources/stylesheets/bootstrap/mixins.less delete mode 100755 client/resources/stylesheets/bootstrap/modals.less delete mode 100755 client/resources/stylesheets/bootstrap/navbar.less delete mode 100755 client/resources/stylesheets/bootstrap/navs.less delete mode 100755 client/resources/stylesheets/bootstrap/normalize.less delete mode 100755 client/resources/stylesheets/bootstrap/pager.less delete mode 100755 client/resources/stylesheets/bootstrap/pagination.less delete mode 100755 client/resources/stylesheets/bootstrap/panels.less delete mode 100755 client/resources/stylesheets/bootstrap/popovers.less delete mode 100755 client/resources/stylesheets/bootstrap/print.less delete mode 100755 client/resources/stylesheets/bootstrap/progress-bars.less delete mode 100755 client/resources/stylesheets/bootstrap/responsive-utilities.less delete mode 100755 client/resources/stylesheets/bootstrap/scaffolding.less delete mode 100755 client/resources/stylesheets/bootstrap/tables.less delete mode 100755 client/resources/stylesheets/bootstrap/theme.less delete mode 100755 client/resources/stylesheets/bootstrap/thumbnails.less delete mode 100755 client/resources/stylesheets/bootstrap/tooltip.less delete mode 100755 client/resources/stylesheets/bootstrap/type.less delete mode 100755 client/resources/stylesheets/bootstrap/utilities.less delete mode 100755 client/resources/stylesheets/bootstrap/variables.less delete mode 100755 client/resources/stylesheets/bootstrap/wells.less delete mode 100644 client/resources/stylesheets/fontawesome/bordered-pulled.less delete mode 100644 client/resources/stylesheets/fontawesome/core.less delete mode 100644 client/resources/stylesheets/fontawesome/fixed-width.less delete mode 100644 client/resources/stylesheets/fontawesome/font-awesome.less delete mode 100644 client/resources/stylesheets/fontawesome/icons.less delete mode 100644 client/resources/stylesheets/fontawesome/larger.less delete mode 100644 client/resources/stylesheets/fontawesome/list.less delete mode 100644 client/resources/stylesheets/fontawesome/mixins.less delete mode 100644 client/resources/stylesheets/fontawesome/path.less delete mode 100644 client/resources/stylesheets/fontawesome/rotated-flipped.less delete mode 100644 client/resources/stylesheets/fontawesome/spinning.less delete mode 100644 client/resources/stylesheets/fontawesome/stacked.less delete mode 100644 client/resources/stylesheets/fontawesome/variables.less delete mode 100644 client/resources/stylesheets/fonts.less delete mode 100644 client/resources/stylesheets/main.less delete mode 100644 client/resources/stylesheets/tabs/base.less delete mode 100644 client/resources/stylesheets/tabs/manager.less delete mode 100644 client/resources/stylesheets/tabs/section.less delete mode 100644 client/resources/stylesheets/tabs/tab.less delete mode 100644 client/resources/stylesheets/ui/alert.less delete mode 100644 client/resources/stylesheets/ui/body.less delete mode 100644 client/resources/stylesheets/ui/grid.less delete mode 100644 client/resources/stylesheets/ui/lateralbar.less delete mode 100644 client/resources/stylesheets/ui/loading.less delete mode 100644 client/resources/stylesheets/ui/login.less delete mode 100644 client/resources/stylesheets/ui/menu.less delete mode 100644 client/resources/stylesheets/ui/menubar.less delete mode 100644 client/resources/stylesheets/ui/operations.less delete mode 100644 client/resources/stylesheets/ui/palette.less delete mode 100644 client/resources/stylesheets/ui/panels.less delete mode 100644 client/resources/stylesheets/ui/statusbar.less delete mode 100644 client/resources/stylesheets/ui/toolbar.less delete mode 100644 client/resources/stylesheets/variables.less delete mode 100644 client/resources/templates/commands/command.html delete mode 100644 client/resources/templates/commands/palette/command.html delete mode 100644 client/resources/templates/commands/palette/input.html delete mode 100644 client/resources/templates/dialogs/alert.html delete mode 100644 client/resources/templates/dialogs/confirm.html delete mode 100644 client/resources/templates/dialogs/fields.html delete mode 100644 client/resources/templates/dialogs/prompt.html delete mode 100644 client/resources/templates/dialogs/select.html delete mode 100644 client/resources/templates/main.html delete mode 100644 client/resources/templates/operations/operation.html delete mode 100644 client/resources/templates/settings/base.html delete mode 100644 client/utils/alerts.js delete mode 100644 client/utils/clipboard.js delete mode 100644 client/utils/contextmenu.js delete mode 100644 client/utils/css.js delete mode 100644 client/utils/dialogs.js delete mode 100644 client/utils/filesync.js delete mode 100644 client/utils/gravatar.js delete mode 100644 client/utils/hash.js delete mode 100644 client/utils/keyboard.js delete mode 100644 client/utils/languages.js delete mode 100644 client/utils/loading.js delete mode 100644 client/utils/string.js delete mode 100644 client/utils/uploader.js delete mode 100644 client/utils/url.js delete mode 100755 client/vendors/bootstrap/affix.js delete mode 100755 client/vendors/bootstrap/alert.js delete mode 100755 client/vendors/bootstrap/button.js delete mode 100755 client/vendors/bootstrap/carousel.js delete mode 100755 client/vendors/bootstrap/collapse.js delete mode 100755 client/vendors/bootstrap/dropdown.js delete mode 100755 client/vendors/bootstrap/modal.js delete mode 100755 client/vendors/bootstrap/popover.js delete mode 100755 client/vendors/bootstrap/scrollspy.js delete mode 100755 client/vendors/bootstrap/tab.js delete mode 100755 client/vendors/bootstrap/tooltip.js delete mode 100755 client/vendors/bootstrap/transition.js delete mode 100644 client/vendors/crypto.js delete mode 100644 client/vendors/diff_match_patch.js delete mode 100644 client/vendors/filer.js delete mode 100644 client/vendors/idb.filesystem.js delete mode 100644 client/vendors/moment.js delete mode 100644 client/vendors/mousetrap.js delete mode 100644 client/vendors/socket.io.js delete mode 100644 client/vendors/taphold.js delete mode 100644 client/views/commands/manager.js delete mode 100644 client/views/commands/menu.js delete mode 100644 client/views/commands/menubar.js delete mode 100644 client/views/commands/palette.js delete mode 100644 client/views/commands/statusbar.js delete mode 100644 client/views/commands/toolbar.js delete mode 100644 client/views/dialogs/base.js delete mode 100644 client/views/files/base.js delete mode 100644 client/views/files/tab.js delete mode 100644 client/views/operations/manager.js delete mode 100644 client/views/panels/base.js delete mode 100644 client/views/panels/file.js delete mode 100644 client/views/panels/manager.js delete mode 100644 client/views/settings/base.js delete mode 100644 client/views/tabs/base.js delete mode 100644 client/views/tabs/file.js delete mode 100644 client/views/tabs/manager.js delete mode 100644 client/views/tabs/section.js delete mode 100644 client/views/tabs/tab.js delete mode 100644 core/cb.addons/addon.js delete mode 100644 core/cb.addons/main.js delete mode 100644 core/cb.addons/manager.js delete mode 100644 core/cb.addons/package.json delete mode 100644 core/cb.addons/registry.js delete mode 100755 core/cb.addons/require-tools/css/css-builder.js delete mode 100755 core/cb.addons/require-tools/css/css.js delete mode 100755 core/cb.addons/require-tools/css/normalize.js delete mode 100755 core/cb.addons/require-tools/less/less-builder.js delete mode 100755 core/cb.addons/require-tools/less/less.js delete mode 100755 core/cb.addons/require-tools/less/lessc.js delete mode 100755 core/cb.addons/require-tools/less/normalize.js delete mode 100644 core/cb.addons/require-tools/text/text.js delete mode 100644 core/cb.codecomplete.ctags/main.js delete mode 100644 core/cb.codecomplete.ctags/package.json delete mode 100644 core/cb.codecomplete.ctags/tags.js delete mode 100644 core/cb.codecomplete/codecomplete.js delete mode 100644 core/cb.codecomplete/main.js delete mode 100644 core/cb.codecomplete/package.json delete mode 100644 core/cb.core/main.js delete mode 100644 core/cb.core/package.json delete mode 100644 core/cb.core/user.js delete mode 100644 core/cb.core/workspace.js delete mode 100644 core/cb.deploy.appengine/main.js delete mode 100644 core/cb.deploy.appengine/package.json delete mode 100644 core/cb.deploy.ftp/main.js delete mode 100644 core/cb.deploy.ftp/package.json delete mode 100755 core/cb.deploy.ftp/upload.sh delete mode 100644 core/cb.deploy.ghpages/main.js delete mode 100644 core/cb.deploy.ghpages/package.json delete mode 100644 core/cb.deploy.heroku/api.js delete mode 100644 core/cb.deploy.heroku/main.js delete mode 100644 core/cb.deploy.heroku/package.json delete mode 100644 core/cb.deploy.parse/main.js delete mode 100644 core/cb.deploy.parse/package.json delete mode 100644 core/cb.deploy/main.js delete mode 100644 core/cb.deploy/package.json delete mode 100644 core/cb.deploy/solution.js delete mode 100644 core/cb.events.log/main.js delete mode 100644 core/cb.events.log/package.json delete mode 100644 core/cb.events.socketio/main.js delete mode 100644 core/cb.events.socketio/package.json delete mode 100644 core/cb.events.webhook/main.js delete mode 100644 core/cb.events.webhook/package.json delete mode 100644 core/cb.events/main.js delete mode 100644 core/cb.events/package.json delete mode 100644 core/cb.export/main.js delete mode 100644 core/cb.export/package.json delete mode 100644 core/cb.files.service/main.js delete mode 100644 core/cb.files.service/package.json delete mode 100644 core/cb.files.service/service.js delete mode 100644 core/cb.files.sync/environment.js delete mode 100644 core/cb.files.sync/main.js delete mode 100644 core/cb.files.sync/manager.js delete mode 100644 core/cb.files.sync/models/cursor.js delete mode 100644 core/cb.files.sync/models/document.js delete mode 100644 core/cb.files.sync/models/selection.js delete mode 100644 core/cb.files.sync/models/user.js delete mode 100644 core/cb.files.sync/package.json delete mode 100644 core/cb.files.sync/utils.js delete mode 100644 core/cb.files.sync/validators.js delete mode 100644 core/cb.hooks/main.js delete mode 100644 core/cb.hooks/package.json delete mode 100644 core/cb.logger/main.js delete mode 100644 core/cb.logger/package.json delete mode 100644 core/cb.main/main.js delete mode 100644 core/cb.main/package.json delete mode 100644 core/cb.offline/main.js delete mode 100644 core/cb.offline/manifest.js delete mode 100644 core/cb.offline/package.json delete mode 100644 core/cb.proc/http.js delete mode 100644 core/cb.proc/main.js delete mode 100644 core/cb.proc/package.json delete mode 100755 core/cb.project/appengine/detector.sh delete mode 100644 core/cb.project/appengine/index.js delete mode 100755 core/cb.project/appengine/run.sh delete mode 100644 core/cb.project/appengine/sample/app.yaml delete mode 100644 core/cb.project/appengine/sample/helloworld.py delete mode 100755 core/cb.project/c/detector.sh delete mode 100644 core/cb.project/c/index.js delete mode 100644 core/cb.project/c/sample/Makefile delete mode 100644 core/cb.project/c/sample/main.c delete mode 100755 core/cb.project/clojure/detector.sh delete mode 100644 core/cb.project/clojure/index.js delete mode 100755 core/cb.project/clojure/run.sh delete mode 100644 core/cb.project/clojure/sample/.gitignore delete mode 100644 core/cb.project/clojure/sample/project.clj delete mode 100644 core/cb.project/clojure/sample/src/helloworld.clj delete mode 100755 core/cb.project/d/detector.sh delete mode 100644 core/cb.project/d/index.js delete mode 100755 core/cb.project/d/run.sh delete mode 100644 core/cb.project/d/sample/main.d delete mode 100755 core/cb.project/dart/detector.sh delete mode 100644 core/cb.project/dart/index.js delete mode 100755 core/cb.project/dart/run_build.sh delete mode 100755 core/cb.project/dart/run_clean.sh delete mode 100755 core/cb.project/dart/run_serve.sh delete mode 100644 core/cb.project/dart/sample/pubspec.lock delete mode 100644 core/cb.project/dart/sample/pubspec.yaml delete mode 100644 core/cb.project/dart/sample/web/helloworld.css delete mode 100644 core/cb.project/dart/sample/web/helloworld.dart delete mode 100644 core/cb.project/dart/sample/web/index.html delete mode 100755 core/cb.project/django/detector.sh delete mode 100644 core/cb.project/django/index.js delete mode 100755 core/cb.project/django/run.sh delete mode 100755 core/cb.project/go/detector.sh delete mode 100644 core/cb.project/go/index.js delete mode 100755 core/cb.project/go/run.sh delete mode 100644 core/cb.project/go/sample/main.go delete mode 100755 core/cb.project/gradle/detector.sh delete mode 100644 core/cb.project/gradle/index.js delete mode 100755 core/cb.project/gradle/run.sh delete mode 100644 core/cb.project/gradle/sample/build.gradle delete mode 100755 core/cb.project/grails/detector.sh delete mode 100644 core/cb.project/grails/index.js delete mode 100755 core/cb.project/grails/run.sh delete mode 100755 core/cb.project/java/detector.sh delete mode 100644 core/cb.project/java/index.js delete mode 100755 core/cb.project/java/run.sh delete mode 100644 core/cb.project/java/sample/HelloWorld.java delete mode 100755 core/cb.project/logo/detector.sh delete mode 100644 core/cb.project/logo/index.js delete mode 100755 core/cb.project/lua/detector.sh delete mode 100644 core/cb.project/lua/index.js delete mode 100755 core/cb.project/lua/run.sh delete mode 100644 core/cb.project/lua/sample/main.lua delete mode 100644 core/cb.project/main.js delete mode 100755 core/cb.project/makefile/detector.sh delete mode 100644 core/cb.project/makefile/index.js delete mode 100755 core/cb.project/makefile/run_all.sh delete mode 100755 core/cb.project/makefile/run_clean.sh delete mode 100755 core/cb.project/maven/detector.sh delete mode 100644 core/cb.project/maven/index.js delete mode 100755 core/cb.project/maven/install.sh delete mode 100755 core/cb.project/maven/run.sh delete mode 100755 core/cb.project/meteor/detector.sh delete mode 100644 core/cb.project/meteor/index.js delete mode 100755 core/cb.project/meteor/mrt_install.sh delete mode 100755 core/cb.project/meteor/mrt_update.sh delete mode 100755 core/cb.project/meteor/run.sh delete mode 100644 core/cb.project/meteor/sample/.meteor/.gitignore delete mode 100644 core/cb.project/meteor/sample/.meteor/packages delete mode 100644 core/cb.project/meteor/sample/.meteor/release delete mode 100644 core/cb.project/meteor/sample/hello.css delete mode 100644 core/cb.project/meteor/sample/hello.html delete mode 100644 core/cb.project/meteor/sample/hello.js delete mode 100755 core/cb.project/node/detector.sh delete mode 100644 core/cb.project/node/index.js delete mode 100755 core/cb.project/node/install.sh delete mode 100755 core/cb.project/node/run.sh delete mode 100644 core/cb.project/node/sample/package.json delete mode 100644 core/cb.project/node/sample/web.js delete mode 100644 core/cb.project/package.json delete mode 100755 core/cb.project/parse/detector.sh delete mode 100644 core/cb.project/parse/index.js delete mode 100755 core/cb.project/parse/run.sh delete mode 100644 core/cb.project/parse/sample/cloud/main.js delete mode 100644 core/cb.project/parse/sample/config/global.json delete mode 100644 core/cb.project/parse/sample/public/index.html delete mode 100755 core/cb.project/php/_waitfile.sh delete mode 100755 core/cb.project/php/detector.sh delete mode 100644 core/cb.project/php/index.js delete mode 100755 core/cb.project/php/run.sh delete mode 100755 core/cb.project/php/run_apache.sh delete mode 100644 core/cb.project/php/sample/index.php delete mode 100755 core/cb.project/play/detector.sh delete mode 100644 core/cb.project/play/index.js delete mode 100755 core/cb.project/play/run.sh delete mode 100755 core/cb.project/procfile/detector.sh delete mode 100644 core/cb.project/procfile/index.js delete mode 100755 core/cb.project/procfile/run.sh delete mode 100644 core/cb.project/project.js delete mode 100755 core/cb.project/python/detector.sh delete mode 100644 core/cb.project/python/index.js delete mode 100755 core/cb.project/python/run.sh delete mode 100644 core/cb.project/python/sample/app.py delete mode 100644 core/cb.project/python/sample/requirements.txt delete mode 100755 core/cb.project/ruby/detector.sh delete mode 100644 core/cb.project/ruby/index.js delete mode 100755 core/cb.project/ruby/run.sh delete mode 100644 core/cb.project/ruby/sample/Gemfile delete mode 100644 core/cb.project/ruby/sample/main.rb delete mode 100755 core/cb.project/scala/detector.sh delete mode 100644 core/cb.project/scala/index.js delete mode 100644 core/cb.project/scala/sample/main.scala delete mode 100755 core/cb.project/static/detector.sh delete mode 100644 core/cb.project/static/index.js delete mode 100755 core/cb.project/static/run.sh delete mode 100644 core/cb.project/static/sample/index.html delete mode 100644 core/cb.rpc.addons/main.js delete mode 100644 core/cb.rpc.addons/package.json delete mode 100644 core/cb.rpc.addons/service.js delete mode 100644 core/cb.rpc.auth/main.js delete mode 100644 core/cb.rpc.auth/package.json delete mode 100644 core/cb.rpc.auth/service.js delete mode 100644 core/cb.rpc.box/main.js delete mode 100644 core/cb.rpc.box/package.json delete mode 100644 core/cb.rpc.box/service.js delete mode 100644 core/cb.rpc.codecomplete/main.js delete mode 100644 core/cb.rpc.codecomplete/package.json delete mode 100644 core/cb.rpc.codecomplete/service.js delete mode 100644 core/cb.rpc.debug/main.js delete mode 100644 core/cb.rpc.debug/package.json delete mode 100644 core/cb.rpc.debug/service.js delete mode 100644 core/cb.rpc.deploy/main.js delete mode 100644 core/cb.rpc.deploy/package.json delete mode 100644 core/cb.rpc.deploy/service.js delete mode 100644 core/cb.rpc.proc/main.js delete mode 100644 core/cb.rpc.proc/package.json delete mode 100644 core/cb.rpc.proc/service.js delete mode 100644 core/cb.rpc.project/main.js delete mode 100644 core/cb.rpc.project/package.json delete mode 100644 core/cb.rpc.project/service.js delete mode 100644 core/cb.rpc.run/main.js delete mode 100644 core/cb.rpc.run/package.json delete mode 100644 core/cb.rpc.run/service.js delete mode 100644 core/cb.rpc.search/main.js delete mode 100644 core/cb.rpc.search/package.json delete mode 100644 core/cb.rpc.search/service.js delete mode 100644 core/cb.rpc.shells/main.js delete mode 100644 core/cb.rpc.shells/package.json delete mode 100644 core/cb.rpc.shells/service.js delete mode 100644 core/cb.rpc.users/main.js delete mode 100644 core/cb.rpc.users/package.json delete mode 100644 core/cb.rpc.users/service.js delete mode 100644 core/cb.rpc/main.js delete mode 100644 core/cb.rpc/manager.js delete mode 100644 core/cb.rpc/package.json delete mode 100644 core/cb.run.file/commands.json delete mode 100644 core/cb.run.file/main.js delete mode 100644 core/cb.run.file/package.json delete mode 100644 core/cb.run.ports/main.js delete mode 100644 core/cb.run.ports/package.json delete mode 100644 core/cb.run.project/main.js delete mode 100644 core/cb.run.project/package.json delete mode 100644 core/cb.run.project/project.js delete mode 100644 core/cb.search/code.js delete mode 100644 core/cb.search/files.js delete mode 100644 core/cb.search/main.js delete mode 100644 core/cb.search/package.json delete mode 100644 core/cb.search/types.js delete mode 100644 core/cb.server/main.js delete mode 100644 core/cb.server/package.json delete mode 100644 core/cb.settings/main.js delete mode 100644 core/cb.settings/package.json delete mode 100644 core/cb.shells.stream/main.js delete mode 100644 core/cb.shells.stream/package.json delete mode 100644 core/cb.shells/main.js delete mode 100644 core/cb.shells/package.json delete mode 100644 core/cb.socket.io/main.js delete mode 100644 core/cb.socket.io/package.json delete mode 100644 core/cb.vfs.http/main.js delete mode 100644 core/cb.vfs.http/package.json delete mode 100644 core/cb.vfs/main.js delete mode 100644 core/cb.vfs/package.json delete mode 100644 core/cb.watch/init.js delete mode 100644 core/cb.watch/main.js delete mode 100644 core/cb.watch/package.json delete mode 100644 core/codebox.js delete mode 100644 core/utils.js create mode 100644 editor/collections/commands.js create mode 100644 editor/collections/packages.js create mode 100644 editor/core/application.js create mode 100644 editor/core/commands.js create mode 100644 editor/core/packages.js rename {client/core/backends => editor/core}/rpc.js (73%) create mode 100644 editor/core/statusbar.js create mode 100644 editor/main.js create mode 100644 editor/models/command.js create mode 100644 editor/models/package.js create mode 100644 editor/resources/init.js create mode 100644 editor/resources/stylesheets/main.less create mode 100644 editor/resources/stylesheets/variables.less create mode 100644 editor/utils/dialogs.js rename {client => editor}/utils/dragdrop.js (94%) rename {client => editor}/views/grid.js (99%) delete mode 100644 index.js delete mode 100755 init.sh create mode 100644 lib/index.js create mode 100644 lib/packages.js create mode 100644 lib/rpc.js create mode 100644 lib/services/fs.js create mode 100644 lib/services/index.js create mode 100644 lib/services/packages.js delete mode 100644 screenshot.png delete mode 100644 tasks/grunt-build-addons.js diff --git a/.gitignore b/.gitignore index 8cf05519..f1960bc6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,5 @@ settings.json */**/addon-built.js */**/node_modules/ /extras/ - +packages +build diff --git a/Gruntfile.js b/Gruntfile.js index e1270702..7dd69677 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,274 +1,51 @@ -module.exports = function (grunt) { - var fs = require('fs'); - var path = require("path"); - var pkg = require("./package.json"); - var _ = require('lodash'); +var path = require("path"); +var pkg = require("./package.json"); +module.exports = function (grunt) { // Path to the client src - var clientPath = path.resolve(__dirname, "client"); - - // Constants - var NW_VERSION = "0.8.4"; + var srcPath = path.resolve(__dirname, "editor"); + var buildPath = path.resolve(__dirname, "build"); // Load grunt modules - grunt.loadNpmTasks('hr.js'); - grunt.loadNpmTasks('grunt-exec'); - grunt.loadNpmTasks('grunt-contrib-compress'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-hr-builder'); // Init GRUNT configuraton grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - hr: { - build: { + "pkg": pkg, + "hr": { + "app": { + "source": path.resolve(__dirname, "node_modules/happyrhino"), + // Base directory for the application - "base": clientPath, + "base": srcPath, // Application name "name": "Codebox", // Mode debug - "debug": process.env.CLIENT_DEBUG != null, + "debug": true, // Main entry point for application "main": "main", - "index": grunt.file.read(path.resolve(clientPath, "index.html")), // Build output directory - "build": path.resolve(clientPath, "build"), + "build": buildPath, - // Static files mappage - "static": { - "images": path.resolve(clientPath, "resources", "images"), - "fonts": path.resolve(clientPath, "resources", "fonts") - }, + // Static files map + "static": {}, // Stylesheet entry point - "style": path.resolve(clientPath, "resources/stylesheets/main.less"), - - // Modules paths - 'paths': { - 'moment': 'vendors/moment' - }, - "shim": { - 'resources/resources': { - deps: [ - 'vendors/bootstrap/carousel', - 'vendors/bootstrap/dropdown', - 'vendors/bootstrap/button', - 'vendors/bootstrap/modal', - 'vendors/bootstrap/affix', - 'vendors/bootstrap/alert', - 'vendors/bootstrap/collapse', - 'vendors/bootstrap/tooltip', - 'vendors/bootstrap/popover', - 'vendors/bootstrap/scrollspy', - 'vendors/bootstrap/tab', - 'vendors/bootstrap/transition', - 'vendors/taphold' - ] - }, - 'vendors/socket.io': { - exports: 'io' - }, - 'vendors/crypto': { - exports: 'CryptoJS' - }, - 'vendors/diff_match_patch': { - exports: 'diff_match_patch' - }, - 'vendors/mousetrap': { - exports: 'Mousetrap' - }, - 'vendors/filer': { - exports: 'Filer', - deps: [ - 'vendors/idb.filesystem' - ] - } - }, - 'args': { - 'version': pkg.version, - 'debug': process.env.CLIENT_DEBUG != null - }, - 'options': { - - } - } - }, - exec: { - publish: { - command: "npm publish", - cwd: '.tmp/', - stdout: true, - stderr: true - }, - build_files_editor: { - command: "npm install", - cwd: './addons/cb.files.editor/', - stdout: true, - stderr: true - }, - clean_addons: { - command: "rm -rf */**/addon-built.js ./addons/**/node_modules", - cwd: '.', - stdout: true, - stderr: true - }, - clean_addons_tmp: { - command: "rm -rf */**/addon-built.js", - cwd: '.tmp/', - stdout: true, - stderr: true - } - }, - copy: { - // Copy most files over - tmp: { - expand: true, - dot: false, - cwd: './', - dest: '.tmp/', - src: [ - // Most files except the ones below - "./**", - - // Ignore gitignore - "!.gitignore", - - // Ignore dev related things - "!./tmp/**", - "!./.git/**", - "!./.addons/**", - "!./appBuilds/**", - - // grunt.file.copy duplicates symbolic and hard links - // so we need to copy it with the shell - "!./extras/**", - - // Only take "./client/build" - "!./client/**", - "./client/build/**", - - // Ignore some build time only modules - "./node_modules/.bin/**", - "!./node_modules/grunt/**", - "!./node_modules/grunt-*/**", - "!./node_modules/hr.js/**", - - // Exclude test directories from node modules - "!./node_modules/**/test/**", - ], - - // Preserve permissions - options: { - mode: true - } - } - }, - compress: { - tmp: { - options: { - mode: 'gzip', - pretty: true - }, - expand: true, - src: [ - // Codebox Built addons - '.tmp/addons/*/addon-built.js', - - // Ace source - '.tmp/addons/cb.files.editor/ace/**', - - // HR.js application - '.tmp/client/build/static/application.{js,css}' - ], - filter: function(src) { - // Compressable file formats - if(!_.contains([ - 'js', - 'css', - 'svg', - 'less', - 'html', - 'snippets' - ], - path.extname(src).slice(1) - )) return false; - try { - // We don't want to gzip tiny files - // Skip files < 10kb - return fs.statSync(src).size > 10*1024; - } catch(err) {} - return false; - } - } - }, - clean: { - tmp: ['.tmp/'] - }, - buildAddons: { - tmp: { - addonsFolder: ".tmp/addons/" - }, - dev: { - addonsFolder: "./addons/", - force: false + "style": path.resolve(srcPath, "resources/stylesheets/main.less") } } }); - // Load in any and all tasks in the `tasks` folder - grunt.loadTasks('tasks'); - - // Rebuild all addons - grunt.registerTask('rebuildAddons', [ - 'exec:clean_addons', - 'buildAddons:dev' - ]); - // Build grunt.registerTask('build', [ - 'hr', - 'exec:build_files_editor', - 'buildAddons:dev' + 'hr:app' ]); - // Build tmp directory - grunt.registerTask('tmp', [ - 'build', - 'clean:tmp', - 'copy:tmp', - 'exec:clean_addons_tmp', - 'buildAddons:tmp', - 'compress:tmp' - ]); - - // Publish to NPM - grunt.registerTask('publish', [ - 'tmp', - 'exec:publish', - 'clean:tmp' - ]); - - // Run - grunt.registerTask('run', function() { - var done = this.async(); - var options = this.options({}); - - grunt.util.spawn({ - cmd: "node", - opts: { - cwd: path.resolve(__dirname), - stdio: 'inherit' - }, - args: ["./bin/codebox.js", "run"] - }, done); - }); - grunt.registerTask('default', [ - 'build', - 'run' + 'build' ]); }; diff --git a/README.md b/README.md index 01266b44..51f6474c 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,3 @@ # Codebox -> "Open source cloud & desktop IDE." -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. - -Codebox is built with web technologies: `node.js`, `javascript`, `html` and `less`. The IDE possesses a very modular and extensible architecture, that allows you to build your own features with through add-ons. Codebox is the first open and modular IDE capable of running both on the Desktop and in the cloud (with offline support). - -The project is open source under the [Apache 2.0](https://github.com/FriendCode/codebox/blob/master/LICENSE) license. -A screencast of the IDE is available on [Youtube](https://www.youtube.com/watch?v=xvPEngyXA2A). - -![Image](https://raw.github.com/FriendCode/codebox/master/screenshot.png) - -## How to install and run Codebox - -#### Desktop Applications - -Installers for the latest stable build for **Mac** and **Linux** can be downloaded on the [release page](https://github.com/FriendCode/codebox/releases). - -Instructions on how to install it can be found for each release. - -#### Install from NPM - -Codebox can be installed as a Node package and use programatically or from the command line. - -Install Codebox globally using NPM: -``` -$ npm install -g codebox -``` - -And start the IDE from the command line: -``` -$ 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. - -Others comand line options are available and can be list with: ```codebox --help```. For deeper configuration, take a look at the documentation about [environment variables](http://help.codebox.io/ide/env.html). - -#### 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. - -## Helping Codebox - -**I want to help with the code:** Codebox accepts pull-requests, please see the [Contributing to Codebox](https://github.com/FriendCode/codebox/blob/master/CONTRIBUTING.md) guide for information on contributing to this project. And don't forget to add your contact informations on the AUTHORS list. - -**I found a bug:** File it as an [issue](https://github.com/FriendCode/codebox/issues) and please describe as much as possible the bug and the context. - -**I have a new suggestion:** For feature requests please first check [the issues list](https://github.com/FriendCode/codebox/issues) to see if it's already there. If not, feel free to file it as an issue and to define the label **enhancement**. - -## Contact info - -* **Website:** [www.codebox.io](https://www.codebox.io) -* **Twitter:** [@CodeboxIO](https://twitter.com/CodeboxIO) -* **Blog:** [blog.codebox.io](http://blog.codebox.io) -* **Youtube:** [Codebox Channel](http://www.youtube.com/channel/UCWocQwS2VmDS3Ej0LQYWVIw) +This is an unstable version of the new codebox. diff --git a/addons/cb.debug/client.js b/addons/cb.debug/client.js deleted file mode 100644 index 54e005de..00000000 --- a/addons/cb.debug/client.js +++ /dev/null @@ -1,72 +0,0 @@ -define([ - "views/tab", - "settings" -], function(DebugTab, settings) { - var Q = codebox.require("hr/promise"); - var _ = codebox.require("hr/utils"); - var File = codebox.require("models/file"); - var toolbar = codebox.require("core/commands/toolbar"); - var dialogs = codebox.require("utils/dialogs"); - var files = codebox.require("core/files"); - var tabs = codebox.require("core/tabs"); - var box = codebox.require("core/box"); - var debugManager = codebox.require("core/debug/manager"); - - // Add files handler - var filesHandler = files.addHandler("debug", { - icon: "bug", - name: "Debug", - valid: function(file) { - return (!file.isDirectory()); - }, - open: function(file) { - if (file.path() != settings.user.get("path")) { - settings.user.set("path", file.path()); - settings.user.save(); - } - - return debugManager.open() - .then(function(dbg) { - return tabs.add(DebugTab, { - 'dbg': dbg, - 'path': file.path(), - 'tool': settings.user.get("tool") == "auto" ? null : settings.user.get("tool"), - 'argument': settings.user.get("argument") - }, { - 'type': "debug", - 'section': "debug" - }); - }); - } - }); - - // Debugging command - toolbar.register("debug.open", { - category: "Debug", - title: "Debugger", - description: "Open Debugger", - icons: { - 'default': "bug", - }, - offline: false, - shortcuts: [ - "alt+d" - ] - }, function(path) { - path = path || settings.user.get("path"); - - if (!path) { - dialogs.alert("No file specified", "Click left on a file and select 'Debug' to open it with the debugger, it'll be saved in your settings as last debugged file."); - return; - } - - var f = new File(); - - return f.getByPath(path) - .then(function() { - return filesHandler.open(f); - }, function() { - dialogs.alert("File not found", "File "+_.escape(path)+" doesn't exists. Click left on an another file and select 'Debug' to open it with the debugger."); - }); - }); -}); \ No newline at end of file diff --git a/addons/cb.debug/package.json b/addons/cb.debug/package.json deleted file mode 100644 index 040b64d1..00000000 --- a/addons/cb.debug/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.debug", - "version": "0.1.0", - "title": "Debug", - "description": "Debugger integration inside codebox", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.debug/settings.js b/addons/cb.debug/settings.js deleted file mode 100644 index 35657580..00000000 --- a/addons/cb.debug/settings.js +++ /dev/null @@ -1,42 +0,0 @@ -define([], function() { - var settings = codebox.require("core/settings"); - - // Add settings - return settings.add({ - 'namespace': "debug", - 'title': "Debug", - 'defaults': { - 'path': "", - 'argument': null, - 'tool': "auto" - }, - 'fields': { - 'path': { - 'label': 'File Path', - 'type': "text", - 'help': "Click left on a file and select 'debug' to update this file." - }, - 'argument': { - 'label': 'Arguments', - 'type': "text", - 'help': "Arguments to run the file with." - }, - 'tool': { - 'label': 'Debugger', - 'type': "select", - 'help': "Force the use of a specific debugger.", - 'options': { - 'auto': "Auto", - 'pdb': "Python Debugger", - 'gdb': "Native Debugger (GDB)", - - // Still unstable: - /* - 'jdb': "Java Debugger", - 'rdb': "Ruby Debugger" - */ - } - } - } - }); -}); \ No newline at end of file diff --git a/addons/cb.debug/stylesheets/tab.less b/addons/cb.debug/stylesheets/tab.less deleted file mode 100644 index f25aeb6b..00000000 --- a/addons/cb.debug/stylesheets/tab.less +++ /dev/null @@ -1,99 +0,0 @@ -.addon-debugger-tab { - .component-grid .grid-section { - .grid-resize-bar-h, .grid-resize-bar-v { - background: rgba(0,0,0, 0.1); - } - } - - .debug-section { - h4 { - display: block; - width: 100%; - background: rgba(0,0,0,.05); - font-size: 14px; - padding: 6px 8px; - margin: 0px; - } - - .table-container { - position: absolute; - top: 27px; - bottom: 0px; - left: 0px; - right: 0px; - display: block; - margin: 0px; - overflow: auto; - } - - .table { - margin: 0px; - - tr { - td, th { - border-top-color: rgba(0,0,0, 0.05); - - a { - color: inherit; - } - } - } - } - - &.debug-console { - .line { - padding-bottom: 3px; - border-bottom: 1px solid rgba(0,0,0, 0.05); - - * { - line-height: 1.4em; - font-size: 14px; - } - - i { - float: left; - width: 24px; - text-align: center; - } - - pre { - margin: 0px; - padding: 0px; - background: none; - color: inherit; - border: none; - display: block; - margin-left: 24px; - font-family: inherit; - } - - &.error { - color: #a94442; - } - &.input { - padding-top: 3px; - padding-bottom: 0px; - border-color: transparent; - } - - input, input:focus { - color: inherit; - background: transparent; - box-shadow: none; - font-size: 14px; - line-height: 1.4em; - padding: 0px; - margin: 0px; - height: 19px; - margin-right: 24px; - width: 90%; - border: none; - } - } - - .console-body { - - } - } - } -} \ No newline at end of file diff --git a/addons/cb.debug/views/backtrace.js b/addons/cb.debug/views/backtrace.js deleted file mode 100644 index 906dc216..00000000 --- a/addons/cb.debug/views/backtrace.js +++ /dev/null @@ -1,35 +0,0 @@ -define([ - "views/section" -], function(DebugSection) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var rpc = codebox.require("core/backends/rpc"); - - var BacktraceSection = DebugSection.extend({ - title: "Backtrace", - formats: [ - { - id: "filename", - title: "File", - type: "file" - }, - { - id: "line", - title: "Line" - } - ], - - update: function() { - var that = this; - - return this.dbg.backtrace() - .then(function(stack) { - that.clearLines(); - _.each(stack, that.addLine, that); - }); - } - }); - - return BacktraceSection; -}); diff --git a/addons/cb.debug/views/breakpoints.js b/addons/cb.debug/views/breakpoints.js deleted file mode 100644 index 15a16211..00000000 --- a/addons/cb.debug/views/breakpoints.js +++ /dev/null @@ -1,39 +0,0 @@ -define([ - "views/section" -], function(DebugSection) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var rpc = codebox.require("core/backends/rpc"); - - var BreakpointsSection = DebugSection.extend({ - title: "Breakpoints", - formats: [ - { - id: "num", - title: "#" - }, - { - id: "filename", - title: "File", - type: "file" - }, - { - id: "line", - title: "Line" - } - ], - - update: function() { - var that = this; - - return this.dbg.breakpoints() - .then(function(breakpoints) { - that.clearLines(); - _.each(breakpoints, that.addLine, that); - }); - } - }); - - return BreakpointsSection; -}); diff --git a/addons/cb.debug/views/console.js b/addons/cb.debug/views/console.js deleted file mode 100644 index 718740f5..00000000 --- a/addons/cb.debug/views/console.js +++ /dev/null @@ -1,107 +0,0 @@ -define([], function() { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - - var ConsoleSection = hr.View.extend({ - className: "debug-section debug-console", - title: "Console", - defaults: { - dbg: null - }, - events: { - "click": "focus" - }, - logIcons: { - 'log': "fa fa-blank", - 'input': "fa fa-angle-right", - 'error': "fa fa-times-circle" - }, - - initialize: function(options) { - var that = this; - ConsoleSection.__super__.initialize.apply(this, arguments); - - // Debugger client - this.dbg = this.options.dbg; - - this.$title = $("

        ", { - 'text': this.title - }); - - this.$container = $("
        ", { - 'class': "table-container" - }); - - this.$body = $("
        ", { - 'class': "console-body" - }); - - this.$input = $("", { - 'class': "form-control input-sm", - 'keyup': function(e) { - var key = e.which || e.keyCode; - var code = $(e.currentTarget).val(); - - if (key == 13) { - /* ENTER */ - e.preventDefault(); - - that.eval(code); - $(e.currentTarget).val(""); - } - } - }); - - - this.$body.appendTo(this.$container); - this.$input.appendTo($("
        ", { - 'class': "line input", - 'html': '' - }).appendTo(this.$container)); - - this.$title.appendTo(this.$el); - this.$container.appendTo(this.$el); - - return this; - }, - - addLine: function(output) { - var $line = $("
        ", { - 'class': "line "+output.type - }); - var $pre = $("
        ", {
        -                'text': output.content
        -            });
        -            var $icon = $("", {
        -                'class': this.logIcons[output.type]
        -            });
        -
        -            $icon.appendTo($line);
        -            $pre.appendTo($line);
        -
        -            $line.appendTo(this.$body);
        -
        -            // Scroll
        -            this.$container.animate({ scrollTop: this.$container[0].scrollHeight}, 100);
        -        },
        -
        -        // Eval some code
        -        eval: function(code) {
        -            this.addLine({
        -                type: "input",
        -                content: code
        -            });
        -            return this.dbg.eval(code)
        -            .then(_.bind(this.addLine, this));
        -        },
        -
        -        // Focus the console
        -        focus: function(e) {
        -            if (e) e.preventDefault();
        -            this.$input.focus();
        -        }
        -    });
        -
        -    return ConsoleSection;
        -});
        diff --git a/addons/cb.debug/views/locals.js b/addons/cb.debug/views/locals.js
        deleted file mode 100644
        index 650bb79d..00000000
        --- a/addons/cb.debug/views/locals.js
        +++ /dev/null
        @@ -1,39 +0,0 @@
        -define([
        -    "views/section"
        -], function(DebugSection) {
        -    var _ = codebox.require("hr/utils");
        -    var $ = codebox.require("hr/dom");
        -    var hr = codebox.require("hr/hr");
        -    var rpc = codebox.require("core/backends/rpc");
        -
        -    var LocalsSection = DebugSection.extend({
        -        title: "Locals",
        -        formats: [
        -            {
        -                id: "name",
        -                title: "Name"
        -            },
        -            {
        -                id: "value",
        -                title: "Value"
        -            }
        -        ],
        -
        -        update: function() {
        -            var that = this;
        -            
        -            return this.dbg.locals()
        -            .then(function(locals) {
        -                that.clearLines();
        -                _.each(locals, function(value, key) {
        -                    that.addLine({
        -                        'name': key,
        -                        'value': value
        -                    });
        -                });
        -            });
        -        }
        -    });
        -
        -    return LocalsSection;
        -});
        diff --git a/addons/cb.debug/views/section.js b/addons/cb.debug/views/section.js
        deleted file mode 100644
        index 55222b31..00000000
        --- a/addons/cb.debug/views/section.js
        +++ /dev/null
        @@ -1,100 +0,0 @@
        -define([], function() {
        -    var _ = codebox.require("hr/utils");
        -    var $ = codebox.require("hr/dom");
        -    var hr = codebox.require("hr/hr");
        -
        -    var files = codebox.require("core/files");
        -
        -    var DebugSection = hr.View.extend({
        -        className: "debug-section",
        -        defaults: {
        -            dbg: null
        -        },
        -        events: {},
        -        formats: [],
        -
        -        initialize: function(options) {
        -            DebugSection.__super__.initialize.apply(this, arguments);
        -            
        -            // Debugger client
        -            this.dbg = this.options.dbg;
        -
        -            this.$title = $("

        ", { - 'text': this.title - }); - - this.$container = $("
        ", { - 'class': "table-container" - }); - - this.$table = $("", { - 'class': "table" - }); - - this.$title.appendTo(this.$el); - this.$container.appendTo(this.$el); - this.$table.appendTo(this.$container); - - this.clearLines(); - - return this; - }, - - // Render - render: function() { - return this.ready(); - }, - - // Clear items - clearLines: function() { - this.$table.empty(); - - // Initiliaze format - var $line = $(""); - - _.each(this.formats, function(key) { - $(""); - - _.each(this.formats, function(key) { - var $e; - var value = item[key.id]; - - if (key.type == "file") { - $e = $("", { - 'href': "#", - 'text': value, - 'click': function(e) { - e.preventDefault(); - files.open(value, { - line: item.line - }); - } - }) - } else { - $e = $("", { - 'text': value - }); - } - - $e.appendTo($("
        ", { - 'text': key.title - }).appendTo($line); - }); - $line.appendTo(this.$table); - }, - - // Add an item - addLine: function(item) { - var $line = $("
        ").appendTo($line)); - }); - $line.appendTo(this.$table); - }, - - // Update content - update: function() { - // to defined - } - }); - - return DebugSection; -}); diff --git a/addons/cb.debug/views/tab.js b/addons/cb.debug/views/tab.js deleted file mode 100644 index dcff112b..00000000 --- a/addons/cb.debug/views/tab.js +++ /dev/null @@ -1,201 +0,0 @@ -define([ - "views/section", - "views/locals", - "views/backtrace", - "views/breakpoints", - "views/console", - "less!stylesheets/tab.less" -], function( DebugSection, LocalsSection, BacktraceSection, BreakpointsSection, ConsoleSection) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var Command = codebox.require("models/command"); - var Tab = codebox.require("views/tabs/base"); - var box = codebox.require("core/box"); - var user = codebox.require("core/user"); - var dialogs = codebox.require("utils/dialogs"); - var debugManager = codebox.require("core/debug/manager"); - - var GridView = codebox.require("views/grid"); - - var DebugTab = Tab.extend({ - className: Tab.prototype.className+ " addon-debugger-tab", - defaults: { - - }, - menuTitle: "Debugger", - events: { - - }, - - initialize: function(options) { - var that = this; - DebugTab.__super__.initialize.apply(this, arguments); - - this.dbg = this.options.dbg; - - - // Create sections - this.console = new ConsoleSection({ - dbg: this.dbg - }); - this.locals = new LocalsSection({ - dbg: this.dbg - }); - this.backtrace = new BacktraceSection({ - dbg: this.dbg - }); - this.breakpoints = new BreakpointsSection({ - dbg: this.dbg - }); - - // Listen events - this.listenTo(this.dbg, "close", function() { - this.closeTab(); - }); - this.listenTo(this.dbg, "update", function() { - this.updateState(); - }); - this.listenTo(this.dbg, "error", function(err) { - this.console.addLine({ - type: "error", - content: err.message || err - }); - }); - this.listenTo(this.dbg, "log", function(message) { - this.console.addLine({ - type: "log", - content: message - }); - }); - - // Create grids for sections - this.gridV = new GridView({ - columns: 1 - }); - this.gridH = new GridView({ - columns: 1000 - }); - - this.gridH.addView(this.breakpoints); - this.gridH.addView(this.backtrace); - this.gridH.addView(this.locals); - - this.gridV.addView(this.gridH); - this.gridV.addView(this.console); - this.gridV.appendTo(this); - - - // Base commands: start, stop, next, continue, restart - this.commandStart = new Command({}, { - title: "Start", - action: function() { - that.dbg.start(that.options.argument); - } - }); - this.commandStop = new Command({}, { - title: "Stop", - action: function() { - that.dbg.stop(); - } - }); - this.commandNext = new Command({}, { - title: "Next", - action: function() { - that.dbg.next(); - } - }); - this.commandContinue = new Command({}, { - title: "Continue", - action: function() { - that.dbg.cont(); - } - }); - this.commandRestart = new Command({}, { - title: "Restart", - action: function() { - that.dbg.restart(); - } - }); - - // Describe debug tab - this.setTabTitle("Debugger "+this.options.path); - - // Tab menu - this.menu - .menuSection([ - this.commandStart, - this.commandStop, - this.commandRestart - ]) - .menuSection([ - this.commandNext, - this.commandContinue - ]) - .menuSection([ - { - 'type': "checkbox", - 'title': "Exit", - 'action': function(state) { - that.closeTab(); - } - } - ]); - - // Statusbar menu - this.statusbar.add([ - this.commandRestart, - this.commandContinue, - this.commandNext, - this.commandStop, - this.commandStart - ]); - - // Start debugger - this.dbg.init({ - 'tool': this.options.tool, - 'path': this.options.path, - 'breakpoints': debugManager.breakpoints.all() - }); - - // Bind event on breakponts changements - this.listenTo(debugManager.breakpoints, "change", function(e) { - if (e.change == "add") { - this.dbg.breakpointAdd({ - 'path': e.path, - 'line': e.line - }); - } else { - var point = this.dbg.getBreakpoint({ - 'path': e.path, - 'line': e.line - }); - - if (point) this.dbg.breakpointRemove(point.num); - } - }); - - // Bind close tab - this.on("tab:close", function() { - this.dbg.close(); - }, this); - - return this; - }, - - // Render - render: function() { - return this.ready(); - }, - - - // Update debugegr state: stack, locals - updateState: function() { - this.locals.update(); - this.backtrace.update(); - this.breakpoints.update(); - } - }); - - return DebugTab; -}); diff --git a/addons/cb.deploy/client.js b/addons/cb.deploy/client.js deleted file mode 100644 index 34de51eb..00000000 --- a/addons/cb.deploy/client.js +++ /dev/null @@ -1,240 +0,0 @@ -define([], function() { - var Q = codebox.require("hr/promise"); - var _ = codebox.require("hr/utils"); - var dialogs = codebox.require("utils/dialogs"); - var box = codebox.require("core/box"); - var rpc = codebox.require("core/backends/rpc"); - var Command = codebox.require("models/command"); - var menu = codebox.require("core/commands/menu"); - var user = codebox.require("core/user"); - - var CryptoJS = codebox.require("vendors/crypto"); - - // Settings for deployment - var settings = user.settings("deploymentSolutions"); - - // Return list of solutions - var getSolutionTypes = function() { - return rpc.execute("deploy/solutions"); - }; - - // Return infos for a specific solution type - var getSolutionType = function(id) { - return getSolutionTypes().then(function(_types) { - var s = _.find(_types, function(_type) { - return _type.id == id; - }); - if (!s) throw "Invalid solution type"; - return s; - }); - }; - - // Id for a solution name - var solutionId = function(name) { - return CryptoJS.MD5(name).toString(); - }; - - // Add a solution - var addSolution = function(name, solution) { - var solutions = settings.get("solutions", {}); - var id = solutionId(name); - if (solutions[id]) return Q.reject("Solution already exists"); - solutions[id] = { - 'name': name, - 'type': solution, - 'settings': {} - }; - settings.set("solutions", solutions); - return settings.save().then(function() { - return solutions[id]; - }); - }; - - // Remove a solution - var removeSolution = function(id) { - var solutions = settings.get("solutions", {}); - if (!solutions[id]) return Q.reject("Invalid solution"); - delete solutions[id]; - settings.set("solutions", solutions); - return settings.save(); - }; - - // Run a solution - var runSolution = function(solution, actionId) { - if (_.isString(solution)) solution = getSolution(solution); - - return rpc.execute("deploy/run", { - 'solution': solution.type, - 'action': actionId, - 'config': solution.settings - }) - .then(function(data) { - // Handle shells - if (data.shellId) { - box.openTerminal(data.shellId, { - id: "deploy."+solutionId(solution.name)+"."+actionId, - title: data.title || "Deployment to "+solution.name+" ("+actionId+")", - icons: { - 'default': "fa-cloud-upload", - } - }); - } - // Handle message - else if (data.message) { - dialogs.alert("Deployment to "+solution.name+" ("+actionId+")", data.message); - } - }, function(err) { - dialogs.alert("Error with "+solution.name, err.message || err); - }); - }; - - // Return solution informations by its id - var getSolution = function(id) { - var solutions = settings.get("solutions", {}); - return solutions[id]; - }; - - var setSolutionSettings = function(id, _settings) { - var solutions = settings.get("solutions", {}); - solutions[id].settings = _settings; - settings.set("solutions", solutions); - }; - - // Open settings for a solution - var openSettings = function(id) { - var solution = getSolution(id); - return getSolutionType(solution.type).then(function(_type) { - return dialogs.fields("Configuration for "+_.escape(solution.name), _type.settings, solution.settings); - }) - .then(function(newSettings) { - setSolutionSettings(id, newSettings); - return settings.save(); - }); - }; - - // Command to add a new deployment solution - var addCommand = Command.register("deploy.solutions.add", { - category: "Deployment", - title: "Add Solution", - description: "Add a solution", - offline: false, - action: function(page) { - getSolutionTypes().then(function(solutionTypes) { - return dialogs.fields("Add Deployment Solution", { - name: { - type: "text", - label: "Label" - }, - solution: { - type: "select", - label: "Type", - options: _.object(_.map(solutionTypes, function(solution) { - return [ - solution.id, - solution.name - ]; - })) - } - }); - }) - .then(function(data) { - if (!data.name || !data.solution) throw "Need 'name' and 'solution'"; - return addSolution(data.name, data.solution); - }) - .then(function(solution) { - return openSettings(solutionId(solution.name)); - }); - } - }); - - // Command to remove a solution - var removeCommand = Command.register("deploy.solutions.remove", { - category: "Deployment", - title: "Remove Solution", - description: "Remove a solution", - offline: false, - action: function() { - dialogs.select("Remove Deployment Solution", - "Select a solution, this solution and its configuration will be removed from your settings.", - _.chain(settings.get("solutions", {})) - .map(function(solution, id) { - return [id, solution.name] - }) - .object() - .value()).then(removeSolution); - } - }); - - // Deploy Menu - var deployMenu = menu.register("deploy", { - title: "Deploy", - position: 90, - offline: false - }); - - // Update list of solutions - var updateSolutions = function() { - return getSolutionTypes().then(function(_types) { - var solutions = settings.get("solutions", {}); - - deployMenu.clearMenu(); - - // Add command to create new solutions - deployMenu.menuSection(_.compact([ - addCommand, - _.size(solutions) > 0 ? removeCommand : null - ])); - - // Add all solutions - if (_.size(solutions) == 0) return; - deployMenu.menuSection( - _.chain(solutions) - .map(function(solution, solutionId) { - var solutionType = _.find(_types, function(_type) { - return _type.id == solution.type; - }); - if (!solutionType) return null; - - var command = Command.register({ - title: solution.name, - type: "menu", - offline: false, - action: function() { - openSettings(solutionId(solution.name)); - } - }); - - command.menuSection( - _.map(solutionType.actions, function(action) { - return { - title: action.name, - offline: false, - action: function() { - return runSolution(solution, action.id); - } - }; - }) - ); - - command.menuSection([ - { - title: "Configure", - offline: false, - action: function() { - openSettings(solutionId); - } - } - ]); - - return command; - }) - .compact() - .value() - ); - }); - - }; - - updateSolutions(); - settings.change(updateSolutions); -}); \ No newline at end of file diff --git a/addons/cb.deploy/package.json b/addons/cb.deploy/package.json deleted file mode 100644 index c341704f..00000000 --- a/addons/cb.deploy/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.deploy", - "version": "0.1.0", - "title": "Project", - "description": "Deployment menu for all solutions", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.files.editor/ace.js b/addons/cb.files.editor/ace.js deleted file mode 100644 index 7090ee09..00000000 --- a/addons/cb.files.editor/ace.js +++ /dev/null @@ -1,10 +0,0 @@ -define([ - "ace/ace", - - // All the ace extensions we used - "ace/ext-modelist", - "ace/ext-language_tools", - "ace/ext-whitespace" -], function() { - return window.ace; -}); \ No newline at end of file diff --git a/addons/cb.files.editor/build.sh b/addons/cb.files.editor/build.sh deleted file mode 100755 index e6b2524b..00000000 --- a/addons/cb.files.editor/build.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -# This script will prepare ace for running properly - -RM_EXTENSIONS=(elastic_tabstops_lite chromevox statusbar emmet error_marker keybinding_menu old_ie textarea themelist static_highlight split spellcheck settings_menu) -RM_LANG=(abap cobol forth mushcode vbscript tcl velocity pascal powershell asciidoc apache_conf ada soy_template verilog vhdl autohotkey batchfile c9search) - -# Sed like command using perl -# We need this because on OS X, sed does not support ignoring case -function PSED { - perl -C -e 'use utf8;' -i -pe $1 $2 -} - -# Remove useless extensions -for ext in ${RM_EXTENSIONS[*]} -do - echo "Remove extension $ext" - rm -f ace/ext-$ext.js -done - -# Remove languages -for lang in ${RM_LANG[*]} -do - echo "Remove language $lang" - - # Remove the files - rm -f "ace/mode-$lang.js" "ace/snippets/$lang.js" "ace/worker-$lang.js" - - # Remove any references in ext-modelist - - # Remove extension mapping - PSED "s/,?${lang}\:\[\"[^\"]+?\"\]//gi" ace/ext-modelist.js - - # Remove readable name mapping - PSED "s/,?${lang}\:\"\w+\"//gi" ace/ext-modelist.js -done - -# Cleanup our previous replaces -# Remove any bad comas left over from modelist -PSED "s/\{,/{/gi" ace/ext-modelist.js -PSED "s/,\}/{/gi" ace/ext-modelist.js - -# Detect empty snippets -# using the value of their 'snippetText' variable -SREGEX="snippetText=(\"\"|\'\')" - -# Remove empty snippet files -find ./ace/snippets -name "*.js" -print | \ -xargs grep -E ${SREGEX} -l | \ -xargs rm -f - - -# Remove useless themes -echo "Removes themes" -rm -rf ace/theme-*.js \ No newline at end of file diff --git a/addons/cb.files.editor/client.js b/addons/cb.files.editor/client.js deleted file mode 100644 index 9619f770..00000000 --- a/addons/cb.files.editor/client.js +++ /dev/null @@ -1,28 +0,0 @@ -define([ - "ace", - "editor/view" -], function(ace, FileEditorView) { - var $ = codebox.require("hr/dom"); - var commands = codebox.require("core/commands/toolbar"); - var files = codebox.require("core/files"); - - var aceconfig = ace.require("ace/config"); - aceconfig.set("basePath", "static/addons/cb.files.editor/ace"); - - // Add files handler - files.addHandler("ace", { - 'name': "Edit", - 'fallback': true, - 'setActive': true, - 'position': 5, - 'View': FileEditorView, - 'valid': function(file) { - return (!file.isDirectory()); - } - }); - - // Return globals - return { - 'ace': ace - }; -}); \ No newline at end of file diff --git a/addons/cb.files.editor/download_ace.sh b/addons/cb.files.editor/download_ace.sh deleted file mode 100755 index 02e24e97..00000000 --- a/addons/cb.files.editor/download_ace.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Ace version to pull -ACE_VERSION="ec3677e978de275bbe1ff8f21f4a157bdb9b8f9e" -ACE_URL="https://github.com/FriendCode/ace-builds/archive/${ACE_VERSION}.tar.gz" - -# Ace build we want to keep -ACE_SUB="src-min-noconflict" - -# Current folder -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# Destination folder -DEST="${DIR}/ace" - -# Make sure $DEST exists -mkdir -p ${DEST} - -# Check if it's already downloaded -if [ -f "${DEST}/ace.js" ]; then - echo "Ace is already downloaded" - echo "'rm -rf ace' if you want to redownload" - exit 0 -fi - -# Download tar.gz and pipe to tar -# decompressing it to $DEST -wget -O - ${ACE_URL} | tar -xzv -C ${DEST} --strip-components=2 ace-builds-${ACE_VERSION}/${ACE_SUB} diff --git a/addons/cb.files.editor/editor/breakpoints.js b/addons/cb.files.editor/editor/breakpoints.js deleted file mode 100644 index 8bad72b9..00000000 --- a/addons/cb.files.editor/editor/breakpoints.js +++ /dev/null @@ -1,102 +0,0 @@ -define([], function() { - var hr = codebox.require("hr/hr"); - - var EditorBreakpoints = hr.Class.extend({ - initialize: function() { - var that = this; - EditorBreakpoints.__super__.initialize.apply(this, arguments); - - this.editor = this.options.editor; - this.$editor = this.editor.editor; - - // Breakpoints list change, it signals the change to the box - this.$editor.session.on("changeBreakpoint", function() { - that.signalBreakpoints(); - }); - - // Add remove breakpoints by clicking the gutter - this.$editor.on("guttermousedown", function(e) { - var target = e.domEvent.target; - if (target.className.indexOf("ace_gutter-cell") == -1) - return; - if (!e.editor.isFocused()) - return; - if (e.clientX > 25 + target.getBoundingClientRect().left) - return; - - var row = e.getDocumentPosition().row; - - if (that.hasBreakpoint(row)) { - e.editor.session.clearBreakpoint(row) - } else { - e.editor.session.setBreakpoint(row); - } - e.stop(); - }); - - // Document change -> update breakpoints list - this.$editor.session.doc.on("change", function(e) { - var delta = e.data; - var range = delta.range; - var changed = false; - - if (range.end.row == range.start.row) - return; - - var len, firstRow; - len = range.end.row - range.start.row; - if (delta.action == "insertText") { - firstRow = range.start.column ? range.start.row + 1 : range.start.row; - } - else { - firstRow = range.start.row; - } - - if (delta.action[0] == "i") { - var args = Array(len); - args.unshift(firstRow, 0); - changed = true; - that.$editor.session.$breakpoints.splice.apply(that.$editor.session.$breakpoints, args); - } - else { - var rem = that.$editor.session.$breakpoints.splice(firstRow + 1, len); - - if (!that.$editor.session.$breakpoints[firstRow]) { - for (var i = rem.length; i--; ) { - if (rem[i]) { - changed = true; - that.$editor.session.$breakpoints[firstRow] = rem[i]; - break; - } - } - } - } - - if (changed) that.signalBreakpoints(); - }); - }, - - // Return list of active breakpoints in ace - getBreakpoints: function() { - return _.chain(this.$editor.session.getBreakpoints() || {}) - .map(function(value, key) { - if (!value) return null; - return parseInt(key)+1; - }) - .compact() - .value(); - }, - - // Check if has breakpoint at a line - hasBreakpoint: function(row) { - return _.contains(this.getBreakpoints(), row+1) - }, - - // Signal breakpoints list - signalBreakpoints: function() { - this.editor.model.setBreakpoints(this.getBreakpoints()); - } - }); - - return EditorBreakpoints; -}); \ No newline at end of file diff --git a/addons/cb.files.editor/editor/codecomplete.js b/addons/cb.files.editor/editor/codecomplete.js deleted file mode 100644 index 8f2fe97a..00000000 --- a/addons/cb.files.editor/editor/codecomplete.js +++ /dev/null @@ -1,31 +0,0 @@ -define([ - "ace" -], function(ace) { - var rpc = codebox.require("core/backends/rpc"); - var langTools = ace.require("ace/ext/language_tools"); - - var normalizeTag = function(tag) { - return { - 'name': tag.name, - 'value': tag.name, - 'score': 0, - 'meta': tag.meta || "" - }; - }; - - langTools.addCompleter({ - getCompletions: function(editor, session, pos, prefix, callback) { - if (prefix.length === 0) { callback(null, []); return } - - rpc.execute("codecomplete/get", { - 'query': prefix - }).then(function(data) { - callback(null, _.map(data.results, normalizeTag)); - }, function(err) { - callback(err); - }); - } - }); - - return {}; -}); \ No newline at end of file diff --git a/addons/cb.files.editor/editor/jshint.js b/addons/cb.files.editor/editor/jshint.js deleted file mode 100644 index 194bd2fe..00000000 --- a/addons/cb.files.editor/editor/jshint.js +++ /dev/null @@ -1,48 +0,0 @@ -define([], function() { - var File = codebox.require("models/file"); - var box = codebox.require("core/box"); - var Q = codebox.require("hr/promise"); - var _ = codebox.require("hr/utils"); - - var jshintFile = new File(); - - // Read the JSHint configuration from a file - var readJshintSettings = _.memoize(function() { - // Get file - return jshintFile.getByPath("/.jshintrc") - .then(function() { - //Read file - return jshintFile.read(); - }) - .then(function(content) { - // Parse JSON - return JSON.parse(content); - }); - }); - - // Apply JSHint config from a file to an editor - var applyJshintSettings = function(editor) { - var session = editor.session; - if (session.getMode().$id != "ace/mode/javascript") return Q.reject(new Error("Not a javascript editor")); - if (!session.$worker) return Q.reject(new Error("No JSHint worker")); - - return readJshintSettings() - .then(function(options) { - if (session.$worker) { - //session.$worker.send("changeOptions",[ {undef: true}]) - // or - session.$worker.send("setOptions",[options]) - } - return options; - }); - }; - - // Clear memoize cache when file change - jshintFile.on("file:change", function(e) { - readJshintSettings.cache = {}; - }); - - return { - 'applySettings': applyJshintSettings - }; -}); \ No newline at end of file diff --git a/addons/cb.files.editor/editor/settings.js b/addons/cb.files.editor/editor/settings.js deleted file mode 100644 index 61e696dd..00000000 --- a/addons/cb.files.editor/editor/settings.js +++ /dev/null @@ -1,93 +0,0 @@ -define([], function() { - var settings = codebox.require("core/settings"); - - // Add settings - var userSettings = settings.add({ - 'namespace': "editor", - 'title': "Code Editor", - 'defaults': { - 'theme': "github", - 'fontsize': "12", - 'printmargincolumn': 80, - 'showinvisibles': false, - 'showprintmargin': false, - 'highlightactiveline': false, - 'wraplimitrange': 80, - 'enablesoftwrap': false, - 'enablesofttabs': true, - 'stripspaces': false, - 'autocollaboration': true, - 'tabsize': 4, - 'keyboard': "textinput" - }, - 'fields': { - 'keyboard': { - 'label': "Keyboard mode", - 'type': "select", - 'options': { - "vim": "Vim", - "emacs": "Emacs", - "textinput": "Default" - } - }, - 'fontsize': { - 'label': "Font Size", - 'type': "number", - 'min': 10, - 'max': 30, - 'step': 1 - }, - 'printmargincolumn': { - 'label': "Print Margin Column", - 'type': "number", - 'min': 0, - 'max': 1000, - 'step': 1 - }, - 'wraplimitrange': { - 'label': "Wrap Limit Range", - 'type': "number", - 'min': 0, - 'max': 1000, - 'step': 1 - }, - 'autocollaboration': { - 'label': "Auto enable realtime collaboration", - 'type': "checkbox" - }, - 'showprintmargin': { - 'label': "Show Print Margin", - 'type': "checkbox" - }, - 'showinvisibles': { - 'label': "Show Invisibles", - 'type': "checkbox" - }, - 'highlightactiveline': { - 'label': "Highlight Active Line", - 'type': "checkbox" - }, - 'enablesoftwrap': { - 'label': "Enable Soft Wrap", - 'type': "checkbox" - }, - 'enablesofttabs': { - 'label': "Use Soft Tabs", - 'type': "checkbox" - }, - 'stripspaces': { - 'label': "Strip Whitespaces", - 'type': "checkbox" - }, - 'tabsize': { - 'label': "Tab Size", - 'type': "number", - 'min': 0, - 'max': 1000, - 'step': 1 - } - } - }); - - return userSettings; -}); \ No newline at end of file diff --git a/addons/cb.files.editor/editor/view.js b/addons/cb.files.editor/editor/view.js deleted file mode 100644 index fd723f82..00000000 --- a/addons/cb.files.editor/editor/view.js +++ /dev/null @@ -1,583 +0,0 @@ -define([ - "ace", - "editor/breakpoints", - "editor/codecomplete", - "editor/jshint", - "editor/settings", - "theme/textmate", - "text!templates/file.html", - "less!stylesheets/file.less", -], function(ace, Breakpoints, codecomplete, jshint, editorSettings, aceDefaultTheme, templateFile) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var alerts = codebox.require("utils/alerts"); - var FilesTabView = codebox.require("views/files/tab"); - var FileSync = codebox.require("utils/filesync"); - var user = codebox.require("core/user"); - var menu = codebox.require("core/commands/menu"); - var settings = codebox.require("core/settings"); - var Command = codebox.require("models/command"); - var themes = codebox.require("core/themes"); - var box = codebox.require("core/box"); - var collaborators = codebox.require("core/collaborators"); - var keyboard = codebox.require("utils/keyboard"); - var debugManager = codebox.require("core/debug/manager"); - - var logging = hr.Logger.addNamespace("editor"); - - // Import ace - var aceRange = ace.require("ace/range"); - var aceModes = ace.require("ace/ext/modelist"); - var aceLangs = ace.require("ace/ext/language_tools"); - var aceWhitespace = ace.require("ace/ext/whitespace"); - - - var FileEditorView = FilesTabView.extend({ - className: "addon-files-aceeditor", - templateLoader: "text", - template: templateFile, - defaults: {}, - events: {}, - shortcuts: { - "mod+s": "saveFile", - "mod+r": "runFile", - "mod+f": "searchInFile" - }, - - // Constructor - initialize: function() { - var that = this; - FileEditorView.__super__.initialize.apply(this, arguments); - - this.markersS = {}; - this.markersC = {}; - this._op_set = false; - - // Syntax menu command - var syntaxMenu = new Command({}, { - 'title': "Syntax", - 'type': "menu" - }); - syntaxMenu.menu.add(_.map(aceModes.modesByName, function(mode, name) { - return { - 'title': mode.caption, - 'action': function() { - that.setMode(name); - } - } - })); - - // Toggle collaboration - this.collaborationToggle = new Command({}, { - 'type': "checkbox", - 'title': "Toggle Collaboration Mode", - 'flags': this.model.isNewfile() ? "hidden": "", - 'offline': false, - 'action': function(state) { - that.editor - that.sync.updateEnv({ - 'sync': state - }); - } - }); - - // Collaborators - this.collaboratorsMenu = new Command({}, { - 'title': "Collaborators", - 'type': "menu", - 'flags': "disabled" - }); - - // Statusbar - this.editorStatusCommand = new Command({}, { - 'title': "Line 1, Column 1", - 'type': "label" - }); - this.tab.statusbar.add(this.editorStatusCommand); - this.tab.statusbar.add(this.collaboratorsMenu); - this.tab.statusbar.add(syntaxMenu); - - // Tab menu - this.tab.menu.menuSection([ - { - 'type': "action", - 'title': "Save", - 'shortcuts': [ - "mod+s" - ], - 'bindKeyboard': false, - 'action': function() { - that.sync.save(); - } - }, - { - 'type': "action", - 'title': "Run File", - 'shortcuts': [ - "mod+r" - ], - 'flags': this.model.isNewfile() ? "disabled": "", - 'bindKeyboard': false, - 'action': function() { - that.model.run(); - } - } - ]).menuSection([ - this.collaborationToggle, - syntaxMenu - ]).menuSection([ - { - 'title': "Convert Indentation to Spaces", - 'action': function() { - aceWhitespace.convertIndentation(that.editor.session, " ", editorSettings.user.get("tabsize", 4)); - } - }, - { - 'title': "Convert Indentation to Tabs", - 'action': function() { - aceWhitespace.convertIndentation(that.editor.session, "\t", 1); - } - }, - { - 'title':"Strip Whitespaces", - 'action': function() { - that.stripspaces(); - } - } - ]).menuSection([ - this.collaboratorsMenu - ]).menuSection([{ - 'type': "action", - 'title': "Settings", - 'offline': false, - 'action': function() { - settings.open("editor"); - } - }]); - - // Create sync - this.sync = new FileSync(); - this.sync.on("update:env", function(options) { - if (options.reset) { - this._op_set = true; - this.editor.setValue(""); - this._op_set = false; - } - this.collaborationToggle.toggleFlag("active", options.sync); - this.collaboratorsMenu.toggleFlag("disabled", !options.sync); - }, this); - - // Create base ace editor instance and configure it - this.$editor = $("
        ", { - 'class': "editor-ace" - }); - this.editor = ace.edit(this.$editor.get(0)); - var $doc = this.editor.session.doc; - - // Set base options - this.editor.session.setUseWorker(true); - this.editor.setOptions({ - enableBasicAutocompletion: true, - enableSnippets: true - }); - - // Force unix newline mode (for cursor position calcul) - $doc.setNewLineMode("unix"); - this.setOptions(); - - // Debug and Breakpoints - this.breakpoints = new Breakpoints({ - editor: this - }); - - // Read-only when debugger is active - this.listenTo(debugManager, "state", function(state) { - this.editor.setReadOnly(state); - }); - this.editor.setReadOnly(debugManager.isActive()); - - // Show active debug line - this.listenTo(debugManager, "position", this.updateDebugLine); - this.updateDebugLine(); - - // Bind settings changement - var update = function() { - var ops = _.extend({}, { - "mode": this.options.mode, - "readonly": this.options.readonly - }); - this.setOptions(ops); - }; - editorSettings.user.change(update, this); - user.settings("themes").change(update, this); - - // Bind editor changement -> sync - this.editor.getSession().selection.on('changeSelection', function(){ - var selection = that.editor.getSelectionRange(); - that.sync.updateUserSelection(selection.start.column, selection.start.row, selection.end.column, selection.end.row); - }); - this.editor.getSession().selection.on('changeCursor', function(){ - var cursor = that.editor.getSession().getSelection().getCursor(); - that.sync.updateUserCursor(cursor.column, cursor.row); - that.editorStatusCommand.set("title", "Line "+(cursor.row+1)+", Column "+(cursor.column+1)); - }); - this.editor.getSession().on("changeMode", function() { - jshint.applySettings(that.editor); - }); - - - // Clear command on Windows/ChromeOS Ctrl-Shift-P - this.editor.commands.addCommands([{ - name: "commandpalette", - bindKey: { - win: "Ctrl-Shift-P", - mac: "Command-Shift-P" - }, - exec: function(editor, line) { - return false; - }, - readOnly: true - }]); - - // Send change - $doc.on('change', function(d) { - if (that._op_set) return; - that.sync.updateContent(that.editor.session.getValue()); - }); - - // Bind sync -> editor - this.sync.on("file:mode", function(mode) { - this.setMode(mode) - }, this); - this.sync.on("content", function(content, oldcontent, patches) { - var selection, cursor_lead, cursor_anchor, scroll_y, operations; - - // if resync patches is null - patches = patches || []; - - // Calcul operaitons from patch - operations = this.sync.patchesToOps(patches); - - // Do some operations on selection to preserve selection - selection = this.editor.getSession().getSelection(); - - scroll_y = this.editor.getSession().getScrollTop(); - - cursor_lead = selection.getSelectionLead(); - cursor_lead = this.sync.cursorApplyOps({ - x: cursor_lead.column, - y: cursor_lead.row - }, operations, oldcontent); - - cursor_anchor = selection.getSelectionAnchor(); - cursor_anchor = this.sync.cursorApplyOps({ - x: cursor_anchor.column, - y: cursor_anchor.row - }, operations, oldcontent); - - // Set editor content - this._op_set = true; - - // Apply ace delta all in once - $doc.applyDeltas( - _.map(operations, function(op) { - return { - action: op.type+"Text", - range: { - start: that.posFromIndex(op.index), - end: that.posFromIndex(op.index + op.content.length) - }, - text: op.content - } - }) - ); - - // Check document content is as expected - if ($doc.getValue() != content) { - logging.error("Invalid operation ", content, $doc.getValue()); - $doc.setValue(content); - this.sync.sendSync(); - } - this._op_set = false; - - // Move cursors - this.editor.getSession().setScrollTop(scroll_y); - - cursor_anchor = this.sync.cursorPosByindex(cursor_anchor, content); - this.editor.getSession().getSelection().setSelectionAnchor(cursor_anchor.y, cursor_anchor.x); - - cursor_lead = this.sync.cursorPosByindex(cursor_lead, content); - this.editor.getSession().getSelection().selectTo(cursor_lead.y, cursor_lead.x); - }, this); - - // Participant cursor moves - this.sync.on("cursor:move", function(cId, c) { - var name, range = new aceRange.Range(c.y, c.x, c.y, c.x+1); - - // Remove old cursor - if (this.markersC[cId]) this.editor.getSession().removeMarker(this.markersC[cId]); - - // Calcul name - name = cId - var participant = collaborators.getById(cId); - if (participant) name = participant.get("name"); - - // Add new cursor - this.markersC[cId] = this.editor.getSession().addMarker(range, "marker-cursor marker-"+c.color.replace("#", ""), function(html, range, left, top, config){ - html.push("
        " - + "
         "+name+" 
        " - + "
         
        "); - }, true); - }, this); - - // Participant selection - this.sync.on("selection:move", function(cId, c) { - var range = new aceRange.Range(c.start.y, c.start.x, c.end.y, c.end.x); - if (this.markersS[cId]) this.editor.getSession().removeMarker(this.markersS[cId]); - this.markersS[cId] = this.editor.getSession().addMarker(range, "marker-selection marker-"+c.color.replace("#", ""), "line", false); - }, this); - - // Remove a cursor/selection - this.sync.on("cursor:remove selection:remove", function(cId) { - if (this.markersC[cId]) this.editor.getSession().removeMarker(this.markersC[cId]); - if (this.markersS[cId]) this.editor.getSession().removeMarker(this.markersS[cId]); - delete this.markersC[cId]; - delete this.markersS[cId] - }, this); - - // Participants list change - this.sync.on("participants", function() { - this.collaboratorsMenu.set("label", _.size(this.sync.participants)); - this.collaboratorsMenu.menu.reset(_.map(this.sync.participants, function(participant) { - return { - 'type': "action", - 'title': participant.user.get("name"), - 'action': function() { - that.editor.getSession().getSelection().setSelectionAnchor(participant.cursor.y, participant.cursor.x); - that.editor.getSession().getSelection().selectTo(participant.cursor.y, participant.cursor.x); - that.editor.focus(); - } - } - })); - }, this); - - // Parent tab - this.tab.on("tab:layout", function() { - this.editor.resize(); - this.editor.renderer.updateFull(); - }, this); - this.tab.on("tab:state", function(state) { - if (state) this.focus(); - }, this); - this.tab.on("tab:close", function() { - // Clear breakpoints - this.model.clearBreakpoints(); - - // Finish sync - this.sync.close(); - - // Destroy the editor - this.editor.destroy(); - - // Destroy events and instance - this.off(); - this.stopListening(); - }, this); - - // Bind editor sync state changements - this.sync.on("sync:state", function(state) { - this.editor.setReadOnly(!state); - if (!state) { - this.tab.setTabState("warning", true); - } else { - this.tab.setTabState("warning", false); - } - }, this); - this.sync.on("mode", function(mode) { - this.tab.setTabState("sync", mode == this.sync.modes.SYNC); - }, this); - this.sync.on("close", function(mode) { - this.tab.closeTab(); - }, this); - this.sync.on("error", function(err) { - alerts.show("Error: "+(err.message || err), 3000); - }, this); - - this.sync.on("sync:modified", function(state) { - this.tab.setTabState("modified", state); - }, this); - - this.sync.on("sync:loading", function(state) { - this.tab.setTabState("loading", state); - }, this); - - this.sync.once("content", function() { - this.adaptOptions(); - }, this); - - // Define file for code editor - this.sync.setFile(this.model, { - 'sync': editorSettings.user.get("autocollaboration") ? collaborators.size() > 1 : false - }); - - // Options chnagements - this.on("file:options", this.adaptOptions, this); - - this.focus(); - - var $input = this.editor.textInput.getElement(); - var handleKeyEvent = function(e) { - if (!e.altKey && !e.ctrlKey && !e.metaKey) return; - keyboard.enableKeyEvent(e); - }; - $input.addEventListener('keypress', handleKeyEvent, false); - $input.addEventListener('keydown', handleKeyEvent, false); - $input.addEventListener('keyup', handleKeyEvent, false); - }, - - // Finish rendering - finish: function() { - this.$editor.appendTo(this.$(".editor-inner")); - this.editor.resize(); - this.editor.renderer.updateFull(); - - return FileEditorView.__super__.finish.apply(this, arguments); - }, - - // Focus editor - focus: function() { - this.editor.resize(); - this.editor.renderer.updateFull(); - this.editor.focus(); - return this; - }, - - // Define editor options - setOptions: function(opts) { - var that = this; - this.options = _.defaults(opts || {}, editorSettings.user.all({}), { - mode: "text", - fontsize: "12", - printmargincolumn: 80, - showprintmargin: false, - showinvisibles: false, - highlightactiveline: false, - wraplimitrange: 80, - enablesoftwrap: false, - keyboard: "textinput", - enablesofttabs: true, - tabsize: 4 - }); - - // Ste mode - this.setMode(this.options.mode); - - // Det keyboard mode - ace.config.loadModule(["keybinding", "ace/keyboard/"+this.options.keyboard], function(binding) { - if (binding && binding.handler) that.editor.setKeyboardHandler(binding.handler); - }); - - this.editor.setTheme(themes.current().editor.theme || aceDefaultTheme); - this.$editor.css("font-size", this.options.fontsize+"px"); - this.editor.setPrintMarginColumn(this.options.printmargincolumn); - this.editor.setShowPrintMargin(this.options.showprintmargin); - this.editor.setShowInvisibles(this.options.showinvisibles); - this.editor.setHighlightActiveLine(this.options.highlightactiveline); - this.editor.getSession().setUseWrapMode(this.options.enablesoftwrap); - this.editor.getSession().setWrapLimitRange(this.options.wraplimitrange, this.options.wraplimitrange); - this.editor.getSession().setUseSoftTabs(this.options.enablesofttabs); - this.editor.getSession().setTabSize(this.options.tabsize); - return this; - }, - - // Define mdoe option - setMode: function(lang) { - this.options.mode = lang; - this.editor.getSession().setMode("ace/mode/"+lang); - }, - - // Get position (row, column) from index in file - posFromIndex: function(index) { - var row, lines; - lines = this.editor.session.doc.getAllLines(); - for (row = 0; row < lines.length; row++) { - var line = lines[row]; - if (index <= (line.length)) break; - index = index - (line.length + 1); - } - - return { - 'row': row, - 'column': index - }; - }, - - // (action) Save file - saveFile: function(e) { - if (e) e.preventDefault(); - - if (editorSettings.user.get("stripspaces")) { - this.stripspaces(); - } - - this.sync.save(); - }, - - stripspaces: function(e) { - if (e) e.preventDefault(); - - // strip all whitespaces from the current document - var doc = this.editor.session.doc; - var lines = doc.getAllLines(); - - for (var i = 0; i < lines.length; ++i) { - var index = lines[i].search(/\s+$/); - if (index !== -1 && index != 0) - doc.removeInLine(i, index, lines[i].length); - } - }, - - // (action) Run this file - runFile: function(e) { - if (e) e.preventDefault(); - this.model.run(); - }, - - // (action) Open search box - searchInFile: function(e) { - if (e) e.preventDefault(); - this.editor.execCommand("find"); - }, - - // Show active line in debug - updateDebugLine: function() { - var position = debugManager.getPosition(); - - // Clear previous marker - if (this.debugMarker != null) { - this.editor.session.removeMarker(this.debugMarker); - this.debugMarker = null; - } - if (position && position.filename == this.model.path()) { - this.debugMarker = this.editor.session.addMarker(new aceRange.Range(parseInt(position.line) - 1, 0, parseInt(position.line) - 1, 2000), "marker-debug", "line", false); - console.log(this.debugMarker); - } - }, - - // Update current line according to options - adaptOptions: function() { - if (this.fileOptions.line) { - this.editor.gotoLine(this.fileOptions.line); - } else if (this.fileOptions.pattern) { - this.editor.find(this.fileOptions.pattern,{ - regExp: false, - backwards: false, - wrap: true - }); - } - } - }); - - return FileEditorView; -}); diff --git a/addons/cb.files.editor/package.json b/addons/cb.files.editor/package.json deleted file mode 100644 index afa3e10d..00000000 --- a/addons/cb.files.editor/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "cb.files.editor", - "version": "0.1.1", - "title": "Code Editor", - "description": "Code Editor with ACE.", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client", - "provides": [ - "ace" - ], - "resources": [ - "ace/**" - ] - }, - "scripts": { - "postinstall": "./download_ace.sh && ./build.sh" - }, - "engines": { - "codebox": ">=0.7.0" - } -} diff --git a/addons/cb.files.editor/stylesheets/file.less b/addons/cb.files.editor/stylesheets/file.less deleted file mode 100644 index 528ada77..00000000 --- a/addons/cb.files.editor/stylesheets/file.less +++ /dev/null @@ -1,114 +0,0 @@ -.mixin-cursor-color(@color) { - @pseudo-selector: ~".marker-@{color}"; - @{pseudo-selector} { - &.marker-selection { - background: ~"#@{color}"; - } - } -} - -.addon-files-aceeditor { - .file-participants { - a { - position: relative; - overflow: hidden; - padding: 6px 10px; - border-left: 6px solid #eee; - } - } - - .editor-inner { - .editor-ace { - -webkit-font-smoothing: subpixel-antialiased; - font-smoothing: subpixel-antialiased; - - position: absolute !important; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - border-radius: 0px; - padding: 0px; - - .marker-debug { - position: absolute; - background: #5e9ef3; - opacity: .5; - width: 100% !important; - } - - .marker-cursor { - position: absolute; - border-left: 2px solid red; - opacity: 1; - } - - .marker-selection { - position: absolute; - background: red; - opacity: 0.2; - } - .mixin-cursor-color("1abc9c"); - .mixin-cursor-color("9b59b6"); - .mixin-cursor-color("e67e22"); - .mixin-cursor-color("16a085"); - .mixin-cursor-color("c0392b"); - .mixin-cursor-color("2980b9"); - .mixin-cursor-color("f39c12"); - .mixin-cursor-color("8e44ad"); - - .marker-cursor { - position: absolute; - z-index: 5; - border-left: 2px solid #0ff; - margin-left: -1px; - right: 5px; - text-align: right; - color: #ff0; - border-bottom: 1px dotted rgba(0,255,255,0.30); - } - .marker-cursor-nametag { - position: absolute; - bottom: -1px; - right: 0px; - text-align: right; - color: #fff; - background: #0ff; - padding: 0px; - padding-left: 3px; - padding-right: 3px; - opacity: 1; - font-family: Arial, sans-serif; - font-weight: 600; - pointer-events: auto; - cursor: help; - } - .marker-cursor-nametag-flag { - position: absolute; - bottom: 0px; - left: -6px; - width: 0px; - height: 0px; - border: 3px solid rgba(0,255,255,0.00); - border-right: 3px solid #0ff; - border-bottom: 3px solid #0ff; - } - - .ace_gutter-cell.ace_breakpoint { - position: relative; - - &:before { - content: " "; - width: 10px; - height: 10px; - border-radius: 16px; - border: 2px solid #fff; - position: absolute; - left: 4px; - background: #5E9EF3; - top: 3px; - } - } - } - } -} diff --git a/addons/cb.files.editor/templates/file.html b/addons/cb.files.editor/templates/file.html deleted file mode 100644 index cbb2e425..00000000 --- a/addons/cb.files.editor/templates/file.html +++ /dev/null @@ -1,3 +0,0 @@ -
        -
        -
        \ No newline at end of file diff --git a/addons/cb.files.editor/theme/textmate.js b/addons/cb.files.editor/theme/textmate.js deleted file mode 100644 index 1f0fa775..00000000 --- a/addons/cb.files.editor/theme/textmate.js +++ /dev/null @@ -1,10 +0,0 @@ -define([ - "less!theme/textmate.less" -], function(cssContent) { - - return { - 'isDark': false, - 'cssClass': "ace-tm", - 'cssText': cssContent - } -}); \ No newline at end of file diff --git a/addons/cb.files.editor/theme/textmate.less b/addons/cb.files.editor/theme/textmate.less deleted file mode 100644 index 269f85cf..00000000 --- a/addons/cb.files.editor/theme/textmate.less +++ /dev/null @@ -1,182 +0,0 @@ - - -.ace_editor { - /* Search dialog */ - .ace_search, .ace_search.right { - right: 0px; - left: 0px; - max-width: none; - bottom: 0px; - top: auto; - border: 1px solid; - border-radius: 0px; - padding: 0px; - box-shadow: 2px 3px 5px rgba(0,0,0,.2); - - font-smoothing: subpixel-antialiased; - -webkit-font-smoothing: subpixel-antialiased; - - .ace_searchbtn_close { - margin: 9px 5px 0px 5px; - } - - .ace_search_field { - } - - .ace_search_form, .ace_replace_form { - border-radius: 0px; - margin: 4px; - } - - .ace_search_options { - text-align: left; - display: inline-block; - float: right; - margin: 0px; - padding: 4px; - height: 24px; - } - - .ace_button { - border-radius: 0px; - padding: 2px 4px; - margin: 0 3px 0 0; - display: inline-block; - } - - .ace_searchbtn, .ace_replacebtn{ - padding: 0px 5px; - border-radius: 0px; - } - } - - &.ace-tm { - background-color: #FFFFFF; - color: #4D4D4C; - - .ace_gutter { - background: transparent; - color: #4D4D4C - } - - .ace_print-margin { - width: 1px; - background: #f6f6f6 - } - - .ace_cursor { - color: #AEAFAD - } - - .ace_marker-layer .ace_selection { - background: #eaeaea; - } - - &.ace_multiselect .ace_selection.ace_start { - box-shadow: 0 0 3px 0px #FFFFFF; - border-radius: 2px - } - - .ace_marker-layer .ace_step { - background: rgb(255, 255, 0) - } - - .ace_marker-layer .ace_bracket { - margin: -1px 0 0 -1px; - border: 1px solid #D1D1D1 - } - - .ace_marker-layer .ace_active-line { - background: #EFEFEF - } - - .ace_gutter-active-line { - background-color : transparent; - } - - .ace_marker-layer .ace_selected-word { - border: 1px solid #D6D6D6 - } - - .ace_invisible { - color: #D1D1D1 - } - - .ace_keyword, - .ace_meta, - .ace_storage, - .ace_storage.ace_type, - .ace_support.ace_type { - color: #8959A8 - } - - .ace_keyword.ace_operator { - color: #3E999F - } - - .ace_constant.ace_character, - .ace_constant.ace_language, - .ace_constant.ace_numeric, - .ace_keyword.ace_other.ace_unit, - .ace_support.ace_constant, - .ace_variable.ace_parameter { - color: #F5871F - } - - .ace_constant.ace_other { - color: #666969 - } - - .ace_invalid { - color: #FFFFFF; - background-color: #C82829 - } - - .ace_invalid.ace_deprecated { - color: #FFFFFF; - background-color: #8959A8 - } - - .ace_fold { - background-color: #4271AE; - border-color: #4D4D4C - } - - .ace_entity.ace_name.ace_function, - .ace_support.ace_function, - .ace_variable { - color: #4271AE - } - - .ace_support.ace_class, - .ace_support.ace_type { - color: #C99E00 - } - - .ace_heading, - .ace_string { - color: #718C00 - } - - .ace_entity.ace_name.ace_tag, - .ace_entity.ace_other.ace_attribute-name, - .ace_meta.ace_tag, - .ace_string.ace_regexp, - .ace_variable { - color: #C82829 - } - - .ace_comment { - color: #8E908C - } - - .ace_indent-guide { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bdu3f/BwAlfgctduB85QAAAABJRU5ErkJggg==) right repeat-y - } - - .ace_search { - border-color: #cbcbcb; - background: #e5e5e5; - } - } -} \ No newline at end of file diff --git a/addons/cb.files.image/client.js b/addons/cb.files.image/client.js deleted file mode 100644 index 899274be..00000000 --- a/addons/cb.files.image/client.js +++ /dev/null @@ -1,19 +0,0 @@ -define([ - "views/image" -], function(FileImageView) { - var _ = codebox.require("hr/utils"); - var files = codebox.require("core/files"); - - var imageExts = [ - ".png", ".jpg", ".gif", ".tiff", ".jpeg", ".bmp", ".webp", ".svg" - ]; - - files.addHandler("imageviewer", { - name: "Image Viewer", - position: 1, - View: FileImageView, - valid: function(file) { - return (!file.isDirectory() && _.contains(imageExts, file.extension())); - } - }); -}); \ No newline at end of file diff --git a/addons/cb.files.image/package.json b/addons/cb.files.image/package.json deleted file mode 100644 index 5364b006..00000000 --- a/addons/cb.files.image/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.files.image", - "version": "0.0.1", - "title": "Image Viewer", - "description": "Images files viewer (png, jpeg, gif).", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} diff --git a/addons/cb.files.image/stylesheets/image.less b/addons/cb.files.image/stylesheets/image.less deleted file mode 100644 index 8e5abe6b..00000000 --- a/addons/cb.files.image/stylesheets/image.less +++ /dev/null @@ -1,5 +0,0 @@ -.addon-files-aceeditor { - .image { - - } -} \ No newline at end of file diff --git a/addons/cb.files.image/templates/image.html b/addons/cb.files.image/templates/image.html deleted file mode 100644 index 40e7dc5f..00000000 --- a/addons/cb.files.image/templates/image.html +++ /dev/null @@ -1 +0,0 @@ -
        \ No newline at end of file diff --git a/addons/cb.files.image/views/image.js b/addons/cb.files.image/views/image.js deleted file mode 100644 index 9b0182ce..00000000 --- a/addons/cb.files.image/views/image.js +++ /dev/null @@ -1,19 +0,0 @@ -define([ - "text!templates/image.html", - "less!stylesheets/image.less" -], function(templateFile) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var Dialogs = codebox.require("utils/dialogs"); - var FilesBaseView = codebox.require("views/files/base"); - - var FileImageView = FilesBaseView.extend({ - className: "addon-files-imageviewer", - templateLoader: "text", - template: templateFile, - events: {} - }); - - return FileImageView; -}); \ No newline at end of file diff --git a/addons/cb.files.preview/client.js b/addons/cb.files.preview/client.js deleted file mode 100644 index d6c621ba..00000000 --- a/addons/cb.files.preview/client.js +++ /dev/null @@ -1,35 +0,0 @@ -define([ - "views/preview_html", - "views/preview_markdown" -], function(PreviewHtml, PreviewMarkdown) { - var _ = codebox.require("hr/utils"); - var files = codebox.require("core/files"); - - var htmlExts = [ - ".html", ".htm" - ]; - - files.addHandler("preview", { - name: "HTML Preview", - icon: "eye", - position: 10, - View: PreviewHtml, - valid: function(file) { - return (!file.isDirectory() && _.contains(htmlExts, file.extension())); - } - }); - - var markdownExts = [ - ".md", ".markdown", ".txt" - ]; - - files.addHandler("preview-markdown", { - name: "Markdown Preview", - icon: "eye", - position: 10, - View: PreviewMarkdown, - valid: function(file) { - return (!file.isDirectory() && _.contains(markdownExts, file.extension())); - } - }); -}); \ No newline at end of file diff --git a/addons/cb.files.preview/package.json b/addons/cb.files.preview/package.json deleted file mode 100644 index a9f506a6..00000000 --- a/addons/cb.files.preview/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "cb.files.preview", - "version": "0.0.1", - "title": "HTML / Markdown Preview", - "description": "Adds option to preview files in a new tab.", - "homepage": "https://github.com/invokr/codebox", - "license": "Apache", - "author": { - "name": "Robin Dietrich", - "email": "me@invokr.org", - "url": "https://github.com/invokr" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - }, - "dependencies": { - "markdown": "0.5.0" - } -} diff --git a/addons/cb.files.preview/settings.js b/addons/cb.files.preview/settings.js deleted file mode 100644 index d1b4a1fa..00000000 --- a/addons/cb.files.preview/settings.js +++ /dev/null @@ -1,17 +0,0 @@ -define([], function() { - var settings = codebox.require("core/settings"); - - return settings.add({ - 'namespace': "preview", - 'title': "Preview", - 'defaults': { - 'refresh': true - }, - 'fields': { - 'refresh': { - 'label': "Reload on Save", - 'type': "checkbox" - } - } - }); -}); \ No newline at end of file diff --git a/addons/cb.files.preview/stylesheets/preview.less b/addons/cb.files.preview/stylesheets/preview.less deleted file mode 100644 index bc878aa7..00000000 --- a/addons/cb.files.preview/stylesheets/preview.less +++ /dev/null @@ -1,19 +0,0 @@ -.addon-files-previewviewer { - height: 100%; - - iframe { - border: 0px; - width: 100%; - height: 100%; - background-color: #fff; - } - - .markdown { - border: 0px; - width: 100%; - height: 100%; - background-color: #fff; - margin-top: 1px; - padding: 3px; - } -} \ No newline at end of file diff --git a/addons/cb.files.preview/templates/preview_html.html b/addons/cb.files.preview/templates/preview_html.html deleted file mode 100644 index bcc3b8c9..00000000 --- a/addons/cb.files.preview/templates/preview_html.html +++ /dev/null @@ -1,3 +0,0 @@ -
        - -
        \ No newline at end of file diff --git a/addons/cb.files.preview/templates/preview_markdown.html b/addons/cb.files.preview/templates/preview_markdown.html deleted file mode 100644 index 6595a5bb..00000000 --- a/addons/cb.files.preview/templates/preview_markdown.html +++ /dev/null @@ -1 +0,0 @@ -
        \ No newline at end of file diff --git a/addons/cb.files.preview/views/preview_html.js b/addons/cb.files.preview/views/preview_html.js deleted file mode 100644 index 9fc20ef9..00000000 --- a/addons/cb.files.preview/views/preview_html.js +++ /dev/null @@ -1,52 +0,0 @@ -define([ - "settings", - "text!templates/preview_html.html", - "less!stylesheets/preview.less" -], function(settings, templateFile) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var box = codebox.require("core/box"); - var FilesTabView = codebox.require("views/files/tab"); - - var PreviewView = FilesTabView.extend({ - className: "addon-files-previewviewer", - templateLoader: "text", - template: templateFile, - events: {}, - - initialize: function() { - PreviewView.__super__.initialize.apply(this, arguments); - var that = this; - - // add refresh menu option - this.tab.menu.menuSection([ - { - 'type': "action", - 'title': "Refresh", - 'shortcuts': [ - "mod+r" - ], - 'bindKeyboard': true, - 'action': function() { - that.refresh(); - } - } - ]); - - // bind save event - box.on("box:watch:change:update", function() { - if (settings.user.get("refresh")) { - that.refresh(); - } - }, this); - - return this; - }, - - refresh: function() { - $(this.$el).find("iframe").attr('src', function ( i, val ) { return val; }); - } - }); - - return PreviewView; -}); diff --git a/addons/cb.files.preview/views/preview_markdown.js b/addons/cb.files.preview/views/preview_markdown.js deleted file mode 100644 index 7fe530f5..00000000 --- a/addons/cb.files.preview/views/preview_markdown.js +++ /dev/null @@ -1,58 +0,0 @@ -define([ - "settings", - "node_modules/markdown/lib/markdown", - "text!templates/preview_markdown.html", - "less!stylesheets/preview.less" -], function(settings, _markdown, templateFile) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var box = codebox.require("core/box"); - var FilesTabView = codebox.require("views/files/tab"); - - var markdown = window.markdown; - - var MarkdownView = FilesTabView.extend({ - className: "addon-files-previewviewer", - templateLoader: "text", - template: templateFile, - events: {}, - - initialize: function() { - MarkdownView.__super__.initialize.apply(this, arguments); - var that = this; - - // add refresh menu option - this.tab.menu.menuSection([ - { - 'type': "action", - 'title': "Refresh", - 'shortcuts': [ - "mod+r" - ], - 'bindKeyboard': true, - 'action': function() { - that.refresh(); - } - } - ]); - - this.model.on("file:change:update", function() { - if (settings.user.get("refresh")) { - that.refresh(); - } - }, this); - - this.refresh(); - return this; - }, - - refresh: function() { - var that = this; - this.model.download().then(function (content) { - that.$el.html("
        "+markdown.toHTML(content)+"
        "); - }); - } - }); - - return MarkdownView; -}); diff --git a/addons/cb.git/client.js b/addons/cb.git/client.js deleted file mode 100644 index 15277921..00000000 --- a/addons/cb.git/client.js +++ /dev/null @@ -1,251 +0,0 @@ -define(["views/dialog"], function(GitDialog) { - var Q = codebox.require("hr/promise"); - var commands = codebox.require("core/commands/toolbar"); - var app = codebox.require("core/app"); - var box = codebox.require("core/box"); - var dialogs = codebox.require("utils/dialogs"); - var menu = codebox.require("core/commands/menu"); - var box = codebox.require("core/box"); - var Command = codebox.require("models/command"); - var rpc = codebox.require("core/backends/rpc"); - var operations = codebox.require("core/operations"); - - // Check git status - var updateStatus = function() { - return rpc.execute("git/status") - .then(function() { - updateMenu(true); - return updateBranchesMenu(false); - }, function(err) { - updateMenu(false); - }) - }; - - // Branches menu - var branchesMenu = Command.register({ - 'title': "Switch To Branch", - 'type': "menu", - 'offline': false - }); - var updateBranchesMenu = function(doUpdateStatus) { - return rpc.execute("git/branches") - .then(function(branches) { - branchesMenu.menu.reset(_.map(branches, function(branch) { - return { - 'title': branch.name, - 'flags': branch.active ? "active" : "", - 'action': function() { - var ref = branch.name; - return operations.start("git.checkout", function(op) { - return rpc.execute("git/checkout", { - 'ref': ref - }) - }, { - title: "Checkout '"+ref+"'" - }) - .then(updateBranchesMenu); - } - } - })); - }, function(err) { - if (doUpdateStatus !== false) updateStatus(); - return Q.reject(err); - }); - }; - - // Handle http auth - var handleHttpAuth = function(method) { - return Q(method()) - .fail(function(err) { - if (err.code == 401) { - // Fields for https auth - var fields = { - username: { - type: "text", - label: "Username" - }, - password: { - type: "password", - label: "Password" - } - }; - - // Passphrase for ssh - if (err.message.toLowerCase().indexOf("authentication") < 0) { - fields = { - passphrase: { - type: "text", - label: "Passphrase" - } - }; - } - - return dialogs.fields("Need authentication:", fields) - .then(method); - } else { - return Q.reject(err); - } - }) - }; - - // Add menu - var gitMenu = menu.register("git", { - title: "Repository", - offline: false - }); - - var updateMenu = function(state) { - // Clear menu - gitMenu.clearMenu(); - - // Invalid repository - if (!state) { - gitMenu.menuSection([ - { - 'title': "No GIT Repository detected", - 'type': "label", - 'icons': { - 'menu': "warning", - } - }, - { - 'title': "Initialize Local Repository", - 'offline': false, - 'action': function() { - return operations.start("git.init", function(op) { - return rpc.execute("git/init") - }, { - title: "Initializing GIT repository" - }); - } - }, - { - 'title': "Clone Remote Repository", - 'offline': false, - 'action': function() { - return operations.start("git.clone", function(op) { - return dialogs.prompt("Clone Remote Repository", "Remote repository URI:") - .then(function(url) { - if (!url) return; - return handleHttpAuth(function(creds) { - return rpc.execute("git/clone", { - 'url': url, - 'auth': creds || {} - }); - }); - }); - }, { - title: "Cloning GIT repository" - }); - } - } - ]); - } else { - gitMenu.menuSection([ - { - 'title': "Commit", - 'shortcuts': ["mod+shift+C"], - 'offline': false, - 'action': function() { - dialogs.open(GitDialog); - } - } - ]).menuSection([ - { - 'title': "Synchronize", - 'shortcuts': ["mod+S"], - 'offline': false, - 'action': function() { - return operations.start("git.sync", function(op) { - return handleHttpAuth(function(creds) { - return rpc.execute("git/sync", { - 'auth': creds || {} - }); - }); - }, { - title: "Pushing & Pulling" - }); - } - }, - { - 'title': "Push", - 'shortcuts': ["mod+P"], - 'offline': false, - 'action': function() { - return operations.start("git.push", function(op) { - return handleHttpAuth(function(creds) { - return rpc.execute("git/push", { - 'auth': creds || {} - }); - }); - }, { - title: "Pushing" - }); - } - }, - { - 'title': "Pull", - 'shortcuts': ["shift+mod+P"], - 'offline': false, - 'action': function() { - return operations.start("git.pull", function(op) { - return handleHttpAuth(function(creds) { - return rpc.execute("git/pull", { - 'auth': creds || {} - }); - }); - }, { - title: "Pulling" - }); - } - } - ]).menuSection([ - branchesMenu, - { - 'title': "Refresh branches", - 'offline': false, - 'action': updateBranchesMenu, - } - ]).menuSection([ - { - 'title': "Create a branch", - 'offline': false, - 'action': function() { - dialogs.prompt("Create a branch", "Enter the name for the new branch:").then(function(name) { - if (!name) return; - operations.start("git.branch.create", function(op) { - return rpc.execute("git/branch/create", { - 'name': name - }) - }, { - title: "Creating branch '"+name+"'" - }); - }); - } - }, - { - 'title': "Delete a branch", - 'offline': false, - 'action': function() { - dialogs.prompt("Delete a branch", "Enter the name of the branch you want to delete:").then(function(name) { - if (!name) return; - operations.start("git.branch.delete", function(op) { - return rpc.execute("git/branch/delete", { - 'name': name - }) - }, { - title: "Deleting branch '"+name+"'" - }).then(updateBranchesMenu); - }); - } - } - ]); - } - }; - - box.on("box:git", function() { - updateStatus(); - }); - updateStatus(); -}); - diff --git a/addons/cb.git/node/main.js b/addons/cb.git/node/main.js deleted file mode 100644 index f23406be..00000000 --- a/addons/cb.git/node/main.js +++ /dev/null @@ -1,29 +0,0 @@ -// Requires -var _ = require('lodash'); -var Gittle = require('gittle'); -var GitRPCService = require('./service').GitRPCService; - - -function setup(options, imports, register) { - // Import - var rpc = imports.rpc; - var events = imports.events; - var workspace = imports.workspace; - - // Service - var service = new GitRPCService(workspace, events); - - // Register RPC - rpc.register('git', service); - - // Register - register(null, { - "git": { - repo: service.repo, - }, - "git_rpc": service - }); -} - -// Exports -module.exports = setup; diff --git a/addons/cb.git/node/service.js b/addons/cb.git/node/service.js deleted file mode 100644 index 0404ae7d..00000000 --- a/addons/cb.git/node/service.js +++ /dev/null @@ -1,150 +0,0 @@ -// Requires -var Q = require('q'); -var _ = require('lodash'); -var Gittle = require('gittle'); - -function GitRPCService(workspace, events) { - this.workspace = workspace; - this.events = events; - - this.repo = new Gittle(workspace.root); - - _.bindAll(this); -} - -GitRPCService.prototype.init = function(args, meta) { - var that = this; - return Gittle.init(this.workspace.root).then(function(repo) { - that.events.emit('git.init', { - userId: meta.user.userId - }); - - that.repo = repo; - return that.repo.status(); - }); -}; - -GitRPCService.prototype.clone = function(args, meta) { - var that = this; - if (!args.url) throw "Need an url for cloning a repository"; - return Gittle.clone(args.url, this.workspace.root, args.auth || {}).then(function(repo) { - that.events.emit('git.clone', { - userId: meta.user.userId, - url: args.url - }); - - that.repo = repo; - return that.repo.status(); - }); -}; - -GitRPCService.prototype.status = function() { - return this.repo.status(); -}; - -GitRPCService.prototype.sync = function(args, meta) { - var that = this; - return this.repo.sync(null, null, args.auth || {}) - .then(function() { - that.events.emit('git.sync', { - userId: meta.user.userId - }); - }); -}; - -GitRPCService.prototype.push = function(args, meta) { - var that = this; - return this.repo.push(null, null, args.auth || {}) - .then(function() { - that.events.emit('git.push', { - userId: meta.user.userId - }); - }); -}; - -GitRPCService.prototype.pull = function(args, meta) { - var that = this; - return this.repo.pull(null, null, args.auth || {}) - .then(function() { - that.events.emit('git.pull', { - userId: meta.user.userId - }); - }); -}; - -GitRPCService.prototype.commit = function(args, meta) { - var msg = args.message; - var files = args.files || []; - var name = meta.user.name; - var email = meta.user.email; - - if(!_.all([msg, files, name, email])) { - return Q.reject(new Error("Could not commit because arguments are missing and/or invalid")); - } - - var that = this; - return this.repo.commitWith(name, email, msg, files) - .then(function() { - that.events.emit('git.commit', { - userId: meta.user.userId, - - message: msg, - name: name, - email: email, - files: files - }); - }); -}; - -GitRPCService.prototype.commits = function(args) { - return this.repo.commits(args.ref, args.limit, args.skip); -}; - -GitRPCService.prototype.branches = function(args) { - var activeBranch, that = this; - - // Get current active branch - return this.repo.branch().then(function(branch) { - activeBranch = branch; - - // Get all local branches - return that.repo.branches(); - }).then(function(branches) { - return _.map(branches, function(branch) { - return { - 'name': branch.name, - 'active': branch.name == activeBranch.name - } - }); - }) -}; - -GitRPCService.prototype.branch_create = function(args) { - if (!args.name) return Q.reject(new Error("Need a name to create a branch")); - return this.repo.create_branch(args.name); -}; - -GitRPCService.prototype.checkout = function(args) { - if (!args.ref) return Q.reject(new Error("Need a referance (ref) to checkout")); - return this.repo.checkout(args.ref); -}; - -GitRPCService.prototype.branch_delete = function(args) { - if (!args.name) return Q.reject(new Error("Need a name to delete a branch")); - return this.repo.delete_branch(args.name); -}; - -GitRPCService.prototype.commits_pending = function() { - return this.repo.commits_pending(); -}; - -GitRPCService.prototype.diff = function(args) { - return this.repo.diff(args.new, args.old).then(function(diffs) { - return _.map(diffs, function(diff) { - return diff.normalize(); - }) - }); -}; - -// Exports -exports.GitRPCService = GitRPCService; diff --git a/addons/cb.git/package.json b/addons/cb.git/package.json deleted file mode 100644 index 0650cd0d..00000000 --- a/addons/cb.git/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "cb.git", - "version": "0.0.3", - "title": "GIT", - "description": "Integration of Git into your workspace.", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "main": "./node/main", - "plugin": { - "provides": [ - "git", - "git_rpc" - ], - "consumes": [ - "rpc", - "events", - "workspace" - ] - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.git/stylesheets/git.less b/addons/cb.git/stylesheets/git.less deleted file mode 100644 index 6ee1e81e..00000000 --- a/addons/cb.git/stylesheets/git.less +++ /dev/null @@ -1,64 +0,0 @@ -.addon-git-dialog { - .modal-body { - padding: 0px; - } - .modal-footer { - margin: 0px; - } - - .navbar { - margin: 0px; - } - - .git-changes { - .git-commit { - padding: 15px 10px; - margin: 0px; - background: #f8f8f8; - - textarea { - resize: none; - } - } - - .git-changes-files { - list-style: none; - margin: 0px; - padding: 0px; - - .file { - background: #fdfdfd; - box-shadow: 0px 1px 0px #fff inset; - border-top: 1px solid #ddd; - padding: 6px; - padding-left: 55px; - position: relative; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - .file-type { - position: absolute; - display: block; - top: 0px; - left: 0px; - width: 45px; - bottom: 0px; - padding: 6px; - text-align: center; - color: #fff; - - &.type-M { - background: #3498db; - } - &.type-A { - background: #2ecc71; - } - &.type-D { - background: #e74c3c; - } - } - } - } - } -} \ No newline at end of file diff --git a/addons/cb.git/templates/dialog.html b/addons/cb.git/templates/dialog.html deleted file mode 100644 index 299a6aef..00000000 --- a/addons/cb.git/templates/dialog.html +++ /dev/null @@ -1,28 +0,0 @@ - \ No newline at end of file diff --git a/addons/cb.git/views/dialog.js b/addons/cb.git/views/dialog.js deleted file mode 100644 index 139a7fa4..00000000 --- a/addons/cb.git/views/dialog.js +++ /dev/null @@ -1,75 +0,0 @@ -define([ - "text!templates/dialog.html", - "less!stylesheets/git.less" -], function(templateFile) { - var DialogView = codebox.require("views/dialogs/base"); - var box = codebox.require("core/box"); - var rpc = codebox.require("core/backends/rpc"); - var operations = codebox.require("core/operations"); - - var GitDialog = DialogView.extend({ - className: "addon-git-dialog modal fade", - templateLoader: "text", - template: templateFile, - events: _.extend({}, DialogView.prototype.events,{ - "submit form": "submit" - }), - - // Constructor - initialize: function(options) { - var that = this; - GitDialog.__super__.initialize.apply(this, arguments); - - that.git = null; - - rpc.execute("git/status").then(function(status) { - that.git = status; - that.render(); - }); - return this; - }, - - // Template Context - templateContext: function() { - return { - git: this.git - }; - }, - - // Render - render: function() { - if (!this.git) return this; - return GitDialog.__super__.render.apply(this, arguments); - }, - - // Finish rendering - finish: function() { - return GitDialog.__super__.finish.apply(this, arguments); - }, - - // Commit (and sync) - submit: function(e) { - if (e) e.preventDefault(); - - var that = this; - var sync = this.$(".git-commit .btn-git-sync").hasClass("active"); - var message = this.$(".git-commit textarea").val(); - - if (message.length == 0) { - return; - } - - operations.start("git.commit", function(op) { - return rpc.execute("git/commit", { - 'message': message - }); - }, { - title: "Commiting" - }).then(function() { - that.close(); - }) - } - }); - - return GitDialog; -}); \ No newline at end of file diff --git a/addons/cb.help/client.js b/addons/cb.help/client.js deleted file mode 100644 index 62cb0ae1..00000000 --- a/addons/cb.help/client.js +++ /dev/null @@ -1,80 +0,0 @@ -define([ - "text!welcome.md" -], function(welcomeText) { - var hr = codebox.require("hr/hr"); - var app = codebox.require("core/app"); - var menu = codebox.require("core/commands/menu"); - var files = codebox.require("core/files"); - var rpc = codebox.require("core/backends/rpc"); - var Command = codebox.require("models/command"); - - // Help url - var helpUrl = "http://help.codebox.io/"; - - // Command to open changelog - var commandChanges = Command.register("help.changes", { - 'category': "Help", - 'title': "Open Release Notes", - 'action': function(title) { - return rpc.execute("box/changes").then(function(changes) { - return files.openNew(title || "Release Notes", changes.content); - }); - } - }); - - // Command to open welcome - var commandWelcome = Command.register("help.welcome", { - 'category': "Help", - 'title': "Welcome", - 'action': function(title) { - return files.openNew(title || "Welcome.md", welcomeText); - } - }); - - - // Add menu - menu.register("help", { - title: "Help", - position: 100 - }).menuSection([ - commandChanges, - { - 'id': "help.documentation", - 'category': "Help", - 'title': "Documentation", - 'description': "Open Documentation", - 'shortcuts': ['?'], - 'offline': false, - 'action': function() { - window.open(helpUrl); - } - } - ]).menuSection([ - { - 'id': "help.feedback", - 'category': "Help", - 'title': "Submit Feedback", - 'offline': false, - 'action': function() { - window.open("https://github.com/FriendCode/codebox/issues"); - } - } - ]); - - // Open changes if version changes - app.once("ready", function() { - // Show release not if version increased - // If first time show welcome message - - var currentVersion = hr.configs.args.version; - var lastVersion = hr.Storage.get("codeboxVersion"); - - if (lastVersion == null) { - commandWelcome.run(); - } else if (currentVersion != lastVersion) { - commandChanges.run(); - } - hr.Storage.set("codeboxVersion", currentVersion); - }); -}); - diff --git a/addons/cb.help/package.json b/addons/cb.help/package.json deleted file mode 100644 index fe5d8e8d..00000000 --- a/addons/cb.help/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.help", - "version": "0.0.1", - "title": "Help", - "description": "Documentation for Codebox", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.help/welcome.md b/addons/cb.help/welcome.md deleted file mode 100644 index c4e4c9d0..00000000 --- a/addons/cb.help/welcome.md +++ /dev/null @@ -1,22 +0,0 @@ -# Welcome to Codebox! - -Here is what you need to know. - -1. This IDE is open source, you can contribute to it on [GitHub](https://github.com/FriendCode/codebox). - -2. If you only remember one thing make it `cmd-shift-P` (or `ctrl-shift-P` on Windows and Linux). This keystroke toggles - the command palette, which lists every command. - -3. We want your feedback! If you have anything positive or negative to say click - on the `Send Feedback` button at the bottom right of this window. - -4. Here is where you can get more help with Codebox - - * The [Codebox docs](http://help.codebox.io/) contain Guides on how to use the IDE. - * Post issues on [GitHub](https://github.com/FriendCode/codebox/issues). - -5. If you ever want to see this buffer again use the command palette - (`cmd-shift-P`) and search for `Welcome`. - -6. If you want to read ChangeLog, use the command palette - (`cmd-shift-P`) and search for `Release Notes`. \ No newline at end of file diff --git a/addons/cb.offline/client.js b/addons/cb.offline/client.js deleted file mode 100644 index 574295f7..00000000 --- a/addons/cb.offline/client.js +++ /dev/null @@ -1,80 +0,0 @@ -define([ - "settings", - "menus" -], function(settings, menus) { - var $ = codebox.require("hr/dom"); - var Q = codebox.require("hr/promise"); - var app = codebox.require("core/app"); - var box = codebox.require("core/box"); - var menu = codebox.require("core/commands/menu"); - var commands = codebox.require("core/commands/toolbar"); - var operations = codebox.require("core/operations"); - var hr = codebox.require("hr/hr"); - var Command = codebox.require("models/command"); - var localfs = codebox.require("core/localfs"); - - var _syncInterval = null; - - - // Run offline cache update operation - var op = operations.start("offline.update", null, { - 'title': "Updating", - 'icons': { - 'default': "fa-cog fa-spin", - }, - 'state': window.applicationCache.status == window.applicationCache.IDLE ? "idle" : "running", - 'progress': 0 - }); - - // Application manifest - $(window.applicationCache).bind('downloading progress', function(e) { - var progress = 0; - if (e && e.originalEvent && e.originalEvent.lengthComputable) { - progress = Math.round(100*e.originalEvent.loaded/e.originalEvent.total); - } - op.state("running"); - op.progress(progress); - }); - $(window.applicationCache).bind('checking', function(e) { - op.state("running"); - op.progress(0); - }); - $(window.applicationCache).bind('noupdate cached obsolete error', function(e) { - op.state("idle"); - }); - - - // Update settings - var updateSettings = function() { - var enabled = settings.user.get("enabled", true); - if (_syncInterval) clearInterval(_syncInterval); - - // Enable sync - localfs.enableSync(settings.user.get("enabled", true)); - - // Set ignored files - localfs.setIgnoredFiles(settings.user.get("syncIgnore", "").split("\n")); - - // Toggle menu - menus.sync.toggleFlag("hidden", !enabled); - - // Run sync every 10min - _syncInterval = setInterval(function() { - localfs.autoSync(); - }, settings.user.get("syncInterval", 10)*60*1000); - }; - - setTimeout(function() { - localfs.sync(); - }, 5*1000); - - // Run sync everytime there is a modification - box.on("box:watch", function() { - localfs.autoSync(); - }); - - // Change settings - settings.user.change(updateSettings); - updateSettings(); -}); - diff --git a/addons/cb.offline/menus.js b/addons/cb.offline/menus.js deleted file mode 100644 index eafdf3a4..00000000 --- a/addons/cb.offline/menus.js +++ /dev/null @@ -1,87 +0,0 @@ -define([], function() { - var box = codebox.require("core/box"); - var menu = codebox.require("core/commands/menu"); - var commands = codebox.require("core/commands/toolbar"); - var operations = codebox.require("core/operations"); - var hr = codebox.require("hr/hr"); - var Command = codebox.require("models/command"); - var localfs = codebox.require("core/localfs"); - var dialogs = codebox.require("utils/dialogs"); - - // Command to check connection - var checkConnection = commands.register("offline.check", { - 'category': "Offline", - 'title': "Check Connection", - 'offline': true, - 'icons': { - 'default': "bolt", - } - }, function() { - hr.Offline.check(); - }); - - // Changes list - var menuListChanges = new Command({}, { - 'title': "Changes", - 'type': "menu", - 'flags': "disabled" - }); - - // Menu Synchronize - var menuSync = menu.register("offline.synchronize", { - title: "Synchronize", - position: 95, - offline: false - }).menuSection([ - checkConnection - ]).menuSection([ - { - 'id': "offline.changes.calcul", - 'category': "Offline", - 'title': "Calcul Changes", - 'offline': false, - 'action': function() { - return localfs.sync(); - } - } - ]).menuSection([ - { - 'id': "offline.changes.reset", - 'category': "Offline", - 'title': "Reset All Changes", - 'offline': false, - 'action': function() { - return localfs.reset(); - } - }, - { - 'id': "offline.changes.apply", - 'category': "Offline", - 'title': "Apply All Changes", - 'offline': false, - 'action': function() { - var n = localfs.changes.size(); - if (n == 0) return; - - dialogs.confirm("Do you really want to apply "+n+" changes?").then(function(yes) { - if (!yes) return; - return localfs.changes.applyAll(); - }); - } - } - ]).menuSection([ - menuListChanges - ]); - - // Changes update - localfs.changes.on("add remove reset", function() { - menuListChanges.toggleFlag("disabled", localfs.changes.size() == 0); - menuListChanges.menu.reset(localfs.changes.map(function(change) { - return change.command(); - })); - }); - - return { - 'sync': menuSync - } -}); \ No newline at end of file diff --git a/addons/cb.offline/package.json b/addons/cb.offline/package.json deleted file mode 100644 index 5fbb6402..00000000 --- a/addons/cb.offline/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.offline", - "version": "0.0.1", - "title": "Help", - "description": "Menu for managing offline sync", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.offline/settings.js b/addons/cb.offline/settings.js deleted file mode 100644 index b77d222c..00000000 --- a/addons/cb.offline/settings.js +++ /dev/null @@ -1,30 +0,0 @@ -define([], function() { - var settings = codebox.require("core/settings"); - - // Add settings - return settings.add({ - 'namespace': "offline", - 'title': "Offline", - 'defaults': { - 'enabled': false, - 'syncInterval': 10 - }, - 'fields': { - 'enabled': { - 'label': 'Enable Files Synchronization', - 'type': "checkbox" - }, - 'syncInterval': { - 'label': "Synchronization (minutes)", - 'type': "number", - 'min': 1, - 'max': 1000, - 'step': 1 - }, - 'syncIgnore': { - 'label': "Ignored files (one by line)", - 'type': "textarea" - } - } - }); -}); \ No newline at end of file diff --git a/addons/cb.panel.files/client.js b/addons/cb.panel.files/client.js deleted file mode 100644 index 699a64b4..00000000 --- a/addons/cb.panel.files/client.js +++ /dev/null @@ -1,85 +0,0 @@ -define([ - "views/panel" -], function(PanelFilesView) { - var Command = codebox.require("models/command"); - var commands = codebox.require("core/commands/toolbar"); - var app = codebox.require("core/app"); - var panels = codebox.require("core/panels"); - var files = codebox.require("core/files"); - var menu = codebox.require("core/commands/menu"); - var box = codebox.require("core/box"); - - // Add files panels - var panel = panels.register("files", PanelFilesView, { - title: "Folders" - }); - - // Open files panel - panel.connectCommand(commands.register("files.tree.open", { - category: "Panels", - title: "Files", - description: "Open Files Panel", - icons: { - 'default': "folder-o", - }, - position: 2, - shortcuts: [ - "alt+f" - ] - })); - - // Recents files - var recentFiles = Command.register({ - 'type': "menu", - 'title': "Open Recent" - }); - files.recent.on("add remove reset", function() { - recentFiles.menu.reset(files.recent.map(function(file) { - var path = file.path(); - return { - 'title': file.get("name"), - 'action': function() { - files.open(path); - } - }; - }).reverse()); - }); - - - // Command new file - menu.getById("file").menuSection([ - { - 'id': "files.file.new", - 'category': "Files", - 'title': "New File", - 'shortcuts': ["alt+shift+n"], - 'action': function() { - files.openNew() - } - }, { - 'id': "files.folder.create", - 'category': "Files", - 'title': "New Folder", - 'shortcuts': ["alt+shift+f"], - 'action': function() { - box.root.actionMkdir(); - } - }, - recentFiles - ], { - position: 0 - }).menuSection([ - { - 'id': "workspace.save.zip", - 'category': "Files", - 'title': "Save Project As TAR.GZ", - 'offline': false, - 'action': function() { - window.open("/export/targz"); - } - } - ]); - - // Open panel - panel.open(); -}); \ No newline at end of file diff --git a/addons/cb.panel.files/package.json b/addons/cb.panel.files/package.json deleted file mode 100644 index 069fe5e0..00000000 --- a/addons/cb.panel.files/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.panel.files", - "version": "0.1.0", - "title": "Files Panel", - "description": "Files Tree Panel for exploring and managing the filesystem", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.panel.files/settings.js b/addons/cb.panel.files/settings.js deleted file mode 100644 index 95326ed3..00000000 --- a/addons/cb.panel.files/settings.js +++ /dev/null @@ -1,25 +0,0 @@ -define([], function() { - var $ = codebox.require("hr/dom"); - var settings = codebox.require("core/settings"); - - // Add settings - return settings.add({ - 'namespace': "files-panel", - 'title': "Files Explorer Panel", - 'defaults': { - 'openfiles': true, - 'hiddenfiles': true, - 'gitfolder': false - }, - 'fields': { - 'hiddenfiles': { - 'label': "Show Hidden Files", - 'type': "checkbox" - }, - 'gitfolder': { - 'label': "Show GIT Folder", - 'type': "checkbox" - } - } - }); -}); \ No newline at end of file diff --git a/addons/cb.panel.files/stylesheets/files.less b/addons/cb.panel.files/stylesheets/files.less deleted file mode 100644 index 288c2f67..00000000 --- a/addons/cb.panel.files/stylesheets/files.less +++ /dev/null @@ -1,97 +0,0 @@ -.cb-files-tree { - margin: 0px; - padding: 0px; - list-style: none; - - &.root { - - } - - .file-item { - cursor: default; - line-height: 24px; - font-size: 14px; - - &.hr-list-fiter-on { - display: none; - } - - >.files { - display: none; - } - - &.active, &.active:hover { - >.name { - background: rgba(0,0,0, 0.08); - color: inherit; - } - } - - &.disabled { - >.name { - color: #999; - } - } - - &.ui-context-menu { - >.name { - background: rgba(0, 0, 0, 0.15); - } - } - - >.name { - color: inherit; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: block; - z-index: 1; - position: relative; - - &:hover { - background: #5E9EF3; - color: #fff; - } - - >i { - width: 11px; - text-align: center; - - &.file-icon { - margin-right: 3px; - } - - &.invisibile { - color: transparent; - text-shadow: none; - } - } - >.fa-angle-right { - display: inline-block; - } - >.fa-angle-down { - display: none; - } - } - - &.type-directory { - >.name { - /*color: inherit;*/ - } - } - - &.open { - >.files { - display: block; - } - >.name { - >.fa-angle-right { - display: none; - } - >.fa-angle-down { - display: inline-block; - } - } - } - } -} \ No newline at end of file diff --git a/addons/cb.panel.files/stylesheets/panel.less b/addons/cb.panel.files/stylesheets/panel.less deleted file mode 100644 index 6091a148..00000000 --- a/addons/cb.panel.files/stylesheets/panel.less +++ /dev/null @@ -1,9 +0,0 @@ -.cb-panel-files { - position: absolute; - top: 0px; - bottom: 0px; - left: 0px; - width: 100%; - z-index: 10; - overflow-y: auto; -} \ No newline at end of file diff --git a/addons/cb.panel.files/templates/item.html b/addons/cb.panel.files/templates/item.html deleted file mode 100644 index 583ff42e..00000000 --- a/addons/cb.panel.files/templates/item.html +++ /dev/null @@ -1,14 +0,0 @@ - - <% if (file.isDirectory()) { %> - - - - <% } else { %> - - - <% } %> - <%- file.get("name") %> - -<% if (file.isDirectory()) { %> -
        -<% } %> \ No newline at end of file diff --git a/addons/cb.panel.files/views/panel.js b/addons/cb.panel.files/views/panel.js deleted file mode 100644 index c4886193..00000000 --- a/addons/cb.panel.files/views/panel.js +++ /dev/null @@ -1,58 +0,0 @@ -define([ - "settings", - "views/tree", - "less!stylesheets/panel.less" -], function(panelSettings, FilesTreeView) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var box = codebox.require("core/box"); - var files = codebox.require("core/files"); - var search = codebox.require("core/search"); - var ContextMenu = codebox.require("utils/contextmenu"); - var PanelBaseView = codebox.require("views/panels/base"); - - var PanelFilesView = PanelBaseView.extend({ - className: "cb-panel-files", - - initialize: function() { - PanelFilesView.__super__.initialize.apply(this, arguments); - - this.tree = new FilesTreeView.Item({ - model: box.root - }, this); - this.tree.update(); - this.tree.select(); - - - var $rootTree = $("
          ", { - "class": "root-tree cb-files-tree" - }); - $rootTree.appendTo(this.$el); - this.tree.$el.appendTo($rootTree); - - // Offline - hr.Offline.on("state", function() { - this.update(); - }, this); - - // Settings update - panelSettings.user.change(function() { - this.update(); - }, this); - - this.on("tab:layout", function() { - //this.render(); - }, this); - - // Context menu - ContextMenu.add(this.$el, box.root.contextMenu()); - }, - - render: function() { - return this.ready(); - } - }); - - return PanelFilesView; -}); \ No newline at end of file diff --git a/addons/cb.panel.files/views/tree.js b/addons/cb.panel.files/views/tree.js deleted file mode 100644 index 5e782d4d..00000000 --- a/addons/cb.panel.files/views/tree.js +++ /dev/null @@ -1,168 +0,0 @@ -define([ - "settings", - "text!templates/item.html", - "less!stylesheets/files.less" -], function(panelSettings, templateFile) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var box = codebox.require("core/box"); - var ContextMenu = codebox.require("utils/contextmenu"); - var FilesBaseView = codebox.require("views/files/base"); - - // File item in the tree - var FilesTreeViewItem = FilesBaseView.extend({ - tagName: "li", - className: "file-item", - templateLoader: "text", - template: templateFile, - events: { - "click .name": "select", - "dblclick .name": "open" - }, - - // Constructor - initialize: function(options) { - FilesTreeViewItem.__super__.initialize.apply(this, arguments); - var that = this; - - // View for subfiles - this.subFiles = null; - this.paddingLeft = this.options.paddingLeft || 0; - - // Context menu - ContextMenu.add(this.$el, this.model.contextMenu()); - - box.on("file.active", function(path) { - this.$el.toggleClass("active", this.model.path() == path); - }, this); - - return this; - }, - - render: function() { - if (this.subFiles) this.subFiles.detach(); - return FilesTreeViewItem.__super__.render.apply(this, arguments); - }, - - // Finish rendering - finish: function() { - this.$el.toggleClass("disabled", !this.model.canOpen()); - this.$(">.name").css("padding-left", this.paddingLeft); - this.$el.toggleClass("type-directory", this.model.isDirectory()); - - if (this.subFiles) { - this.subFiles.$el.appendTo(this.$(".files")); - } - return FilesTreeViewItem.__super__.finish.apply(this, arguments); - }, - - // (event) select the file : extend tree - select: function(e) { - if (e != null) { - e.preventDefault(); - e.stopPropagation(); - } - - if (!this.model.canOpen()) { - return; - } - - if (this.model.isDirectory()) { - if (this.subFiles == null) { - this.subFiles = new FilesTreeView({ - "codebox": this.codebox, - "model": this.model, - "paddingLeft": this.paddingLeft+15 - }, this); - this.subFiles.$el.appendTo(this.$(".files")); - this.subFiles.update(); - } - this.$el.toggleClass("open"); - } else { - this.open(); - } - }, - - // (event) open the file or directory - open: function(e) { - if (e != null) { - e.preventDefault(); - e.stopPropagation(); - } - - if (!this.model.canOpen()) { - return; - } - - if (!this.model.isDirectory()) { - this.model.open({ - 'userChoice': false - }); - } else { - this.select(); - } - } - }); - - // Complete files tree - var FilesTreeView = FilesBaseView.extend({ - tagName: "ul", - className: "cb-files-tree", - - // Constructor - initialize: function(options) { - FilesTreeView.__super__.initialize.apply(this, arguments); - var that = this; - - this.countFiles = 0; - this.paddingLeft = this.options.paddingLeft || 10; - - panelSettings.user.change(function() { - this.update(); - }, this); - - return this; - }, - - // Render the files tree - render: function() { - var that = this; - this.$el.toggleClass("root", this.model.isRoot()); - - // Context menu - ContextMenu.add(this.$el, this.model.contextMenu()); - - this.model.listdir().then(function(files) { - that.clearComponents(); - that.empty(); - that.countFiles = 0; - - _.each(files, function(file) { - if ((file.isGit() && !panelSettings.user.get("gitfolder")) - || (file.isHidden() && !panelSettings.user.get("hiddenfiles"))) { - return; - } - - var v = new FilesTreeViewItem({ - "codebox": that.codebox, - "model": file, - "paddingLeft": that.paddingLeft - }); - v.update(); - v.$el.appendTo(that.$el); - that.addComponent("file", v); - - that.countFiles = that.countFiles + 1; - }); - that.trigger("count", that.countFiles); - }); - - return that.ready(); - }, - }, { - 'Item': FilesTreeViewItem - }); - - return FilesTreeView; -}); \ No newline at end of file diff --git a/addons/cb.panel.outline/client.js b/addons/cb.panel.outline/client.js deleted file mode 100644 index ec27fe23..00000000 --- a/addons/cb.panel.outline/client.js +++ /dev/null @@ -1,30 +0,0 @@ -define([ - "settings", - "views/panel" -], function(settings, PanelOutlineView) { - var commands = codebox.require("core/commands/toolbar"); - var app = codebox.require("core/app"); - var panels = codebox.require("core/panels"); - var menu = codebox.require("core/commands/menu"); - var box = codebox.require("core/box"); - - // Add outline panel - var panel = panels.register("outline", PanelOutlineView, { - title: "Outline" - }); - - // Add command to open outline panel - panel.connectCommand(commands.register("outline.open", { - category: "Panels", - title: "Outline", - description: "Open Outline Panel", - icons: { - 'default': "code", - }, - position: 2, - shortcuts: [] - })); - - // Open panel during startup - if (settings.user.get("startup")) panel.open(); -}); \ No newline at end of file diff --git a/addons/cb.panel.outline/package.json b/addons/cb.panel.outline/package.json deleted file mode 100644 index 553911ee..00000000 --- a/addons/cb.panel.outline/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.panel.outline", - "version": "0.1.0", - "title": "Outline Panel", - "description": "Panel for exploring tags in current file", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.panel.outline/settings.js b/addons/cb.panel.outline/settings.js deleted file mode 100644 index 078ede4a..00000000 --- a/addons/cb.panel.outline/settings.js +++ /dev/null @@ -1,23 +0,0 @@ -define([], function() { - var settings = codebox.require("core/settings"); - - // Add settings - return settings.add({ - 'namespace': "outline", - 'title': "Outline", - 'defaults': { - 'separator': ".", - 'startup': false - }, - 'fields': { - 'separator': { - 'label': 'Tag Parts Separator', - 'type': "text" - }, - 'startup': { - 'label': 'Show Outline Panel at Startup', - 'type': "checkbox" - } - } - }); -}); \ No newline at end of file diff --git a/addons/cb.panel.outline/stylesheets/panel.less b/addons/cb.panel.outline/stylesheets/panel.less deleted file mode 100644 index 4ac541f2..00000000 --- a/addons/cb.panel.outline/stylesheets/panel.less +++ /dev/null @@ -1,107 +0,0 @@ -.cb-panel-outline { - position: absolute; - top: 0px; - bottom: 0px; - left: 0px; - width: 100%; - z-index: 10; - - .tags-tree { - margin: 0px; - padding: 0px; - list-style: none; - - li { - span { - display: block; - padding: 2px 8px; - cursor: default; - position: relative; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - - &:hover { - background: rgba(0, 0, 0, 0.05); - } - - &:before { - content: " "; - background: #b55761; - width: 8px; - height: 8px; - position: absolute; - border-radius: 16px; - top: 8px; - left: 19px; - border: 1px solid #000; - } - } - - &.type-v { - span:before { - background: #81a2be; - } - } - &.type-f { - span:before { - background: #9dbe8c; - } - } - - i { - width: 24px; - } - - .tags-tree { - padding-left: 15px; - display: none; - } - - .tag-icon-close { - display: inline-block; - } - .tag-icon-open { - display: none; - } - - &.open { - > .tag-icon-open { - display: inline-block; - } - > .tag-icon-close { - display: none; - } - - > .tags-tree { - display: block; - } - } - } - } - - .outline-file { - > .tags-tree { - position: absolute; - top: 0px; - bottom: 40px; - left: 5px; - right: 5px; - z-index: 10; - overflow-y: auto; - - .alert { - text-align: center; - } - } - - input, input:focus { - position: absolute; - bottom: 6px; - left: 4px; - right: 4px; - width: calc(~"100% - 8px"); - box-shadow: none; - } - } -} \ No newline at end of file diff --git a/addons/cb.panel.outline/views/panel.js b/addons/cb.panel.outline/views/panel.js deleted file mode 100644 index 8db0acee..00000000 --- a/addons/cb.panel.outline/views/panel.js +++ /dev/null @@ -1,13 +0,0 @@ -define([ - "settings", - "views/tags" -], function(panelSettings, TagsView) { - var PanelFileView = codebox.require("views/panels/file"); - - var PanelOutlineView = PanelFileView.extend({ - className: "cb-panel-outline", - FileView: TagsView - }); - - return PanelOutlineView; -}); \ No newline at end of file diff --git a/addons/cb.panel.outline/views/tags.js b/addons/cb.panel.outline/views/tags.js deleted file mode 100644 index 93ddf941..00000000 --- a/addons/cb.panel.outline/views/tags.js +++ /dev/null @@ -1,182 +0,0 @@ -define([ - "settings", - "less!stylesheets/panel.less" -], function(panelSettings) { - var _ = codebox.require("hr/utils"); - var Q = codebox.require("hr/promise"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var ContextMenu = codebox.require("utils/contextmenu"); - var rpc = codebox.require("core/backends/rpc"); - var box = codebox.require("core/box"); - var files = codebox.require("core/files"); - - var TagsView = hr.View.extend({ - className: "outline-file", - events: { - "click": "onClick", - "keyup input": "onKeyup" - }, - - initialize: function() { - TagsView.__super__.initialize.apply(this, arguments); - - this.$filterInput = $("", { - 'type': "text", - 'class': "form-control input-sm", - "placeholder": "Filter Tags" - }); - this.$tree = $("
            ", { - 'class': "tags-tree" - }); - - this.$filterInput.appendTo(this.$el); - this.$tree.appendTo(this.$el); - }, - - render: function() { - var that = this; - - return this.updateTags() - .fail(function(err) { - that.$tree.html("
            "+(err.message || err)+"
            "); - }) - .fin(function() { - that.ready(); - }); - }, - - // Add tag - addTag: function(tag, $parent, hasChildren) { - var $tags, that = this; - - var open = function() { - files.open(that.options.path, { - // Open content "/^ foo $/"" _> "foo" in editor - pattern: tag.pattern.slice(2, -2) - }); - }; - - var $tag = $("
          • ", { - 'data-tag': tag.name, - 'class': "type-"+(tag.kind || "v"), - 'click': function(e) { - e.stopPropagation(); - - $tag.toggleClass("open"); - if (!hasChildren) open(); - }, - 'dblclick': open - }); - - var $span = $("", { - 'text': tag.showName - }); - $span.appendTo($tag); - - if (hasChildren) { - $tags = $("
              ", { - 'class': "tags-tree" - }).appendTo($tag); - $("", { - 'class': "fa fa-angle-right tag-icon-close" - }).prependTo($span); - $("", { - 'class': "fa fa-angle-down tag-icon-open" - }).prependTo($span); - } else { - $("", { - 'class': "fa fa-blank" - }).prependTo($span); - } - - $tag.appendTo($parent); - - return { - '$tag': $tag, - '$children': $tags - }; - }, - - // Get tag view - getTag: function(name) { - var $tag = this.$("li[data-tag='"+name+"']"); - if ($tag.length == 0) return null; - return $($tag.get(0)); - }, - - // Convert tags as a tree - convertTagsToTree: function(tags) { - var that = this, tree = {}; - var tagSeparator = panelSettings.user.get("separator"); - - _.chain(tags) - .sortBy(function(tag) { - return tag.name.length; - }) - .each(function(tag) { - var parent = tree; - var parts = tag.name.split(tagSeparator); - var _name = _.last(parts); - - _.each(parts, function(part, i) { - if (parent[part]) { - parent = parent[part].children; - } else { - _name = parts.slice(i, parts.length).join(tagSeparator); - return false; - } - }); - - parent[_name] = tag; - parent[_name].showName = _name; - parent[_name].children = {}; - }); - - return tree; - }, - - // Update complete tree - updateTags: function() { - var message = "No outline available for the current file."; - var that = this, tree, path = this.options.path; - - // Clear tree - this.$tree.empty(); - - // No current file - if (!path || path == "/") { - return Q.reject(message); - } - - return rpc.execute("codecomplete/get", { - 'file': path - }) - .then(function(tags) { - if (tags.results.length == 0) return Q.reject(message); - tree = that.convertTagsToTree(tags.results); - - var addChildren = function($parent, tags) { - _.each(tags, function(tag, name) { - var vTag = that.addTag(tag, $parent, _.size(tag.children) > 0); - - addChildren(vTag.$children, tag.children); - }); - } - addChildren(that.$tree, tree); - }); - }, - - onClick: function() { - this.$filterInput.focus(); - }, - onKeyup: function(e) { - var query = this.$filterInput.val().toLowerCase(); - this.$("li").each(function() { - $(this).toggle(!query || $(this).text().toLowerCase().indexOf(query) !== -1); - }); - } - }); - - return TagsView; -}); \ No newline at end of file diff --git a/addons/cb.project/autorun.js b/addons/cb.project/autorun.js deleted file mode 100644 index 2d627f1b..00000000 --- a/addons/cb.project/autorun.js +++ /dev/null @@ -1,146 +0,0 @@ -define([ - 'settings', - 'ports' -], function(settings, ports) { - var _ = codebox.require("hr/utils"); - var commands = codebox.require("core/commands/toolbar"); - var operations = codebox.require("core/operations"); - var box = codebox.require("core/box"); - var dialogs = codebox.require("utils/dialogs"); - var alerts = codebox.require("utils/alerts"); - var Command = codebox.require("models/command"); - - // Currently running terminal - // undefined -> nothing is running - // null -> preparing the run - // {...} _> running - var runningTerm = undefined; - - // Map type -> icon - var typeIcons = { - 'run': "fa-play", - 'build': "fa-cog fa-spin", - 'clean': "fa-eraser" - }; - - // Run command - var runCommand = commands.register("project.run", { - category: "Project", - title: "Run", - icons: { - 'default': "play", - }, - offline: false, - position: 1, - shortcuts: [ - "alt+r" - ] - }, function(options) { - options = _.defaults(options || {}, { - 'ignoreErrors': false - }); - - if (runningTerm !== undefined) { - // For stopping: Close terminal - if (runningTerm != null) { - runningTerm.terminal.closeTab(); - } - - return; - } - - setRunTerminal(null); - - // Run - return box.run({ - 'id': options.id, - 'type': options.type - }) - .then(function(runInfo) { - var op = operations.start("project.run."+runInfo.shellId, null, { - 'title': runInfo.name+" running on port "+runInfo.port, - 'icons': { - 'default': typeIcons[runInfo.type] || "play", - }, - 'action': function() { - // Open the url - window.open(runInfo.url); - - // Check that port is still active - ports.update().then(function(ports) { - var _port = _.find(ports, function(proc) { - return proc.port == runInfo.port; - }); - if (!_port) { - op.destroy(); - } - }); - } - }); - - // Terminal is close: finish the operation - runInfo.terminal.on("tab:close", function() { - op.destroy(); - setRunTerminal(undefined); - }); - - // Set active runningterminal - setRunTerminal(runInfo); - - // Update list of ports - ports.update(); - - // Open url if settings is set for - if (settings.user.get("openrundialog", true)) { - dialogs.confirm("Application is now running on port "+runInfo.port, "Open "+_.escape(runInfo.url)+" in a new window? (This dialog can be disabled in the settings).") - .progress(function(diag) { - diag.once("close", function(result, e) { - if (result) window.open(runInfo.url); - }) - }); - } - }, function(err) { - setRunTerminal(undefined); - - if (!options.ignoreErrors) dialogs.alert("Error running this project", "An error occurred when trying to run this project: "+(err.message || err)); - }); - }); - - var setRunTerminal = function(st) { - var previous = runningTerm; - runningTerm = st; - - // Change command state - if (runningTerm != null) { - runCommand.set({ - 'title': "Stop", - 'icons': { - 'default': "stop" - } - }); - } else { - runCommand.set({ - 'title': "Run", - 'icons': { - 'default': "play" - } - }); - } - - if (previous - && previous.type == "run" - && runningTerm === undefined) { - // Run stop command - return runCommand.run({ - 'type': "stop", - 'ignoreErrors': true - }); - } - }; - - setRunTerminal(undefined); - - return { - 'command': runCommand - } -}); \ No newline at end of file diff --git a/addons/cb.project/client.js b/addons/cb.project/client.js deleted file mode 100644 index 83eff385..00000000 --- a/addons/cb.project/client.js +++ /dev/null @@ -1,77 +0,0 @@ -define([ - 'runner', - 'ports', - 'autorun', - 'samples' -], function(runner, ports, autorun, samples) { - var _ = codebox.require("hr/utils"); - var operations = codebox.require("core/operations"); - var app = codebox.require("core/app"); - var box = codebox.require("core/box"); - var menu = codebox.require("core/commands/menu"); - var dialogs = codebox.require("utils/dialogs"); - var alerts = codebox.require("utils/alerts"); - - // Add samples submenu - menu.getById("file").menuSection([ - samples.command - ]) - - // Add menu - menu.register("project", { - title: "Project", - offline: false - }).menuSection([ - autorun.command, - runner.command - ]).menuSection([ - { - 'id': "project.build", - 'category': "Project", - 'title': "Build", - 'offline': false, - 'action': function() { - return autorun.command.run({ - 'type': "build" - }); - }, - 'shortcuts': [ - "mod+b" - ] - }, - { - 'id': "project.clean", - 'category': "Project", - 'title': "Clean", - 'offline': false, - 'action': function() { - return autorun.command.run({ - 'type': "clean" - }); - }, - 'shortcuts': [ - "mod+shift+k" - ] - } - ]).menuSection([ - { - - 'id': "project.ports.refresh", - 'category': "Project", - 'title': "Refresh Ports", - 'offline': false, - 'action': ports.update - }, - ports.command - ]); - - // Auto-updates - box.on("box:project:define", function() { - runner.update(); - }); - - // Updates list - runner.update(); - ports.update(); - samples.update(); -}); \ No newline at end of file diff --git a/addons/cb.project/package.json b/addons/cb.project/package.json deleted file mode 100644 index 05329674..00000000 --- a/addons/cb.project/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.project", - "version": "0.1.0", - "title": "Project", - "description": "Project types manager: autorun, sample", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.project/ports.js b/addons/cb.project/ports.js deleted file mode 100644 index 810b5f71..00000000 --- a/addons/cb.project/ports.js +++ /dev/null @@ -1,43 +0,0 @@ -define([], function() { - var _ = codebox.require("hr/utils"); - var operations = codebox.require("core/operations"); - var box = codebox.require("core/box"); - var dialogs = codebox.require("utils/dialogs"); - var alerts = codebox.require("utils/alerts"); - var Command = codebox.require("models/command"); - - // HTTP Ports - var httpPorts = Command.register("project.ports", { - 'category': "Project", - 'title': "Running Ports", - 'type': "menu", - 'offline': false, - 'search': false - }); - - // Update running ports list - var updatePorts = function() { - return box.procHttp().then(function(ports) { - httpPorts.menu.reset(_.map(ports, function(proc) { - return { - 'title': proc.port, - 'flags': proc.reachable ? "" : "disabled", - 'action': function() { - if (proc.reachable) { - window.open(proc.url); - } else { - dialogs.alert("Your server is not accessible ", "Your server is not accessible externally because it is bound to 'localhost', please bind it to '0.0.0.0' instead"); - } - } - }; - })); - - return ports; - }); - }; - - return { - 'command': httpPorts, - 'update': updatePorts - } -}); \ No newline at end of file diff --git a/addons/cb.project/runner.js b/addons/cb.project/runner.js deleted file mode 100644 index 75b2018f..00000000 --- a/addons/cb.project/runner.js +++ /dev/null @@ -1,42 +0,0 @@ -define([ - 'autorun' -], function(autorun) { - var _ = codebox.require("hr/utils"); - var operations = codebox.require("core/operations"); - var box = codebox.require("core/box"); - var dialogs = codebox.require("utils/dialogs"); - var alerts = codebox.require("utils/alerts"); - var Command = codebox.require("models/command"); - - // Run commands - var runCommands = Command.register("project.run.action", { - 'category': "Project", - 'title': "Perform Action", - 'type': "menu", - 'offline': false, - 'search': false - }); - - // Update runner list - var updateList = function() { - return box.runner().then(function(runner) { - runCommands.menu.reset(_.map(runner, function(_runner) { - return { - 'title': _runner.name, - 'action': function() { - autorun.command.run({ - 'id': _runner.id - }); - } - }; - })); - - return ports; - }); - }; - - return { - 'command': runCommands, - 'update': updateList - } -}); \ No newline at end of file diff --git a/addons/cb.project/samples.js b/addons/cb.project/samples.js deleted file mode 100644 index 052ac478..00000000 --- a/addons/cb.project/samples.js +++ /dev/null @@ -1,49 +0,0 @@ -define([], function() { - var _ = codebox.require("hr/utils"); - var dialogs = codebox.require("utils/dialogs"); - var box = codebox.require("core/box"); - var rpc = codebox.require("core/backends/rpc"); - var Command = codebox.require("models/command"); - - // HTTP Ports - var samplesMenu = Command.register("project.samples", { - 'category': "Project", - 'title': "Use Sample Project", - 'type': "menu", - 'offline': false, - 'search': false - }); - - // Update samples list - var updateSamples = function() { - return rpc.execute("project/supported").then(function(projectTypes) { - samplesMenu.menu.reset( - _.chain(projectTypes) - .map(function(projectType) { - if (!projectType.sample) return null; - - return { - 'title': projectType.name, - 'action': function() { - dialogs.confirm("Replace workspace contents with "+projectType.name+" sample?", - "WARNING: Using a sample will erase the current contents of your workspace. Use only if your workspace is empty or if you want to wipe it").then(function() { - return rpc.execute("project/useSample", { - 'sample': projectType.id - }); - }); - } - }; - }) - .compact() - .value() - ); - - return projectTypes; - }); - }; - - return { - 'command': samplesMenu, - 'update': updateSamples - } -}); \ No newline at end of file diff --git a/addons/cb.project/settings.js b/addons/cb.project/settings.js deleted file mode 100644 index 83df4a46..00000000 --- a/addons/cb.project/settings.js +++ /dev/null @@ -1,19 +0,0 @@ -define([], function() { - var settings = codebox.require("core/settings"); - - // Add settings - return settings.add({ - 'namespace': "project", - 'title': "Project", - 'defaults': { - 'openrundialog': true - }, - 'fields': { - 'openrundialog': { - 'label': "Open Run Dialog", - 'type': "checkbox", - 'help': "Open new window when running application." - }, - } - }); -}); \ No newline at end of file diff --git a/addons/cb.settings/client.js b/addons/cb.settings/client.js deleted file mode 100644 index 570b2122..00000000 --- a/addons/cb.settings/client.js +++ /dev/null @@ -1,29 +0,0 @@ -define(["views/dialog"], function(SettingsDialog) { - var Command = codebox.require("models/command"); - var app = codebox.require("core/app"); - var dialogs = codebox.require("utils/dialogs"); - var menu = codebox.require("core/commands/menu"); - - // Add opening command - var command = Command.register("settings", { - category: "Application", - title: "Settings", - icons: { - 'default': "cog", - }, - shortcuts: [ - "mod+," - ], - position: 100, - offline: false, - action: function(page) { - dialogs.open(SettingsDialog, { - 'page': page, - 'keyboardEnter': false - }); - } - }); - - menu.getById("file").menu.add(command); -}); - diff --git a/addons/cb.settings/package.json b/addons/cb.settings/package.json deleted file mode 100644 index e8283180..00000000 --- a/addons/cb.settings/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.settings", - "version": "0.1.2", - "title": "Settings", - "description": "Configuration interface for your workspace.", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/addons/cb.settings/stylesheets/dialog.less b/addons/cb.settings/stylesheets/dialog.less deleted file mode 100644 index 49e53302..00000000 --- a/addons/cb.settings/stylesheets/dialog.less +++ /dev/null @@ -1,57 +0,0 @@ -.addon-settings-dialog { - .modal-dialog { - @media screen and (min-width: 768px) { - width: 760px; - } - } - - .modal-body { - padding: 0px; - position: relative; - width: 100%; - height: 100%; - - .settings-title { - opacity: 0.6; - } - - .settings-menu { - position: absolute; - top: 51px; - left: 0px; - width: 200px; - bottom: 0px; - overflow-y: auto; - background: #f8f8f8; - border-right: 1px solid #ddd; - - .nav-pills>li { - >a { - border-radius: 0px; - color: inherit; - border-left: 3px solid transparent; - } - - &.active { - >a { - border-left-color: #5E9EF3; - background: transparent; - } - } - } - } - - .settings-content { - margin-left: 200px; - min-height: 300px; - } - } - - .modal-footer { - margin-top: 0px; - } - - .settings-basepane { - margin: 20px; - } -} diff --git a/addons/cb.settings/templates/dialog.html b/addons/cb.settings/templates/dialog.html deleted file mode 100644 index 4a6fbc66..00000000 --- a/addons/cb.settings/templates/dialog.html +++ /dev/null @@ -1,34 +0,0 @@ -
              - -
              \ No newline at end of file diff --git a/addons/cb.settings/views/dialog.js b/addons/cb.settings/views/dialog.js deleted file mode 100644 index 147fe33e..00000000 --- a/addons/cb.settings/views/dialog.js +++ /dev/null @@ -1,66 +0,0 @@ -define([ - "text!templates/dialog.html", - "less!stylesheets/dialog.less" -], function(templateFile) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var DialogView = codebox.require("views/dialogs/base"); - var settings = codebox.require("core/settings"); - - var SettingsDialog = DialogView.extend({ - className: "addon-settings-dialog modal fade", - templateLoader: "text", - template: templateFile, - events: _.extend({}, DialogView.prototype.events,{ - "submit form": "submit", - "shown.bs.tab a[data-toggle='tab']": "changeTab" - }), - - // Template settings - templateContext: function() { - return { - 'settings': settings, - 'tabs': _.pairs(settings.sections), - 'limit': 3 - } - }, - - // Finish rendering - finish: function() { - var that = this; - settings.each(function(tab) { - tab.render(); - tab.$el.appendTo(this.$("#settings-tab-"+tab.namespace)); - }, this); - setTimeout(function() { - if (that.options.page) { - that.$(".settings-menu a[href='#settings-tab-"+that.options.page+"']").tab('show'); - } else { - that.$(".settings-menu a:first").tab('show'); - } - }, 200); - return SettingsDialog.__super__.finish.apply(this, arguments); - }, - - // Update settings - submit: function(e) { - var that = this; - - if (e != null) { - e.preventDefault(); - e.stopPropagation(); - } - - settings.save().fin(function() { - that.close(); - }); - }, - - // Change tab - changeTab: function(e) { - this.$(".settings-title").text($(e.target).text()); - } - }); - - return SettingsDialog; -}); \ No newline at end of file diff --git a/addons/cb.terminal/client.js b/addons/cb.terminal/client.js deleted file mode 100644 index d701295c..00000000 --- a/addons/cb.terminal/client.js +++ /dev/null @@ -1,137 +0,0 @@ -define([ - "node_modules/sh.js/build/sh", - "views/tab" -], function(Terminal, TerminalTab) { - var Command = codebox.require("models/command"); - var commands = codebox.require("core/commands/toolbar"); - var box = codebox.require("core/box"); - var tabs = codebox.require("core/tabs"); - var settings = codebox.require("core/settings"); - var menu = codebox.require("core/commands/menu"); - - // Add settings - settings.add({ - 'namespace': "terminal", - 'title': "Terminal", - 'defaults': { - 'font': "monospace", - 'size': 13, - 'line-height': 1.3, - 'theme': 'monokai_soda' - }, - 'fields': { - 'font': { - 'label': "Font", - 'type': "select", - 'options': { - 'monospace': "Monospace", - 'arial': "Arial", - 'Courier New': "Courier New", - "'MS Sans Serif', Geneva, sans-serif;": "MS Sans Serif", - "'Lucida Sans Unicode', 'Lucida Grande', sans-serif": "Lucida Sans Unicode", - 'monaco': "Monaco (Mac OS)", - 'menlo': "Menlo (Mac OS)", - 'Ubuntu Mono': "Ubuntu Mono (Ubuntu)", - 'Consolas': "Consolas (Windows)", - 'Lucida Console': "Lucida Console (Windows)" - }, - }, - 'size': { - 'label': 'Font size', - 'type': 'number', - 'min': 8, - 'max': 20, - 'step': 1 - }, - 'line-height': { - 'label': 'Line height', - 'type': 'number', - 'min': 1, - 'max': 1.9, - 'step': 0.1 - }, - 'theme': { - 'label': 'Theme', - 'type': 'select', - 'options': _.chain(Terminal.themes.defaults) - .clone() - .map(function(theme, name) { - return [name, name]; - }) - .object() - .value() - } - } - }); - - // Add opening command - var command = commands.register("terminal.open", { - category: "Terminal", - title: "New Terminal", - description: "New Tab Terminal", - icons: { - 'default': "terminal", - }, - offline: false, - shortcuts: [ - "alt+t" - ] - }, function(shellId, options) { - options = _.defaults(options || {}, { - cwd: null - }); - - // Create trminal tab - var tab = tabs.add(TerminalTab, { - 'shellId': shellId, - 'cwd': options.cwd - }, { - 'type': "terminal", - 'section': "terminals" - }); - - // Return the tab - return tab; - }); - - // Restorer for tabs - tabs.addRestorer("terminal", function(tabInfos) { - var tab = tabs.add(TerminalTab, {}, { - 'type': "terminal", - 'section': "terminals" - }); - - return tab; - }); - - // List terminals menu - var terminalsList = Command.register("terminal.list", { - category: "Terminal", - title: "Open Terminals", - type: "menu", - offline: false, - search: false - }); - - var refreshList = function() { - return box.listShells().then(function(shellIds) { - terminalsList.menu.reset(_.map(shellIds, function(shellId) { - return { - title: shellId, - action: function() { - command.run(shellId) - } - } - })); - }); - }; - - // Reset list when events from shells: - box.on("box:shell:open box:shell:exit", refreshList); - - // Add the command to file/tools menu - menu.getById("file").menuSection([ - command, - terminalsList - ]); -}); \ No newline at end of file diff --git a/addons/cb.terminal/package.json b/addons/cb.terminal/package.json deleted file mode 100644 index edcc0d49..00000000 --- a/addons/cb.terminal/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "cb.terminal", - "version": "0.0.5", - "title": "Terminal", - "description": "Terminal tab integration into your workspace.", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "client" - }, - "engines": { - "codebox": ">=0.7.0" - }, - "dependencies": { - "sh.js": "git+https://github.com/FriendCode/sh.js#1.1.1" - } -} diff --git a/addons/cb.terminal/stylesheets/tab.less b/addons/cb.terminal/stylesheets/tab.less deleted file mode 100644 index 576007d3..00000000 --- a/addons/cb.terminal/stylesheets/tab.less +++ /dev/null @@ -1,76 +0,0 @@ -.addon-terminal-tab { - position: absolute; - width: 100%; - height: 100%; - background: transparent; - - .tab-panel-body .tab-panel-inner.terminal-body { - background: #000; - border-color: #000; - overflow: auto; - cursor: text; - padding: 0px; - font-weight: bold; - padding: 3px; - overflow: hidden; - - * { - user-select: initial; - -webkit-user-select: initial; - } - - .terminal-container { - position: relative; - width: 100%; - height: 100%; - font-family: Menlo,Monaco,"DejaVu Sans Mono",Consolas,"Andale Mono",monospace; - } - .terminal-container div { - margin: 0px; - padding: 0px; - } - .terminal-container .terminal { - width: 100%; - height: 100%; - font-size: 13px; - line-height: 18px; - } - .terminal-container .terminal-input { - position: absolute; - top: 0; - left: -1000%; - background: rgba(255,255,255,0.75); - color: black; - border: 0; - outline: 0; - width: 100%; - } - .terminal-container .terminal-screen-keys { - position: absolute; - top: 0; - right: 0; - } - .terminal-container .terminal-screen-keys button { - background: -webkit-linear-gradient(top, #eeeef0, #d3d3d9); - border: 1px solid #58575e; - box-shadow: 0 2px 2px rgba(0,0,0,0.25), inset 0 -2px 0 rgba(0,0,0,0.25), inset 0 1px 0 #fff; - border: 1px solid #000; - border-radius: 2px; - padding: 8px; - font-size: 14px; - text-shadow: 0 1px 0 #fff; - } - .terminal-container .terminal-screen-keys button:focus, .terminal-container .terminal-screen-keys button.active { - outline: 0px; - background: -webkit-linear-gradient(top, #ccccd0, #a3a3a9); - } - .terminal-container .terminal-size-indicator { - position: absolute; - bottom: 0; - left: 0; - background-color: rgba(255,255,255,0.75); - color: black; - padding: 0 3px; - } - }; -} \ No newline at end of file diff --git a/addons/cb.terminal/views/tab.js b/addons/cb.terminal/views/tab.js deleted file mode 100644 index 2d4c2193..00000000 --- a/addons/cb.terminal/views/tab.js +++ /dev/null @@ -1,177 +0,0 @@ -define([ - "node_modules/sh.js/build/sh", - "less!stylesheets/tab.less" -], function(Terminal) { - var _ = codebox.require("hr/utils"); - var $ = codebox.require("hr/dom"); - var hr = codebox.require("hr/hr"); - var Tab = codebox.require("views/tabs/base"); - var box = codebox.require("core/box"); - var user = codebox.require("core/user"); - - var settings = user.settings("terminal"); - - var TerminalTab = Tab.extend({ - className: Tab.prototype.className+ " addon-terminal-tab", - defaults: { - shellId: null, - resize: true, - cwd: null - }, - menuTitle: "Terminal", - events: { - 'contextmenu': "clickTerm", - 'click': "clickTerm", - 'touchstart': "clickTerm" - }, - - initialize: function(options) { - var that = this; - TerminalTab.__super__.initialize.apply(this, arguments); - this.connected = false; - this.setTabState("loading", true); - - // Init menu - this.menu.menuSection([ - { - 'type': "checkbox", - 'title': "Exit", - 'action': function(state) { - that.closeTab(); - } - } - ]); - - // Init rendering - this.term_el = $("
              ", { - 'class': "tab-panel-inner terminal-body" - }).appendTo($("
              ", {"class": "tab-panel-body"}).appendTo(this.$el)).get(0); - - // New terminal - this.term = new Terminal({ - cols: 80, - rows: 24, - theme: settings.get("theme", 'default') - }); - this.term.open(this.term_el); - - this.interval = setInterval(_.bind(this.resize, this), 2000); - - // Init codebox stream - this.sessionId = this.options.shellId || _.uniqueId("term"); - this.shell = box.openShell({ - 'shellId': this.options.shellId ? this.sessionId : this.sessionId+"-"+(new Date()).getSeconds(), - 'cwd': this.options.cwd - }); - - this.on("tab:close", function() { - clearInterval(this.interval); - this.shell.disconnect(); - this.term.destroy(); - }, this); - - this.on("tab:state", function(state) { - if (state) { - this.focus(); - } - }, this); - this.on("tab:layout", function() { - that.resize(); - }, this); - - this.setTabTitle("Terminal - "+this.sessionId); - - - this.shell.once('data', function() { - that.setTabState("loading", false); - that.resize(); - }); - - this.shell.on('data', function(chunk) { - that.write(chunk); - }); - - this.shell.on("connect", function() { - that.connected = true; - that.trigger("terminal:ready"); - }, this); - - this.shell.on('disconnect', function() { - that.writeln("Connection closed"); - that.closeTab(); - }); - - // Connect term - this.term.on('data', function(data) { - that.shell.write(data); - }); - this.term.on("resize", function(w, h) { - that.shell.resize(w, h); - }); - - this.shell.connect(); - - setTimeout(function() { - that.focus(); - }, 300); - return this; - }, - - // Render - render: function() { - this.$el.css({ - "font-family": settings.get("font", "monospace"), - "font-size": settings.get("size", 13)+'px', - "line-height": settings.get("line-height", 1.3) - }); - $(this.term_el).css({ - 'background': this.term.colors[256], - 'border-color': this.term.colors[256] - }); - - return this.ready(); - }, - - // Resize the terminal - resize: function() { - if (!this.options.resize) { return false; } - - var w = this.$el.width(); - var h = this.$el.height(); - - if (w != this._width || h != this._height) { - this._width = w; - this._height = h; - this.term.sizeToFit(); - } - - return this; - }, - - // Focus - focus: function() { - this.term.focus(); - }, - - // Write - write: function(content) { - this.term.write(content); - return this; - }, - - // Write a line - writeln: function(line) { - return this.write(line+"\r\n"); - }, - - // Block propagation of clicks to sublevel - clickTerm: function(e) { - e.stopPropagation(); - - // We stop propagation so we need to active the tab manually - this.openTab(); - } - }); - - return TerminalTab; -}); diff --git a/addons/cb.theme.dark/ace/theme.js b/addons/cb.theme.dark/ace/theme.js deleted file mode 100644 index 4c4c049f..00000000 --- a/addons/cb.theme.dark/ace/theme.js +++ /dev/null @@ -1,10 +0,0 @@ -define([ - "less!ace/theme.less" -], function(cssContent) { - - return { - 'isDark': true, - 'cssClass': "ace-codebox-dark", - 'cssText': cssContent - } -}); \ No newline at end of file diff --git a/addons/cb.theme.dark/ace/theme.less b/addons/cb.theme.dark/ace/theme.less deleted file mode 100644 index c824a690..00000000 --- a/addons/cb.theme.dark/ace/theme.less +++ /dev/null @@ -1,206 +0,0 @@ -.ace_editor.ace-codebox-dark { - background-color: #2b303b; - color: #C5C8C6; - - .ace_gutter { - background: #2b303b; - color: #757a84 - } - - .ace_print-margin { - width: 1px; - background: #333d46 - } - - .ace_cursor { - color: #AEAFAD - } - - .ace_marker-layer .ace_selection { - background: #3d4550; - } - - &.ace_multiselect .ace_selection.ace_start { - box-shadow: 0 0 3px 0px #1D1F21; - border-radius: 2px - } - - .ace_marker-layer .ace_step { - background: rgb(102, 82, 0) - } - - .ace_marker-layer .ace_bracket { - margin: -1px 0 0 -1px; - border: 1px solid #4B4E55 - } - - .ace_marker-layer .ace_active-line { - background: #333d46 - } - - .ace_gutter-active-line { - background-color: #333d46; - } - - .ace_marker-layer .ace_selected-word { - border: 1px solid #373B41 - } - - .ace_invisible { - color: #4B4E55 - } - - .ace_keyword, - .ace_meta, - .ace_storage, - .ace_storage.ace_type, - .ace_support.ace_type { - color: #B294BB - } - - .ace_keyword.ace_operator { - color: #8ABEB7 - } - - .ace_constant.ace_character, - .ace_constant.ace_language, - .ace_constant.ace_numeric, - .ace_keyword.ace_other.ace_unit, - .ace_support.ace_constant, - .ace_variable.ace_parameter { - color: #cc856c - } - - .ace_constant.ace_other { - color: #CED1CF - } - - .ace_invalid { - color: #CED2CF; - background-color: #DF5F5F - } - - .ace_invalid.ace_deprecated { - color: #CED2CF; - background-color: #B798BF - } - - .ace_fold { - background-color: #81A2BE; - border-color: #757a84 - } - - .ace_entity.ace_name.ace_function, - .ace_support.ace_function, - .ace_variable { - color: #81A2BE - } - - .ace_support.ace_class, - .ace_support.ace_type { - color: #F0C674 - } - - .ace_heading, - .ace_string { - color: #9dbe8c - } - - .ace_entity.ace_name.ace_tag, - .ace_entity.ace_other.ace_attribute-name, - .ace_meta.ace_tag, - .ace_string.ace_regexp, - .ace_variable { - color: #b55761 - } - - .ace_comment { - color: #969896 - } - - .ace_indent-guide { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y; - } - - /* Autocomplete dialog */ - &.ace_autocomplete { - width: 280px; - z-index: 200000; - background: #323d46; - color: #65737e; - border: none; - position: fixed; - box-shadow: 2px 3px 5px rgba(0,0,0,.2); - line-height: 2; - - font-smoothing: subpixel-antialiased; - -webkit-font-smoothing: subpixel-antialiased; - - .ace_marker-layer .ace_active-line { - background-color: #4f5b67; - z-index: 1; - } - .ace_text-layer .ace_selected, - .ace_text-layer .ace_selected .ace_rightAlignedText{ - color: #a1adba; - } - .ace_line-hover { - border: none; - margin-top: -1px; - background: none; - } - .ace_line-hover { - position: absolute; - z-index: 2; - } - .ace_rightAlignedText { - color: #65737e; - font-style: italic; - display: inline-block; - position: absolute; - right: 4px; - text-align: right; - z-index: -1; - } - .ace_completion-highlight{ - color: #c0c5ce; - } - } - - /* Search dialog */ - .ace_search { - border-color: transparent; - background: #1c1f25; - color: #65737e; - - .ace_searchbtn_close { - &:hover { - background-color: #323d46; - color: #65737e; - } - } - - .ace_search_field { - background: #323d46; - color: #65737e; - border-color: transparent; - } - - .ace_search_form, .ace_replace_form { - background-color: #323d46; - color: #65737e; - border-color: transparent; - } - - .ace_button { - background-color: #323d46; - color: #65737e; - } - - .ace_searchbtn, .ace_replacebtn{ - background-color: #323d46; - color: #65737e; - border-color: transparent; - } - } -} diff --git a/addons/cb.theme.dark/main.js b/addons/cb.theme.dark/main.js deleted file mode 100644 index a8e988b9..00000000 --- a/addons/cb.theme.dark/main.js +++ /dev/null @@ -1,128 +0,0 @@ -define([ - 'ace/theme' -], function(aceTheme) { - var themes = codebox.require("core/themes"); - - var bgDarker = "#1a1d24"; - var colorDarker = "#505c66"; - - var bgDark = "#222830"; - var colorDark = "#64737e"; - - var bgNormal = "#1c1f25"; - var colorNormal = "#dfe0e6"; - - var bgLight = "#2b303b"; - var colorLight = "#dadfe6"; - - themes.add({ - id: "dark", - title: "Dark", - - editor: { - 'theme': aceTheme - }, - styles: { - // Top menubar - menubar: { - 'background': bgDarker, - 'color': colorDark, - 'border-color': "#111", - - button: { - 'border-color': bgNormal - } - }, - - // Statusbar - statusbar: { - 'background': bgDarker, - 'color': colorDark, - 'border-color': "#111", - - button: { - 'border-color': bgNormal - } - }, - - // Lateral bar panels - lateralbar: { - 'background': bgDark, - - commands: { - 'background': bgDark, - 'color': colorLight - }, - body: { - 'color': colorDark - } - }, - - // Body - body: { - 'background': bgDark, - 'color': colorDark - }, - - // Tabs - tabs: { - section: { - 'border-color': bgDark - }, - header: { - 'background': bgDark, - 'color': colorDark - }, - content: { - 'background': bgLight - }, - tab: { - '&.active': { - 'background': bgLight, - 'color': colorLight - } - } - }, - - // Operations - operations: { - operation: { - 'background': bgLight, - 'color': "#fff", - 'border-color': "transparent" - } - }, - - // Alerts - alerts: { - alert: { - 'background': bgLight, - 'color': colorLight, - 'border-color': "transparent" - } - }, - - // Palette - palette: { - 'background': bgDark, - 'border-color': bgDarker, - - input: { - 'background': bgLight, - 'border-color': bgDarker, - 'color': colorLight - }, - - results: { - 'background': bgLight, - 'border-color': bgDarker, - 'color': colorLight, - - command: { - 'border-color': bgDarker - } - } - } - } - }); -}); \ No newline at end of file diff --git a/addons/cb.theme.dark/package.json b/addons/cb.theme.dark/package.json deleted file mode 100644 index e5d40d00..00000000 --- a/addons/cb.theme.dark/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cb.theme.dark", - "version": "0.0.1", - "title": "Dark Theme", - "description": "Dark and gray theme.", - "homepage": "https://github.com/FriendCode/codebox", - "license": "Apache", - "author": { - "name": "Codebox", - "email": "contact@friendco.de", - "url": "https://www.codebox.io" - }, - "client": { - "main": "main" - }, - "engines": { - "codebox": ">=0.7.0" - } -} \ No newline at end of file diff --git a/bin/codebox.js b/bin/codebox.js index 5051d6e6..ceb0a5d9 100755 --- a/bin/codebox.js +++ b/bin/codebox.js @@ -1,155 +1,12 @@ -#!/usr/bin/env node +#! /usr/bin/env node -var Q = require('q'); -var _ = require('lodash'); -var cli = require('commander'); -var path = require('path'); -var open = require("open"); -var Gittle = require('gittle'); +var codebox = require("../lib"); -var pkg = require('../package.json'); -var codebox = require("../index.js"); - -// Codebox git repo: use to identify the user -var codeboxGitRepo = new Gittle(path.resolve(__dirname, "..")); - -// Options -cli.option('-p, --port [http port]', 'Port to run the IDE'); -cli.option('-n, --hostname [http hostname]', 'Hostname to run the IDE'); -cli.option('-t, --title [project title]', 'Title for the project.'); -cli.option('-s, --sample [project type]', 'Replace directory content by a sample (warning: erase content).'); -cli.option('-o, --open', 'Open the IDE in your favorite browser'); -cli.option('-e, --email [email address]', 'Email address to use as a default authentication'); -cli.option('-u, --users [list users]', 'List of coma seperated users and password (formatted as "username:password")'); - - -// An authentication hook that uses a dictionary of users -function usersAuthHook(users) { - return function(data) { - if (!data.email || !data.token) { - return Q.reject(new Error("Need 'token' and 'email' for auth hook")); - } - - var userId = data.email; - - if (!users[userId] || data.token != users[userId]) { - return Q.reject(new Error("Invalid user !")); - } - - return { - 'userId': userId, - 'name': userId, - 'token': data.token, - 'email': data.email - }; - }; -} - -// Command 'run' -cli.command('run [folder]') -.description('Run a Codebox into a specific folder.') -.action(function(projectDirectory) { - var that = this; - var prepare = Q(); - - // Codebox.io settings - that.box = process.env.CODEBOXIO_BOXID; - that.key = process.env.CODEBOXIO_TOKEN; - that.codeboxio = process.env.CODEBOXIO_HOST || "https://api.codebox.io"; - - // Default options - that.directory = projectDirectory || process.env.WORKSPACE_DIR || "./"; - that.title = that.title || process.env.WORKSPACE_NAME; - that.port = that.port || process.env.PORT || 8000; - that.hostname = that.hostname || "0.0.0.0"; - - var users = !that.users ? {} : _.object(_.map(that.users.split(','), function(x) { - // x === 'username:password' - return x.split(':', 2); - })); - - - var config = { - 'root': that.directory, - 'title': that.title, - 'server': { - 'port': parseInt(that.port), - 'hostname': that.hostname - }, - 'users': { - 'defaultEmail': that.email - }, - 'project': { - 'forceSample': that.sample - } - }; - - // Use Codebox.io - if (that.box && that.codeboxio && that.key) { - _.extend(config, { - 'workspace': { - 'id': that.box - }, - 'hooks': { - 'auth': that.codeboxio+"/api/box/"+that.box+"/auth", - 'events': that.codeboxio+"/api/box/"+that.box+"/events", - 'settings': that.codeboxio+"/api/account/settings", - 'addons': that.codeboxio+"/api/addons/valid" - }, - 'webhook': { - 'authToken': that.key - }, - 'proc': { - 'urlPattern': 'http://web-%d.' + that.box + '.vm1.dynobox.io' - }, - 'users': { - // Don't use default git user - 'gitDefault': false - } - }); - } else if(!_.isEmpty(users)) { - _.extend(config, { - 'public': false, - 'hooks': { - 'auth': usersAuthHook(users) - } - }); - } - - // Auth user using git - prepare.fin(function() { - // Start Codebox - return codebox.start(config).then(function() { - var url = "http://localhost:"+that.port; - - console.log("\nCodebox is running at",url); - - if (that.open) { - open(url); - } - }, function(err) { - console.error('Error initializing CodeBox'); - console.error(err); - console.error(err.stack); - - // Kill process - process.exit(1); - }); - }) +codebox.start({ + port: 3000 +}) +.then(function() { + console.log("Codebox is running"); +}, function(err) { + console.log(err.stack || err.message || err); }); - -cli.on('--help', function(){ - console.log(' Version: %s', pkg.version); - console.log(''); - console.log(' Examples:'); - console.log(''); - console.log(' $ codebox run'); - console.log(' $ codebox run ./myProject'); - console.log(''); - console.log(' Use option --open to directly open the IDE in your browser:'); - console.log(' $ codebox run ./myProject --open'); - console.log(''); -}); - -cli.version(pkg.version).parse(process.argv); -if (!cli.args.length) cli.help(); diff --git a/client/collections/addons.js b/client/collections/addons.js deleted file mode 100644 index b4b4bca6..00000000 --- a/client/collections/addons.js +++ /dev/null @@ -1,233 +0,0 @@ -define([ - "hr/promise", - "hr/utils", - "hr/hr", - "core/backends/rpc", - "models/addon", - "utils/dialogs" -], function(Q, _, hr, rpc, Addon, dialogs) { - var Addons = hr.Collection.extend({ - model: Addon, - defaults: _.defaults({ - loader: "getInstalled", - loaderArgs: [], - }, hr.Collection.prototype.defaults), - - // Constructor - initialize: function() { - Addons.__super__.initialize.apply(this, arguments); - - this.resolved = {}; - this.provides = {}; - - return this; - }, - - // Get installed addons - getInstalled: function(options) { - var that = this; - - options = _.defaults(options || {}, {}); - - return rpc.execute("addons/list").then(function(data) { - that.add(_.values(data)); - }); - }, - - // Get by name - getByName: function(name) { - return this.find(function(addon) { - return addon.get("name") == name; - }); - }, - - // Check addons is installed - isInstalled: function(name) { - if (!_.isString(name)) name = name.get("name"); - - return this.getByName(name) != null; - }, - - // Check addons is a default addon - isDefault: function(name) { - if (!_.isString(name)) name = name.get("name"); - - var m = this.getByName(name); - if (m == null) return false; - return m.get("default"); - }, - - // Check is updated - isUpdated: function(addon) { - var m = this.getByName(addon.get("name")); - if (!m) return true; - return m.version() >= addon.version(); - }, - - // Get addon state - getState: function(name) { - if (!_.isString(name)) name = name.get("name"); - - var m = this.getByName(name); - if (m == null) return null; - return m.get("state"); - }, - - // Install an addon - install: function(git) { - var that = this; - - return rpc.execute("addons/install", { - 'git': git - }).then(function(data) { - that.reset([]); - return that.getInstalled(); - }); - }, - uninstall: function(name) { - var that = this; - - return rpc.execute("addons/uninstall", { - 'name': name - }).then(function() { - that.reset([]); - return that.getInstalled(); - }); - }, - - // Extract from engineer, order addons for loading - checkCycles: function() { - var that = this; - var plugins = this.map(function(addon, index) { - return { - name: addon.get("name"), - provides: addon.get("client.provides", []).concat(), - consumes: addon.get("client.consumes", []).concat(), - i: index - }; - }); - - var changed = true; - var sorted = []; - - while(plugins.length && changed) { - changed = false; - - plugins.concat().forEach(function(plugin) { - var consumes = plugin.consumes.concat(); - - var resolvedAll = true; - for (var i=0; i 0) { - var e = new Error("Error with "+_.size(errors)+" addons"); - e.addonsError = errors; - return Q.reject(e); - } - return Q(); - }); - }, - - // Get addons from an index - loadFromIndex: function(indexUrl) { - var cached, that = this, indexKey = indexUrl; - var box = require("core/box"); - - var resetCollection = function(index) { - that.reset(_.map(index.addons, function(addon) { - return _.extend(addon['package'], { - 'git': addon.git - }); - })); - return Q(index); - }; - - cached = hr.Cache.get("addons", indexKey); - if (cached) return resetCollection(cached); - - return rpc.execute("addons/registry", { - 'url': indexUrl - }) - .then(function(index) { - hr.Cache.set("addons", indexKey, index, 60*60); - return resetCollection(index); - }); - } - }); - - return Addons; -}); \ No newline at end of file diff --git a/client/collections/changes.js b/client/collections/changes.js deleted file mode 100644 index 2105e18e..00000000 --- a/client/collections/changes.js +++ /dev/null @@ -1,27 +0,0 @@ -define([ - "hr/utils", - "hr/hr", - "models/change" -], function(_, hr, Change) { - var Changes = hr.Collection.extend({ - model: Change, - - // Sort comparator - comparator: function(command) { - return command.get("path", "").length; - }, - - // Apply all - applyAll: function() { - console.log("apply all changes", this.size()); - return this.reduce(function(prev, change) { - console.log("next ", change, prev); - return prev.then(function() { - return change.apply(); - }) - }, Q()); - } - }); - - return Changes; -}); \ No newline at end of file diff --git a/client/collections/commands.js b/client/collections/commands.js deleted file mode 100644 index 4c3780ac..00000000 --- a/client/collections/commands.js +++ /dev/null @@ -1,16 +0,0 @@ -define([ - "hr/utils", - "hr/hr", - "models/command" -], function(_, hr, Command) { - var Commands = hr.Collection.extend({ - model: Command, - - // Sort comparator - comparator: function(command) { - return command.get("position", 2); - } - }); - - return Commands; -}); \ No newline at end of file diff --git a/client/collections/files.js b/client/collections/files.js deleted file mode 100644 index 6c51e8dc..00000000 --- a/client/collections/files.js +++ /dev/null @@ -1,11 +0,0 @@ -define([ - "hr/utils", - "hr/hr", - "models/file" -], function(_, hr, File) { - var Files = hr.Collection.extend({ - model: File - }); - - return Files; -}); \ No newline at end of file diff --git a/client/collections/operations.js b/client/collections/operations.js deleted file mode 100644 index 95486c2b..00000000 --- a/client/collections/operations.js +++ /dev/null @@ -1,66 +0,0 @@ -define([ - "hr/utils", - "hr/hr", - "hr/promise", - "models/operation", - "utils/dialogs" -], function(_, hr, Q, Operation, dialogs) { - var Operations = hr.Collection.extend({ - model: Operation, - - // Sort comparator - comparator: function(command) { - return command.get("progress", 0); - }, - - // Get by id - getById: function(opId) { - return this.find(function(op) { - return op.id == opId; - }); - }, - - // Start an operation - start: function(opId, startMethod, properties, options) { - var op, d; - - options = _.defaults({}, options || {}, { - 'unique': true - }); - - op = this.getById(opId); - - if (op) return Q.reject(new Error("An operation with this id is already running: "+opId)); - - op = new Operation({}, _.extend({ - 'id': opId - }, properties || {})); - - this.add(op); - - if (startMethod) { - d = startMethod(op); - - // Error during the operation - d.fail(function(err) { - dialogs.alert("Error during an operation ("+_.escape(opId)+")", err.message || err); - }); - - // Progress - d.progress(function(p) { - if (_.isNumber(p)) op.progress(p); - }); - - // Destroy the operation - d.fin(function() { - op.destroy(); - }); - return d; - } else { - return op; - } - } - }); - - return Operations; -}); \ No newline at end of file diff --git a/client/collections/tabs.js b/client/collections/tabs.js deleted file mode 100644 index 20f6b6f4..00000000 --- a/client/collections/tabs.js +++ /dev/null @@ -1,25 +0,0 @@ -define([ - "hr/utils", - "hr/hr", - "models/tab" -], function(_, hr, Tab) { - var Tabs = hr.Collection.extend({ - model: Tab, - - // Return a tab by its id - getById: function(id) { - return this.find(function(tab) { - return tab.id == id; - }); - }, - - // Return current active tab - getActive: function() { - return this.find(function(tab) { - return tab.get("active"); - }); - }, - }); - - return Tabs; -}); \ No newline at end of file diff --git a/client/collections/users.js b/client/collections/users.js deleted file mode 100644 index 106c2c41..00000000 --- a/client/collections/users.js +++ /dev/null @@ -1,27 +0,0 @@ -define([ - "hr/utils", - "hr/hr", - "core/backends/rpc", - "models/user" -], function(_, hr, rpc, User) { - var Users = hr.Collection.extend({ - model: User, - - // Return an user from the collection by its user id - getById: function(userId) { - return this.find(function(model) { - return model.get("userId") == userId; - }) - }, - - // Get list of collaborators from the box - getCollaborators: function() { - var that = this; - return rpc.execute("users/list").then(function(data) { - that.reset(data); - }); - } - }); - - return Users; -}); \ No newline at end of file diff --git a/client/core/addons.js b/client/core/addons.js deleted file mode 100644 index 01c2b53e..00000000 --- a/client/core/addons.js +++ /dev/null @@ -1,59 +0,0 @@ -define([ - 'hr/hr', - 'hr/promise', - 'models/command', - 'collections/commands', - 'collections/addons', - 'utils/dialogs', - 'core/operations' -], function (hr, Q, Command, Commands, Addons, dialogs, operations) { - // Collection for all installed addons - var addons = new Addons(); - - // Command to install with an url - Command.register("addons.install", { - category: "Add-ons", - title: "Install", - description: "Install with GIT Url", - offline: false, - action: function(url) { - return Q() - .then(function() { - if (url) return url; - return dialogs.prompt("Install a new addon", "GIT url for the addon:", ""); - }) - .then(function(_url) { - return operations.start("addon.install", function(op) { - return addons.install(_url); - }, { - title: "Installing add-on" - }); - }) - } - }); - - // Command to uninstall from a name - Command.register("addons.uninstall", { - category: "Add-ons", - title: "Uninstall", - description: "Uninstall with name", - offline: false, - search: false, - action: function(name) { - return Q() - .then(function() { - if (name) return name; - return dialogs.prompt("Uninstall an addon", "Name of the addon:", ""); - }) - .then(function(_name) { - return operations.start("addon.uninstall", function(op) { - return addons.uninstall(_name); - }, { - title: "Uninstalling add-on" - }); - }) - } - }); - - return addons; -}); \ No newline at end of file diff --git a/client/core/app.js b/client/core/app.js deleted file mode 100644 index 65e1aa1c..00000000 --- a/client/core/app.js +++ /dev/null @@ -1,252 +0,0 @@ -define([ - 'hr/hr', - 'utils/url', - 'utils/dialogs', - 'utils/alerts', - 'utils/loading', - 'views/grid', - 'text!resources/templates/main.html', - 'core/box', - 'core/session', - 'core/addons', - 'core/box', - 'core/files', - 'core/commands/toolbar', - 'core/commands/menu', - 'core/commands/statusbar', - 'core/commands/palette', - 'core/tabs', - 'core/panels', - 'core/operations', - 'core/localfs', - 'core/themes', - 'core/search/commands', - 'core/search/files', - 'core/search/tags', - 'core/search/addons', - 'core/search/code' -], function (hr, url, dialogs, alerts, loading, GridView, templateFile, -box, session, addons, box, files, commands, menu, statusbar, palette, tabs, panels, operations, localfs, themes) { - - // Define base application - var Application = hr.Application.extend({ - name: "Codebox", - template: templateFile, - metas: { - "robots": "noindex, nofollow", - "description": "Cloud IDE on a box.", - "apple-mobile-web-app-capable": "yes", - "apple-mobile-web-app-status-bar-style": "black", - "viewport": "width=device-width, initial-scale=1, user-scalable=no" - }, - links: { - "icon": hr.Urls.static("images/icons/32.png"), - "apple-touch-icon": hr.Urls.static("images/icons/ios.png") - }, - events: { - "click .cb-login .login-box #login-submit": "actionLoginBox" - }, - - // Constructor - initialize: function() { - Application.__super__.initialize.apply(this, arguments); - this._autologin = true; - this.loginError = null; - - // Init base grid for UI - this.grid = new GridView({ - columns: 1000 - }); - - // Add lateral bar: panels and operations - var v = this.grid.addView(new hr.View(), { - width: 18 - }); - panels.$el.appendTo(v.$el); - operations.$el.appendTo(v.$el); - - // Add operations - operations.on("add remove reset", function() { - setTimeout(function() { - panels.$el.css("bottom", operations.$el.height()); - }, 200); - }); - - // Add tabs - this.grid.addView(tabs); - - // Default tab: new file - tabs.on("tabs:default tabs:opennew", function() { - files.openNew(); - }, this); - - // Offline: state/update - hr.Offline.on("state", function(state) { - if (!state) { - alerts.show("Caution: Connection lost, Workspace is now in Offline mode", 5000); - if (!localfs.isSyncEnabled()) { - dialogs.alert("Caution: Connection lost", "Offline file synchronization is not enabled for this workspace, enable it first when online."); - } - } else { - dialogs.confirm("Connection detected", "Save changes before refreshing. Do you want to refresh now (unsaved changes will be lost) ?") - .then(function() { - location.reload(); - }); - } - }); - hr.Offline.on("update", function() { - location.reload(); - }); - - // Title changed - box.on("change:name", function() { - this.title(box.get("name")); - }, this); - - return this; - }, - - // Template rendering context - templateContext: function() { - return { - 'email': hr.Cookies.get("email"), - 'token': hr.Cookies.get("token"), - 'loginError': this.loginError - }; - }, - - // Render the application - render: function() { - var email = hr.Cookies.get("email"); - var password = hr.Cookies.get("token"); - - if (!box.isAuth() && ((email && password) || (email && box.get("public"))) && this._autologin) { - this.doLogin(email, password); - return; - } - return Application.__super__.render.apply(this, arguments); - }, - - // Finish rendering - finish: function() { - var that = this; - - if (box.isAuth()) { - // Add menu - menu.$el.appendTo(this.$(".cb-menubar")); - menu.render(); - - // Add statusbar - statusbar.$el.appendTo(this.$(".cb-statusbar")); - statusbar.render(); - - // Add commands - commands.$el.appendTo(this.$(".cb-commands")); - commands.render(); - - // Add grid - this.grid.$el.appendTo(this.$(".cb-body")); - - // Add palette - palette.$el.appendTo(this.$(".cb-body")); - palette.render(); - - // Load addons - loading.show(addons.loadAll()).fail(function(err) { - return dialogs.alert("Error loading Add-ons", - "

              Error when initializing addons." + - " Please check addons states using the addons manager and reinstall problematic add-ons.

              " + - "

              Error message: "+ (err.message || err) +"

              " + - _.map(err.addonsError || [], function(error) { - return "

              - "+_.escape(error.addon)+": "+(error.error.message || error.error)+"

              "; - }).join("\n")); - }) - .fin(themes.init) - .fin(function() { - // Load new addons - addons.on("add", function(addon) { - addon.load(); - }); - - // Check update - hr.Offline.checkUpdate(); - - // Trigger event that app is ready - that.trigger("ready"); - - // Open new file if not files opened by addons and no restored tabs - tabs.restoreTabs() - .then(function(_n) { - if (files.active.size() == 0 && _n == 0) files.openNew(); - }); - }); - } - return Application.__super__.finish.apply(this, arguments); - }, - - // Login to box - actionLoginBox: function(e) { - var that = this; - if (e) { - e.preventDefault(); - } - - var email = this.$(".login-box #login-email").val(); - var password = this.$(".login-box #login-token").val(); - - this.doLogin(email, password); - }, - - // Do login - doLogin: function(email, password) { - var that = this; - - // If public: generate a random password - if (box.get("public")) { - password = Math.random().toString(36).substring(8); - } - - // Clear errors - this.$(".login-box .form-group").removeClass("has-error"); - - // No email - if (!email) { - this.$(".login-box #login-email").parent(".form-group").addClass("has-error"); - return Q.reject(new Error("No email")); - } - - // No password - if (!password) { - this.$(".login-box #login-token").parent(".form-group").addClass("has-error"); - return Q.reject(new Error("No password")); - } - - return session.start(email, password).then(function() { - hr.Cookies.set("email", email); - hr.Cookies.set("token", password); - - that.render(); - }).fail(function(err) { - that._autologin = false; - that.loginError = err; - - loading.stop(); - that.render(); - }); - }, - - // Toggle mode - toggleMode: function(mode, st) { - $("#codebox").toggleClass("mode-"+mode, st); - st = this.hasMode(mode); - $(".cb-active-mode-"+mode).toggleClass("active", st); - $(".cb-inactive-mode-"+mode).toggleClass("active", !st); - }, - hasMode: function(mode, st) { - return $("#codebox").hasClass("mode-"+mode); - } - }); - - var app = new Application(); - return app; -}); diff --git a/client/core/backends/vfs.js b/client/core/backends/vfs.js deleted file mode 100644 index 0d1ade59..00000000 --- a/client/core/backends/vfs.js +++ /dev/null @@ -1,176 +0,0 @@ -define([ - 'hr/utils', - 'hr/hr', - 'utils/url', - 'core/localfs' -], function(_, hr, Url, localfs) { - var logger = hr.Logger.addNamespace("vfs"); - - // Create backend - var vfs = new hr.Backend({ - prefix: "vfs" - }); - - // Emulate a vfs event - var triggerWatchEvent = function(name, path, data) { - var eventName = "watch.change."+name; - vfs.trigger("event:"+eventName, { - 'event': eventName, - 'data': _.extend({}, { - 'path': path, - 'change': name, - 'source': "fakeVfs" - }, data) - }); - }; - - // Refresh all vfs when offlien change - hr.Offline.on("state", function() { - triggerWatchEvent("folder", "/"); - }); - - // Map vfs method -> http request method - var methodsMap = { - "listdir": "getJSON", - "write": "put", - "mkdir": "put", - "create": "put", - "special": "post", - "remove": "delete", - "read": "get" - }; - - // Base method when connection is on - vfs.defaultMethod({ - execute: function(args, options, method) { - if (args && method != "write") args = JSON.stringify(args); - if (!options.url) return Q.reject(new Error("VFS requests need 'url' option")); - if (!methodsMap[method]) return Q.reject(new Error("Invalid VFS request: "+method)); - - logger.log(method+": "+options.url); - return hr.Requests[methodsMap[method]](options.url, args, options); - }, - after: function(args, results, options, method) { - var path = localfs.urlToPath(options.url); - switch (method) { - case "remove": - triggerWatchEvent("delete", path); - break; - case "special": - // Rename - if (args.renameFrom) { - triggerWatchEvent("delete", args.renameFrom); - triggerWatchEvent("create", path); - } else if (args.copyFrom){ - triggerWatchEvent("create", path); - } - - break; - case "create": - triggerWatchEvent("create", path); - break; - case "mkdir": - triggerWatchEvent("create", path); - break; - case "write": - triggerWatchEvent("update", path); - break; - } - } - }); - - // Read a file content - vfs.addMethod('read', { - fallback: function(args, options) { - var path = localfs.urlToPath(options.url); - return localfs.read(path); - }, - after: function(args, results, options) { - - } - }); - - // Create a new file - vfs.addMethod('create', { - fallback: function(args, options) { - var path = localfs.urlToPath(options.url); - return localfs.create(path, args); - }, - after: function(args, results, options) { - localfs.autoSync(); - } - }); - - // Create a new directory - vfs.addMethod('mkdir', { - fallback: function(args, options) { - var path = localfs.urlToPath(options.url); - return localfs.mkdir(path, args); - }, - after: function(args, results, options) { - localfs.autoSync(); - } - }); - - // Write a file content - vfs.addMethod('write', { - fallback: function(args, options) { - var path = localfs.urlToPath(options.url); - return localfs.write(path, args); - }, - after: function(args, results, options) { - localfs.autoSync(); - } - }); - - // Rename a file - vfs.addMethod('special', { - fallback: function(args, options) { - // Rename - if (args.renameFrom) { - var to = localfs.urlToPath(options.url); - var from = args.renameFrom; - - if (!from) return Q.reject("need 'renameFrom'"); - return localfs.mv(from, to); - } - // Copy - else if (args.copyFrom) { - var to = localfs.urlToPath(options.url); - var from = args.copyFrom; - - if (!from) return Q.reject("need 'copyFrom'"); - return localfs.cp(from, to); - } else { - return Q.reject("Invalid special operations"); - } - }, - after: function(args, results, options) { - localfs.autoSync(); - } - }); - - // Remove a file or directory - vfs.addMethod('remove', { - fallback: function(args, options) { - var path = localfs.urlToPath(options.url); - return localfs.rm(path, args); - }, - after: function(args, results, options) { - localfs.autoSync(); - } - }); - - // List a directory - vfs.addMethod('listdir', { - fallback: function(args, options) { - var path = localfs.urlToPath(options.url); - return localfs.ls(path); - }, - after: function(args, results, options) { - - } - }); - - return vfs; -}); \ No newline at end of file diff --git a/client/core/box.js b/client/core/box.js deleted file mode 100644 index cfdf1687..00000000 --- a/client/core/box.js +++ /dev/null @@ -1,20 +0,0 @@ -define([ - 'hr/promise', - 'hr/hr', - 'models/box', - 'core/search', - 'core/collaborators' -], function (Q, hr, Codebox, search, collaborators) { - // Current box - var box = new Codebox(); - - // Bind collaborators changement - box.on("box:users:add", function(e) { - collaborators.add(e.data); - }); - box.on("box:users:remove", function(e) { - collaborators.remove(collaborators.getById(e.data.userId)); - }); - - return box; -}); \ No newline at end of file diff --git a/client/core/collaborators.js b/client/core/collaborators.js deleted file mode 100644 index 7de8e885..00000000 --- a/client/core/collaborators.js +++ /dev/null @@ -1,19 +0,0 @@ -define([ - 'hr/utils', - 'hr/hr', - 'collections/users', - 'utils/alerts' -], function (_, hr, Users, alerts) { - // Collection for all current collaborators - var collaborators = new Users(); - - collaborators.on("add", function(user) { - alerts.show(user.get("name")+" just joined the workspace", 5000); - }); - - collaborators.on("remove", function(user) { - alerts.show(user.get("name")+" just left the workspace", 5000); - }); - - return collaborators; -}); \ No newline at end of file diff --git a/client/core/commands/menu.js b/client/core/commands/menu.js deleted file mode 100644 index 9c4d8557..00000000 --- a/client/core/commands/menu.js +++ /dev/null @@ -1,50 +0,0 @@ -define([ - 'hr/utils', - 'hr/hr', - 'views/commands/menubar', - 'core/box', - 'core/panels', - 'core/tabs', - 'core/session', - 'core/localfs', - 'core/settings' -], function (_, hr, MenubarView, box, panels, tabs, session, localfs, settings) { - // Collection for all menu commands - var menu = new MenubarView(); - - menu.register("view", { - title: "View", - position: 5 - }).menuSection({ - 'id': "themes.settings", - 'category': "View", - 'title': "Settings", - 'description': "Open Theme and View Settings", - 'offline': false, - 'action': function() { - settings.open("themes"); - } - }).menuSection([ - panels.panelsCommand - ]).menuSection([ - tabs.layoutCommand - ]); - - menu.register("file", { - title: "File", - position: 0 - }).menuSection([{ - 'id': "quit", - 'category': "Application", - 'title': "Quit", - 'description': "Close Current Session", - 'shortcuts': [ - "alt+q" - ], - 'action': session.exit - }], { - 'position': 1000 - }); - - return menu; -}); \ No newline at end of file diff --git a/client/core/commands/palette.js b/client/core/commands/palette.js deleted file mode 100644 index fa426d07..00000000 --- a/client/core/commands/palette.js +++ /dev/null @@ -1,26 +0,0 @@ -define([ - 'views/commands/palette', - 'core/commands/toolbar', - 'core/search' -], function (PaletteView, commands, search) { - - var palette = new PaletteView({ - searchHandler: _.bind(search.query, search) - }); - - commands.register("palette.toggle", { - title: "Palette", - description: "Toggle Command Palette", - icons: { - 'default': "search", - }, - position: 0, - shortcuts: [ - "mod+shift+p", "alt+s" - ] - }, function() { - palette.toggle(); - }); - - return palette; -}); \ No newline at end of file diff --git a/client/core/commands/statusbar.js b/client/core/commands/statusbar.js deleted file mode 100644 index 7ec36f6d..00000000 --- a/client/core/commands/statusbar.js +++ /dev/null @@ -1,20 +0,0 @@ -define([ - 'hr/utils', - 'hr/hr', - 'views/commands/statusbar' -], function (_, hr, StatusbarView) { - // Collection for all statusbar commands - var statusbar = new StatusbarView(); - - // Feedback - statusbar.register("statusbar.sendfeedback", { - title: "Send Feedback", - position: 5, - offline: false, - search: false - }, function() { - window.open("https://github.com/FriendCode/codebox/issues"); - }); - - return statusbar; -}); \ No newline at end of file diff --git a/client/core/commands/toolbar.js b/client/core/commands/toolbar.js deleted file mode 100644 index 0fe133e3..00000000 --- a/client/core/commands/toolbar.js +++ /dev/null @@ -1,11 +0,0 @@ -define([ - 'hr/utils', - 'hr/hr', - 'views/commands/toolbar', - 'core/search', -], function (_, hr, CommandsToolbar, search) { - // Collection for all toolbar commands - var commands = new CommandsToolbar(); - - return commands; -}); \ No newline at end of file diff --git a/client/core/debug/breakpoints.js b/client/core/debug/breakpoints.js deleted file mode 100644 index 55485fcc..00000000 --- a/client/core/debug/breakpoints.js +++ /dev/null @@ -1,47 +0,0 @@ -define([ - "hr/hr", - "hr/utils" -], function(hr, _) { - - var Breakpoints = hr.Class.extend({ - initialize: function() { - Breakpoints.__super__.initialize.apply(this, arguments); - - // map filename -> array of int - this.breakpoints = {}; - }, - - // Signal change for a breakpoint - signalChange: function(change, path, line) { - this.trigger("change:"+change, { - 'change': change, - 'path': path, - 'line': line - }); - }, - - // Return all breakpoints - all: function() { - return _.clone(this.breakpoints); - }, - - // Get breakpoints for a file - getFileBreakpoints: function(path) { - return this.breakpoints[path] || []; - }, - - // When breakpoints changed for a file - setFileBreakpoints: function(path, breakpoints) { - var oldBreakpoints = this.breakpoints[path] || []; - this.breakpoints[path] = _.clone(breakpoints); - - var added = _.difference(this.breakpoints[path], oldBreakpoints); - var removed = _.difference(oldBreakpoints, this.breakpoints[path]); - - _.each(added, _.partial(_.bind(this.signalChange, this), "add", path)); - _.each(removed, _.partial(_.bind(this.signalChange, this), "remove", path)); - } - }); - - return Breakpoints; -}); \ No newline at end of file diff --git a/client/core/debug/manager.js b/client/core/debug/manager.js deleted file mode 100644 index 307d5a35..00000000 --- a/client/core/debug/manager.js +++ /dev/null @@ -1,60 +0,0 @@ -define([ - "hr/hr", - "hr/promise", - "core/debug/session", - "core/debug/breakpoints" -], function(hr, Q, DebuggerSession, DebuggerBreakpoints) { - - var Debugger = hr.Class.extend({ - initialize: function(options) { - Debugger.__super__.initialize.apply(this, arguments); - - // Active debugger - this.activeDebugger = null; - - // Debugegr running - this.state = false; - - // Current breakpoints - this.breakpoints = new DebuggerBreakpoints(); - }, - - // Open a debugger session - open: function() { - if (this.isActive()) { - // already an active debugger -> close it - return this.activeDebugger.close() - .then(_.bind(function() { - return this.open(); - }, this)); - } - - this.activeDebugger = new DebuggerSession(); - this.trigger("state", true); - - this.listenTo(this.activeDebugger, "close", function() { - this.activeDebugger = null; - this.trigger("state", false); - this.trigger("position", null); - }); - this.listenTo(this.activeDebugger, "position", function(position) { - this.trigger("position", position); - }); - - return Q(this.activeDebugger); - }, - - // Return if debug is active - isActive: function(st) { - return this.activeDebugger != null; - }, - - // Get current debug position - getPosition: function() { - if (!this.isActive()) return null; - return this.activeDebugger.position; - } - }); - - return new Debugger(); -}); \ No newline at end of file diff --git a/client/core/debug/session.js b/client/core/debug/session.js deleted file mode 100644 index 9de9b077..00000000 --- a/client/core/debug/session.js +++ /dev/null @@ -1,198 +0,0 @@ -define([ - "hr/hr", - "hr/promise", - "core/backends/rpc" -], function(hr, Q, rpc) { - - var DebuggerSession = hr.Class.extend({ - initialize: function(options) { - DebuggerSession.__super__.initialize.apply(this, arguments); - - // Session id for this debugger - this.id = null; - - // Breakpoints - this._breakpoints = []; - - // Position - this.position = null; - }, - - // Initialize the debugger - init: function(args) { - var that = this; - return rpc.execute("debug/init", args) - .then(function(dbg) { - that.id = dbg.id; - that.trigger("update:init"); - }); - }, - - // Close debugger - close: function() { - var that = this; - if (!that.id) return Q.reject(new Error("Session not yet initialized")); - - return this.execute("close") - .then(function() { - that.id = null; - that.trigger("close"); - that.stopListening(); - that.off(); - }); - }, - - // Execute a RPC requests - execute: function(method, args, options) { - var that = this; - options = _.defaults(options || {}, { - error: true - }); - - return rpc.execute("debug/"+method, _.extend(args || {}, { - 'id': this.id - })) - .then(function(data) { - return data; - }, - function(err) { - if (options.error) that.trigger("error", err); - return Q.reject(err); - }); - }, - - // Get locals - locals: function() { - return this.execute("locals"); - }, - - // Get breakpoints - breakpoints: function() { - var that = this; - - return this.execute("breakpoints") - .then(function(list) { - that._breakpoints = list; - return list; - }); - }, - - // Get backtrace - backtrace: function() { - return this.execute("backtrace") - .then(_.bind(function(trace) { - this.position = _.last(trace); - this.trigger("position", this.position); - - return trace; - }, this)); - }, - - // Get a breapoint id from its location - getBreakpoint: function(location) { - return _.find(this._breakpoints, function(point) { - return point.filename == location.path && point.line == location.line; - }); - }, - - // Add a breakpoint - breakpointAdd: function(args) { - var that = this; - return this.execute("breakpoint/add", args) - .then(function(point) { - that.trigger("update:breakpoints:add"); - return point; - }); - }, - - // Remove a breakpoint - breakpointRemove: function(num) { - var that = this; - - return this.execute("breakpoint/clear", { - 'num': num - }) - .then(function(point) { - that.trigger("update:breakpoints:clear"); - return point; - }); - }, - - // Start - start: function(arg) { - var that = this; - return this.execute("start", { - 'arg': arg - }) - .then(function(output) { - that.trigger("log", output); - that.trigger("update:start"); - }); - }, - - // Stop - stop: function() { - var that = this; - return this.execute("stop") - .then(function(output) { - that.trigger("log", output); - that.trigger("update:stop"); - }); - }, - - // Next - next: function() { - var that = this; - return this.execute("next") - .then(function(output) { - that.trigger("log", output); - that.trigger("update:next"); - }); - }, - - // Continue - cont: function() { - var that = this; - return this.execute("cont") - .then(function(output) { - that.trigger("log", output); - that.trigger("update:cont"); - }); - }, - - // Restart - restart: function() { - var that = this; - return this.execute("restart") - .then(function(output) { - that.trigger("log", output); - that.trigger("update:restart"); - }); - }, - - // Eval code - eval: function(code) { - var that = this; - - var handle = function(type, data) { - that.trigger("update:eval:"+type); - return { - 'type': type, - 'content': data.message || data - }; - }; - - return this.execute("eval", { - 'code': code - }, { - 'error': false - }) - .then( - _.partial(handle, "log"), - _.partial(handle, "error") - ); - }, - }); - - return DebuggerSession; -}); \ No newline at end of file diff --git a/client/core/files.js b/client/core/files.js deleted file mode 100644 index 510016b4..00000000 --- a/client/core/files.js +++ /dev/null @@ -1,255 +0,0 @@ -define([ - 'hr/promise', - 'hr/utils', - 'hr/hr', - 'models/file', - 'collections/files', - 'core/user', - 'core/box', - 'core/tabs', - 'core/settings', - 'utils/dialogs', - 'views/tabs/file', - 'views/files/base', - 'views/files/tab' -], function(Q, _, hr, File, Files, user, box, tabs, settings, dialogs, FileTab) { - var logging = hr.Logger.addNamespace("files"); - - // Settings for files manager - var settings = settings.add({ - 'namespace': "files", - 'title': "Files", - 'fields': {} - }); - var userSettings = user.settings("files"); - - // Recent files - var recentFiles = new Files(); - recentFiles.on("add", function() { - // Limit collection size - if (recentFiles.size() > 20) recentFiles.shift(); - }); - - // Active files - var activeFiles = new Files(); - - // Files handlers map - var handlers = {}; - - // Restorer for tabs - tabs.addRestorer("file", function(tabInfos) { - var parts = tabInfos.id.split(":"); - var handlerId = parts[0]; - var path = parts.slice(1).join(":"); - - var newFile = path.indexOf("temporary") == 0; - if (!newFile) { - return openFile(path.replace("file://", "")) - .then(function() { - return tabs.getById(tabInfos.id); - }); - } - - return null; - }); - - // Add handler - var addHandler = function(handlerId, handler) { - if (!handler - || !handlerId - || !handler.name - || !handler.valid - || (!handler.View && !handler.open)) { - throw "Invalid files handler format"; - } - - handler = _.defaults(handler, { - // Mark this file as active when open with - 'setActive': false, - - // Fallback when no correct handler - 'fallback': false, - - // Priority of this handler - 'position': 10 - }); - - handler.id = handlerId; - - if (handler.View) { - handler.open = function(file, fileOptions) { - var path = file.path(); - var uniqueId = handler.id+":"+file.syncEnvId(); - - // Add files as open - if (handler.setActive) activeFiles.add(file); - - // Add new tab - var tab = tabs.add(FileTab, { - "model": file, - "handler": handler, - 'fileOptions': fileOptions - }, { - "uniqueId": uniqueId, - "type": "file", - }); - - // Focus tab -> set active file - tab.on("tab:state", function(state) { - if (state) box.setActiveFile(file); - }); - - // Close tab -> close active file - tab.on("tab:close", function(state) { - if (handler.setActive) activeFiles.remove(file); - }); - - tab.setFileOptions(fileOptions); - }; - } - - // Add settings - settings.setField(handlerId, { - 'label': handler.name, - 'type': "checkbox", - 'default': true - }); - - // Register handler - handlers[handlerId] = handler; - - return handlers[handlerId]; - }; - - // Get handler for a file - var getHandlers = function(file) { - return _.chain(handlers) - .filter(function(handler) { - return userSettings.get(handler.id, true) && handler.valid(file); - }) - .sortBy(function(handler) { - return handler.position || 10; - }) - .value(); - }; - - // get fallback handlers for a file - var getFallbacks = function(file) { - return _.filter(handlers, function(handler) { - return userSettings.get(handler.id, true) && handler.fallback == true; - }); - }; - - // Open file with handler - var openFileHandler = function(handler, file, fileOptions) { - // Add to recent files - if (!file.isNewfile()) recentFiles.add(file); - - // Options for the file handler - fileOptions = _.defaults(fileOptions || {}, { - line: null, - pattern: null - }); - - return Q(handler.open(file, fileOptions)).then(function() { - box.setActiveFile(file); - }); - }; - - // Select to open a file with any handler - var openFileWith = function(file, fileOptions) { - var choices = {}; - _.each(handlers, function(handler) { - choices[handler.id] = handler.name; - }); - - if (_.size(choices) == 0) { - return Q.reject(new Error("No handlers for this file")); - } - - return dialogs.select("Can't open this file", "Sorry, No handler has been found to open this file. Try to find and install an add-on to manage this file or select one of the following handlers:", choices).then(function(value) { - var handler = handlers[value]; - return Q(openFileHandler(handler, file, fileOptions)); - }); - }; - - // Open a file - var openFile = function(file, options) { - options = _.defaults({}, options || {}, { - 'userChoice': null, - 'useFallback': true, - 'line': null - }); - - // Options for the file handler - var fileOptions = _.pick(options, ["line", "pattern"]); - - if (_.isString(file)) { - var nfile = new File({ - 'codebox': box - }); - return nfile.getByPath(file).then(function() { - return openFile(nfile, options); - }); - } - - var possibleHandlers = getHandlers(file); - - // Get fallbacks - if (_.size(possibleHandlers) == 0 && options.useFallback) { - possibleHandlers = getFallbacks(); - } - - // All choices - if (_.size(possibleHandlers) == 0) { - return openFileWith(file, fileOptions); - } - - if (_.size(possibleHandlers) == 1 || (options.userChoice != true)) { - return Q(openFileHandler(_.first(possibleHandlers), file, fileOptions)); - } - - var choices = {}; - _.each(possibleHandlers, function(handler) { - choices[handler.id] = handler.name; - }) - - if (_.size(choices) == 0) { - return Q.reject(new Error("No handlers for this file")); - } - - return dialogs.select("Open with...", "Select one of the following handlers to open this file:", choices).then(function(value) { - var handler = handlers[value]; - return Q(openFileHandler(handler, file, fileOptions)); - }); - }; - - // Open a new file - var openNew = function(name, content, options) { - name = name || "untitled"; - - // Create a temporary file - var f = new File({ - 'newFileContent': content || "", - 'codebox': box - }, { - 'name': name, - 'size': 0, - 'mtime': 0, - 'mime': "text/plain", - 'href': location.protocol+"//"+location.host+"/vfs/"+name, - 'exists': false - }); - - return openFile(f, options); - }; - - return { - 'addHandler': addHandler, - 'getHandlers': getHandlers, - 'open': openFile, - 'openNew': openNew, - 'recent': recentFiles, - 'active': activeFiles - }; -}); \ No newline at end of file diff --git a/client/core/globals.js b/client/core/globals.js deleted file mode 100644 index 2912459c..00000000 --- a/client/core/globals.js +++ /dev/null @@ -1,7 +0,0 @@ -define(['require'], function (require) { - window.codebox = { - 'require': require - }; - - return window.codebox; -}); \ No newline at end of file diff --git a/client/core/localfs.js b/client/core/localfs.js deleted file mode 100644 index 00210a5a..00000000 --- a/client/core/localfs.js +++ /dev/null @@ -1,519 +0,0 @@ -define([ - 'hr/utils', - 'hr/hr', - 'utils/url', - 'vendors/filer', - 'core/operations', - 'utils/alerts', - 'collections/changes' -], function(_, hr, Url, Filer, operations, alerts, Changes) { - var logger = hr.Logger.addNamespace("localfs"); - - // Base folder for localfs - var base = "/"; - var _isInit = false; - var _syncIsEnable = false; - var _ignoredFiles = []; - var changes = new Changes(); - - // Constant mime type for a directory - var MIME_DIRECTORY = "inode/directory"; - - // Duration for sync (ms) - syncDuration = 1*60*1000; - - - // Create fs interface - var filer = new Filer(); - - var fsCall = function(method, args, context) { - if (!_syncIsEnable) return Q.reject(new Error("Offline synchronization is disabled")); - if (_.isUndefined(args)) args = []; - if (!_.isArray(args)) args = [args]; - - var d = Q.defer(); - args.push(function() { - if (arguments.length == 1) return d.resolve(arguments[0]); - d.resolve(arguments); - }); - args.push(function(err) { - logger.error("Error occurs: ", method.name, err); - d.reject(err); - }) - - try { - method.apply(context, args); - } catch(err) { - d.reject(err); - } - - return d.promise; - }; - - /* - * Init the localfs - */ - var initFs = function(baseDir) { - base = "/"+baseDir; - logger.log("base is", base); - return Q(); - }; - - /* - * Enable/Disable sync - */ - var enableSync = function(state) { - _syncIsEnable = state != undefined? state : true; - }; - - /* - * Set ignored files list - */ - var setIgnoredFiles = function(files) { - _ignoredFiles = files || []; - _ignoredFiles.push("/.git") - _ignoredFiles = _.compact(_ignoredFiles); - _ignoredFiles = _.uniq(_ignoredFiles); - _ignoredFiles = _.map(_ignoredFiles, function(p) { - if (p[0] != "/") p = "/"+p; - return p; - }); - }; - - var prepareFs = function() { - if (_isInit) return Q(); - return fsCall(filer.init, { - persistent: true, - size: 10 * 1024 * 1024 - }, filer) - .then(function() { - logger.log("fs is ready"); - _isInit = true; - return Q(); - }); - }; - - var needFsReady = function(fn) { - return function() { - var args = arguments; - return prepareFs().then(function() { - return fn.apply(fn, args); - }); - }; - } - - /* - * Adapt path - */ - var adaptPath = function(path) { - path = base+path; - path = path.replace("//", "/"); - return path; - } - - /* - * Convert a vfs url in a path - */ - var urlToPath = function(url) { - var basePath = window.location.pathname; - basePath = basePath.substring(0, basePath.lastIndexOf("/")+1) + "vfs/"; - var path = url.substr(basePath.length-1); - if (path.length == 0) path = '/'; - if (path[0] != '/') path = "/" + path; - return path; - }; - - /* - * Test if path is ignored files - */ - var isIgnoredFile = function(path) { - return _.reduce(_ignoredFiles, function(state, ignoredPath) { - if (state) return state; - if (path.indexOf(ignoredPath) == 0) return true; - }, false); - }; - - /* - * Return informations about a fileentry - */ - var getEntryInfos = needFsReady(function(fEntry) { - return fsCall(fEntry.getMetadata, [], fEntry).then(function(metadata) { - var path = fEntry.fullPath.replace(base, "/").replace("//", "/"); - var url = location.protocol+"//"+location.host+"/vfs"+path; - - if (fEntry.isDirectory) url = url + "/"; - - return { - "name": fEntry.name, - "size": metadata.size, - "mtime": metadata.modificationTime.getTime(), - "mime": fEntry.isDirectory ? MIME_DIRECTORY : "application/octet-stream", - "href": url, - "exportUrl": fEntry.toURL(), - "offline": true, - '_fullPath': path - }; - }) - }); - - /* - * List a directory - */ - var listDir = needFsReady(function(path, adapt) { - path = (adapt == false) ? path : adaptPath(path); - - logger.log("ls:", path); - return fsCall(filer.ls, path, filer).then(function(entries) { - return Q.all(_.map(entries, function(entry) { - return getEntryInfos(entry); - })) - }).then(function(entries) { - return entries; - }, function(err) { - logger.error("ls:", err); - }); - }); - - /* - * Create a file - */ - var createFile = needFsReady(function(path) { - path = adaptPath(path); - logger.log("create:", path); - return fsCall(filer.create, [path, true], filer); - }); - - /* - * Write file - */ - var writeFile = needFsReady(function(path, data) { - path = adaptPath(path); - logger.log("write:", path); - return fsCall(filer.write, [path, { - 'data': data || "" - }], filer); - }); - - /* - * Open a file - */ - var openFile = needFsReady(function(path) { - path = adaptPath(path); - logger.log("open:", path); - return fsCall(filer.getEntry, [path], filer).then(function(fEntry) { - return getEntryInfos(fEntry); - }) - }); - - /* - * Read a file - */ - var readFile = needFsReady(function(path) { - path = adaptPath(path); - logger.log("read:", path); - return fsCall(filer.open, [path], filer).then(function(file) { - var d = Q.defer(); - - var reader = new FileReader(); - reader.onerror = function(err) { - d.reject(err); - }; - reader.onload = function(e) { - d.resolve(reader.result); - }; - reader.readAsText(file); - - return d.promise; - }); - }); - - /* - * Create a file - */ - var createDirectory = needFsReady(function(path) { - path = adaptPath(path); - logger.log("mkdir:", path); - return fsCall(filer.mkdir, [path, false], filer); - }); - - /* - * Move a file - */ - var move = needFsReady(function(from, to) { - from = adaptPath(from); - to = adaptPath(to); - - logger.log("move:", from, "to", to); - return fsCall(filer.mv, [from, '.', to], filer); - }); - - /* - * Copy a file - */ - var copy = needFsReady(function(from, to) { - from = adaptPath(from); - to = adaptPath(to); - - logger.log("copy:", from, "to", to); - return fsCall(filer.cp, [from, '.', to], filer); - }); - - /* - * Remove a file or directory - */ - var remove = needFsReady(function(path, adapt) { - path = (adapt == false) ? path : adaptPath(path); - - logger.log("remove:", path); - return fsCall(filer.rm, [path], filer); - }); - - /* - * Return changes - */ - var getChanges = needFsReady(function() { - var File = require("models/file"); - var box = require("core/box"); - changes.reset([]); - - if (!_syncIsEnable) return Q(changes); - - var addChange = function(path, type, args) { - if (isIgnoredFile(path)) { - return; - } - logger.log("change:",type,path); - changes.add(_.extend(args || {}, { - 'path': path, - 'time': Date.now(), - 'type': type || "M" - })); - } - - var getDirChanges = function(path) { - var localEntries, boxEntries, currentEntryInfos, fp; - if (isIgnoredFile(path)) { - return Q(); - } - - logger.log("get changes in:", path); - - // File in the workspace - fp = new File(); - - return openFile(path).then(function(infos) { - return listDir(path); - }).then(function(entries) { - // Entry in the browser - localEntries = entries; - - // Get file on the workspace - return fp.getByPath(path).fail(function() { - return Q(); - }); - }).then(function() { - if (fp.isDirectory()) { - return fp.listdir(); - } - return Q([]); - }).then(function(entries) { - // Entries on the boxes - boxEntries = entries; - }).then(function() { - // Eliminate old useless entries - return Q.all(_.map(boxEntries, function(boxFile) { - if (isIgnoredFile(boxFile.path())) return Q(); - - var localEntry = _.find(localEntries, function(localEntry) { - return localEntry.name == boxFile.get("name"); - }); - - // File don't exists and box file older than current directory - if (!localEntry && boxFile.get("mtime") < currentEntryInfos.mtime) { - // -> Remove the file on the box - addChange(boxFile.path(), "remove"); - } - - // Do nothing - return Q(); - })); - }).then(function() { - // Update entries and create new entries - return Q.all(_.map(localEntries, function(localEntry) { - var entryIsDir = localEntry.mime == MIME_DIRECTORY; - var boxFile = _.find(boxEntries, function(boxFile) { - return localEntry.name == boxFile.get("name"); - }); - - if (!boxFile) { - // Create file - if (!entryIsDir) { - addChange(localEntry._fullPath, "create"); - } else { - addChange(localEntry._fullPath, "mkdir"); - } - } else if (!entryIsDir && boxFile.get("mtime") < localEntry.mtime) { - // Check modification - return readFile(localEntry._fullPath).then(function(content) { - return boxFile.read().then(function(vfsContent) { - if (vfsContent == content) { - // Same content - return Q(); - } - addChange(localEntry._fullPath, "write", { - 'content': content - }); - }) - }); - } - - if (entryIsDir) { - return getDirChanges(localEntry._fullPath); - } - - // Do nothing - return Q(); - })); - }).fail(function(err) { - logger.error("Error during sync: ", err); - }); - }; - - return operations.start("files.sync.changes", function(op) { - return getDirChanges("/").then(function() { - return Q(changes); - }); - }, { - title: "Calculating Offline Changes" - }); - }); - - /* - * Sync a file in the box fs with the local fs - * - * this will download the files and saved them in the localfs - */ - var syncFileBoxToLocal = needFsReady(function() { - var box = require("core/box"); - - var doSync = function(fp) { - var path = fp.path(); - if (isIgnoredFile(path)) return Q(); - - logger.log("sync:box->local:", path); - - if (fp.isDirectory()) { - // Create the directory - return createDirectory(path).then(function() { - // List subfiles - return fp.listdir(); - }).then(function(files) { - // Recursively sync files and directory - return Q.all(_.map(files, function(f) { - return doSync(f); - })); - }); - } else { - // Read file content - return fp.read().then(function(content) { - // Write file content - return writeFile(path, content); - }); - } - }; - return operations.start("files.sync.download", function(op) { - logger.warn("Start sync: box->local"); - - return listDir("/", false).then(function(rootFiles) { - return Q.all(_.map(rootFiles, function(fp) { - return remove("/"+fp.name, false); - })); - }).then(function() { - return doSync(box.root); - }, function() { - return doSync(box.root) - }).then(function() { - changes.reset([]); - logger.warn("Finished sync: box->local"); - }); - }, { - title: "Updating Offline Cache" - }); - }); - - /* - * Global sync: - * -> if never sync: download everything - * -> if already sync: upload changes and download last changes - */ - var sync = needFsReady(function(options) { - options = _.defaults({}, options || {}, { - - }); - - var previousChanges = changes.size(); - - if (hr.Offline.isConnected()) { - var endT, startT = Date.now(); - return openFile("/").then(function(infos) { - return getChanges(); - }, function() { - return createDirectory("/"); - }).then(function() { - if (changes.size() > 0) { - if (changes.size() == previousChanges) return Q.reject(new Error("Offline changes not synced")); - alerts.show(changes.size()+" changes made offline need to be synced manually", 5000); - return Q.reject(new Error("Offline changes not synced")); - } - return syncFileBoxToLocal(); - }).then(function() { - //Calcul duration - endT = Date.now(); - syncDuration = _.max([endT - startT, 5000]); - updateAutoSync(); - return syncDuration; - }, function(err) { - logger.error("Sync error:", err); - }); - } else { - return Q.reject(new Error("Can't synchronize when offline")); - } - }); - - /* - * Auto sync allow to resync the localfs every interval - */ - var autoSync = null; - var updateAutoSync = function() { - syncDuration = _.max([syncDuration, 5*60*1000]); - logger.log("sync took ", syncDuration/1000,"seconds"); - autoSync = _.throttle(function() { - sync(); - }, 2*syncDuration); - }; - updateAutoSync(); - - return { - 'changes': changes, - 'urlToPath': urlToPath, - 'init': initFs, - 'ls': listDir, - 'create': createFile, - 'mkdir': createDirectory, - 'write': writeFile, - 'read': readFile, - 'mv': move, - 'cp': copy, - 'rm': remove, - 'reset': syncFileBoxToLocal, - 'sync': sync, - 'autoSync': function() { - return autoSync(); - }, - 'enableSync': enableSync, - 'isSyncEnabled': function() { return _syncIsEnable; }, - 'filer': filer, - 'syncDuration': syncDuration, - 'setIgnoredFiles': setIgnoredFiles - }; -}); \ No newline at end of file diff --git a/client/core/operations.js b/client/core/operations.js deleted file mode 100644 index 93892c0d..00000000 --- a/client/core/operations.js +++ /dev/null @@ -1,6 +0,0 @@ -define([ - 'views/operations/manager' -], function (Operations) { - var operations = new Operations(); - return operations; -}); \ No newline at end of file diff --git a/client/core/panels.js b/client/core/panels.js deleted file mode 100644 index b3aa8e03..00000000 --- a/client/core/panels.js +++ /dev/null @@ -1,6 +0,0 @@ -define([ - 'views/panels/manager' -], function (PanelsView) { - var panels = new PanelsView(); - return panels; -}); \ No newline at end of file diff --git a/client/core/search.js b/client/core/search.js deleted file mode 100644 index fb666991..00000000 --- a/client/core/search.js +++ /dev/null @@ -1,121 +0,0 @@ -define([ - 'hr/hr', - 'hr/dom', - 'hr/utils', - 'hr/promise', - 'models/command', - 'core/user', - 'core/settings' -],function(hr, $, _, Q, Command, user, settings) { - var logging = hr.Logger.addNamespace("search"); - - var Search = hr.Class.extend({ - defaults: {}, - - // Constructor - initialize: function(){ - Search.__super__.initialize.apply(this, arguments); - - // Search handlers - this.handlers = {}; - - // Settings - this.settings = settings.add({ - 'namespace': "search", - 'title': "Search", - 'fields': {} - }); - - return this; - }, - - /* - * Add a search handler - * @name: name for the search handler - * @getter: method which returns a promise with results - */ - handler: function(infos, getter) { - if (!infos.id || !infos.title) { - throw new Error("Need 'id' and 'title' to define a search handler"); - } - - // Define handler - this.handlers[infos.id] = _.defaults({ - 'getter': getter - }, infos, {}); - - // Define settings - this.settings.setField(infos.id, { - 'label': infos.title, - 'type': "checkbox", - 'default': true - }); - - logging.log("add search handler", infos.id); - - return this; - }, - - /* - * Normalize result - */ - normResult: function(handler, result) { - if (result instanceof Command) { - return result; - } else { - return new Command({}, _.defaults(result, { - 'category': handler.title - })); - } - }, - - /* - * Search by query - */ - query: function(query) { - var that = this; - var errors = []; - var d = Q.defer(); - var n = _.size(this.handlers), i = 0; - - _.each(this.handlers, function(handler, name) { - var done = function(results) { - i = i + 1; - if (results) { - d.notify({ - 'category': { - 'title': handler.title - }, - 'results': _.chain(results) - .map(_.partial(that.normResult, handler)) - .value(), - 'query': query - }); - } - if (i == n) { - if (errors.length == 0) { - d.resolve(n); - } else { - d.reject(errors); - } - } - }; - - if (!user.get("settings.search."+name, true)) return done(); - - Q() - .then(function() { - return handler.getter(query); - }) - .then(done, function(err) { - errors.push(err); - return done(); - }); - }); - - return d.promise; - } - }); - - return (new Search()); -}); \ No newline at end of file diff --git a/client/core/search/addons.js b/client/core/search/addons.js deleted file mode 100644 index d6f9c036..00000000 --- a/client/core/search/addons.js +++ /dev/null @@ -1,111 +0,0 @@ -define([ - 'hr/promise', - 'hr/utils', - 'hr/hr', - 'models/command', - 'collections/addons', - 'core/box', - 'core/addons', - 'core/search', - 'core/settings', - 'utils/string' -], function(Q, _, hr, Command, Addons, box, addons, search, settings, string) { - - var addonsSettings = settings.add({ - 'namespace': "manager", - 'title': "Addons", - 'defaults': { - 'registry': "https://api.codebox.io" - }, - 'fields': { - 'registry': { - 'label': "Registry", - 'type': "text" - } - } - }); - - // Filter a collection fo addons by a query to search for - var filterAddonsByQuery = function(_addons, query) { - return _addons.filter(function(addon) { - var text = [ - addon.get("name"), - addon.get("description") - ].join(" "); - return (string.score("Add-ons", query) > 0 - || string.score(text, query) > 0); - }); - }; - - // Transform an addon to a basic command - var addonToCommand = function(preText, addon, callback) { - return { - 'category': "Add-ons", - 'title': preText+" "+addon.get("name"), - 'label': addon.get("version")+ " - "+addon.get("author.name"), - 'icons': { - 'search': "puzzle-piece" - }, - 'offline': false, - 'action': callback - }; - }; - - // Transform an addon to an install command - var addonToInstallCommand = function(addon) { - var preText = "Install"; - if (addons.isInstalled(addon)) { - if (!addons.isUpdated(addon)) { - preText = "Update"; - } else { - return null; - } - } - return addonToCommand(preText, addon, function() { - return Command.run("addons.install", addon.get("git")); - }); - }; - - // Transform an addon to an uninstall command - var addonToUninstallCommand = function(addon) { - if (addons.isDefault(addon)) return null; - return addonToCommand("Uninstall", addon, function() { - return Command.run("addons.uninstall", addon.get("name")); - }); - }; - - // Search for add-ons to uninstall - search.handler({ - 'id': "addons:uninstall", - 'title': "Uninstall Add-ons" - }, function(query) { - if (!query) return []; - - return _.chain( - filterAddonsByQuery(addons, query) - ) - .map(addonToUninstallCommand) - .compact() - .value(); - }); - - // Search for add-ons to install/update - search.handler({ - 'id': "addons:install", - 'title': "Install Add-ons" - }, function(query) { - if (!query) return []; - - var addonsIndex = new Addons(); - - return addonsIndex.loadFromIndex(addonsSettings.user.get("registry")) - .then(function() { - return _.chain( - filterAddonsByQuery(addonsIndex, query) - ) - .map(addonToInstallCommand) - .compact() - .value(); - }); - }); -}); \ No newline at end of file diff --git a/client/core/search/code.js b/client/core/search/code.js deleted file mode 100644 index 8ca5a855..00000000 --- a/client/core/search/code.js +++ /dev/null @@ -1,155 +0,0 @@ -define([ - 'hr/promise', - 'hr/utils', - 'hr/hr', - 'models/command', - 'core/commands/menu', - 'core/backends/rpc', - 'core/search', - 'core/files', - 'utils/dialogs' -], function(Q, _, hr, Command, menu, rpc, search, files, dialogs) { - var logging = hr.Logger.addNamespace("codeSearch"); - var OPTIONS = [ - 'query', 'path', 'casesensitive', 'replacement', 'pattern', 'maxresults', - 'wholeword', 'regexp', 'replaceAll' - ]; - - - // Normalize results as a buffer - var normResults = function(results) { - // Header - var buffer = 'Searching 1 file for "'+results.options.query+'"'; - if (results.options.casesensitive) buffer += " (case sensitive)" - buffer += '\n\n'; - - _.each(results.files, function(lines, path) { - buffer += path+"\n"; - _.each(lines, function(line) { - buffer += line.line+" "+line.content+"\n"; - }); - buffer += '\n\n'; - }); - - // Footer - buffer += results.matches+" matches across "+_.size(results.files)+" files"; - - return buffer; - }; - - // Do a basic search - var searchCode = function(options) { - options = _.extend({}, options || {}); - return rpc.execute("search/code", _.pick(options, OPTIONS)); - }; - - - - var searchCommandHandler = function(title, fields, forceOptions) { - return function(args) { - if (_.isString(args)) args = {'query': args}; - args = _.defaults(args || {}, {}); - - var doSearch = function(_args) { - return searchCode(_.extend(_args, forceOptions || {})) - .then(function(results) { - return normResults(results); - }, function(err) { - logging.error("error", err); - return "Error during search: "+(err.message || err); - }) - .then(function(buffer) { - return files.openNew("Find Results", buffer); - }); - }; - - if (!args.query) { - return dialogs.fields(title, fields, args) - .then(doSearch); - } - - return doSearch(args); - } - }; - - - // Command search code - var commandSearch = Command.register("code.search", { - title: "Find in Files", - category: "Find", - shortcuts: [ - "mod+shift+f" - ], - action: searchCommandHandler("Find in Files", { - 'query': { - 'label': "Find", - 'type': "text" - }, - 'path': { - 'label': "Where", - 'type': "text" - }, - 'regexp': { - 'label': "Regular expression", - 'type': "checkbox" - }, - 'casesensitive': { - 'label': "Case sensitive", - 'type': "checkbox" - }, - 'wholeword': { - 'label': "Whole word", - 'type': "checkbox" - } - }) - }); - - // Command replace code - var commandReplace = Command.register("code.replace", { - title: "Replace in Files", - category: "Find", - shortcuts: [], - action: searchCommandHandler("Find and Replace in Files", { - 'query': { - 'label': "Find", - 'type': "text" - }, - 'path': { - 'label': "Where", - 'type': "text" - }, - 'replacement': { - 'label': "Replace", - 'type': "text" - }, - 'regexp': { - 'label': "Regular expression", - 'type': "checkbox" - }, - 'casesensitive': { - 'label': "Case Sensitive", - 'type': "checkbox" - }, - 'wholeword': { - 'label': "Whole word", - 'type': "checkbox" - } - }, { - replaceAll: true - }) - }) - - - // Create find menu - menu.register("find", { - title: "Find", - position: 5 - }).menuSection([ - commandSearch, - commandReplace - ]); - - return { - search: searchCode - }; -}); \ No newline at end of file diff --git a/client/core/search/commands.js b/client/core/search/commands.js deleted file mode 100644 index fc38bff3..00000000 --- a/client/core/search/commands.js +++ /dev/null @@ -1,29 +0,0 @@ -define([ - 'hr/promise', - 'hr/utils', - 'hr/hr', - 'models/command', - 'core/search' -], function(Q, _, hr, Command, search) { - // Search for commands - search.handler({ - 'id': "commands", - 'title': "Command" - }, function(query) { - return Command.all.filter(function(command) { - return ( - // Only action - command.get("type") == "action" - - // Accept to be visible in search bar - && command.get("search") - - // Not disabled (offline, ...) - && !command.hasFlag("disabled") - - // Fit the current search - && (!query || command.textScore(query) > 0) - ); - }); - }); -}); \ No newline at end of file diff --git a/client/core/search/files.js b/client/core/search/files.js deleted file mode 100644 index d57fe684..00000000 --- a/client/core/search/files.js +++ /dev/null @@ -1,54 +0,0 @@ -define([ - 'hr/promise', - 'hr/utils', - 'hr/hr', - 'core/box', - 'core/search', - 'core/files' -], function(Q, _, hr, box, search, files) { - // Search for files - search.handler({ - 'id': "files", - 'title': "Files" - }, function(query) { - if (!query) return []; - - return box.searchFiles(query) - .then(function(data) { - return Q(_.map(data.files, _.bind(function(path) { - var filename = _.last(path.split("/")); - if (filename.length == 0) filename = path; - - return { - "title": path, - "icons": { - "search": "file-o" - }, - "action": _.bind(function() { - files.open(path); - }, this) - }; - }, this))); - }); - }); - - // Search for recent opned files - search.handler({ - 'id': "recentfiles", - 'title': "Recent Files" - }, function(query) { - return files.recent.map(function(file) { - return { - "category": "Recent Files", - "title": file.path(), - "position": 0, - "icons": { - "search": "file-o" - }, - "action": _.bind(function() { - files.open(file); - }, this) - }; - }); - }); -}); \ No newline at end of file diff --git a/client/core/search/tags.js b/client/core/search/tags.js deleted file mode 100644 index a26f4bb0..00000000 --- a/client/core/search/tags.js +++ /dev/null @@ -1,40 +0,0 @@ -define([ - 'hr/promise', - 'hr/utils', - 'hr/hr', - 'core/backends/rpc', - 'core/search', - 'core/files' -], function(Q, _, hr, rpc, search, files) { - var normalizeTag = function(tag) { - return { - "title": tag.name, - "label": tag.file, - "icons": { - "search": "code" - }, - "action": _.bind(function() { - files.open(tag.file, { - // Open content "/^ foo $/"" _> "foo" in editor - pattern: tag.pattern.slice(2, -2) - }); - }, this) - }; - }; - - // Search for files - search.handler({ - 'id': "tags", - 'title': "Tags" - }, function(query) { - if (!query) return []; - - return rpc.execute("codecomplete/get", { - 'query': query - }).then(function(data) { - return _.map(data.results, normalizeTag); - }, function() { - return Q([]); - }); - }); -}); \ No newline at end of file diff --git a/client/core/session.js b/client/core/session.js deleted file mode 100644 index 3d843db9..00000000 --- a/client/core/session.js +++ /dev/null @@ -1,66 +0,0 @@ -define([ - "hr/utils", - "hr/hr", - "core/user", - "core/box", - "core/addons", - "core/collaborators", - "core/backends/rpc", - "core/localfs" -], function(_, hr, user, box, addons, collaborators, rpc, localfs) { - // Extend template context - hr.Template.extendContext({ - 'session': { - 'user': user, - 'box': box, - 'addons': addons, - 'collaborators': collaborators - } - }); - - // Redefine check for connection status - hr.Offline.check = function() { - return rpc.execute("box/ping").then(function(data) { - hr.Offline.setState(data.ping == true); - if (!hr.Offline.isConnected()) { - return Q.reject(new Error("No connected")); - } - }, function() { - hr.Offline.setState(false); - }) - }; - - return { - // Prepare session - prepare: function() { - return hr.Offline.check().then(function() { - return box.status(); - }).then(function() { - return localfs.init(box.get("name")); - }); - }, - - // Start session - start: function(email, token) { - var that = this; - - return box.auth({ - 'email': email, - 'token': token - }, user).then(function() { - // Get installed addons - return addons.getInstalled(); - }).then(function() { - // Get collaborators - return collaborators.getCollaborators(); - }); - }, - - // Logout - exit: function() { - hr.Cookies.remove("email"); - hr.Cookies.remove("token"); - location.reload(); - } - }; -}); \ No newline at end of file diff --git a/client/core/settings.js b/client/core/settings.js deleted file mode 100644 index e92c973e..00000000 --- a/client/core/settings.js +++ /dev/null @@ -1,78 +0,0 @@ -define([ - 'hr/hr', - 'models/command', - 'core/user', - 'views/settings/base' -], function (hr, Command, user, SettingsPageView) { - - /* - * This module define a unify way - * to manage user settings. - */ - - var logging = hr.Logger.addNamespace("settings"); - - var settings = { - sections: {}, - - /* - * Define a new settings tab - * Tab: View for the tab - */ - add: function(Tab, options) { - var section, namespace; - if (!_.isFunction(Tab)) { - options = Tab; - Tab = SettingsPageView; - } - - var tab = new Tab(options); - - var namespace = tab.namespace || "main"; - - logging.log("add settings tab", namespace); - settings.sections[namespace] = tab; - - var defaults = options.defaults || {}; - var currentValues = user.get("settings."+namespace, {}); - currentValues = _.defaults(currentValues, defaults); - user.set("settings."+namespace, currentValues); - - return tab; - }, - - /* - * For all tabs - */ - each: function(callback, context) { - _.each(settings.sections, callback, context); - }, - - /* - * Save settings - */ - save: function() { - var data = {}; - this.each(function(tab) { - data[tab.namespace] = tab.submit(); - }); - return user.saveSettings(_.extend({}, user.get("settings"), data)); - }, - - /* - * Open a settings page - */ - open: function(page) { - Command.run("settings", page); - }, - - /* - * User settings - */ - user: function(namespace) { - return user.settings(namespace); - } - }; - - return settings; -}); \ No newline at end of file diff --git a/client/core/tabs.js b/client/core/tabs.js deleted file mode 100644 index 561e1ec6..00000000 --- a/client/core/tabs.js +++ /dev/null @@ -1,6 +0,0 @@ -define([ - 'views/tabs/manager' -], function (TabsView) { - var tabs = new TabsView(); - return tabs; -}); \ No newline at end of file diff --git a/client/core/themes.js b/client/core/themes.js deleted file mode 100644 index ff406837..00000000 --- a/client/core/themes.js +++ /dev/null @@ -1,161 +0,0 @@ -define([ - 'hr/hr', - 'hr/dom', - 'hr/utils', - 'utils/css', - 'core/settings', - 'core/user' -], function (hr, $, _, css, settings, user) { - var logger = hr.Logger.addNamespace("themes"); - - - // User settings - var userSettings = user.settings("themes"); - - // Map of themes - var currentTheme = null; - var themes = {}; - - // CSS dom - var $css = $("" ).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 550/646] 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 574/646] 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 575/646] 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 576/646] 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 577/646] 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 578/646] 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 579/646] 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 580/646] 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 581/646] 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 582/646] 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 583/646] 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 584/646] 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 585/646] 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 586/646] 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 587/646] 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 588/646] 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 589/646] 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 590/646] 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 591/646] 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 592/646] 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 593/646] 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 594/646] 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 595/646] 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 596/646] 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 597/646] 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 598/646] 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 599/646] 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 600/646] 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 601/646] 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 602/646] 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 603/646] 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 604/646] 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 605/646] 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 606/646] 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 607/646] 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 608/646] 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 609/646] 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 610/646] 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 611/646] 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 612/646] 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 613/646] 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 614/646] 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 615/646] 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 616/646] 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 617/646] 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 618/646] 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 619/646] 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 620/646] 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 621/646] 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 622/646] 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 623/646] 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 624/646] 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 625/646] 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 626/646] 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 627/646] 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 628/646] 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 629/646] 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 630/646] 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 631/646] 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 632/646] 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 633/646] 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 634/646] 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 635/646] 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 636/646] 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 637/646] 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 638/646] 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 639/646] 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 640/646] 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 641/646] 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 642/646] 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 643/646] 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 644/646] 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 645/646] 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 646/646] 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,