summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarren Smith <garren.smith@gmail.com>2014-01-08 15:26:12 +0200
committerGarren Smith <garren.smith@gmail.com>2014-01-29 16:53:46 +0200
commit96be583d8566536b0356296d4f9b01164e31f4fc (patch)
treef67e98718799a55bfe24fb2e496b18482b286ef4
parent5c9f9a9f056ca71c516ddd7eb9ab32f8eb01dc12 (diff)
downloadcouchdb-96be583d8566536b0356296d4f9b01164e31f4fc.tar.gz
Fauxton: Split up api.js
Split up api to core/*. To seperate out the Framework from the UI. Move tests to fit with this refactor. Change addons/Fauxton with work with api extraction.
-rw-r--r--src/Makefile.am22
-rw-r--r--src/fauxton/Gruntfile.js4
-rw-r--r--src/fauxton/app/addons/activetasks/tests/viewsSpec.js3
-rw-r--r--src/fauxton/app/addons/auth/base.js1
-rw-r--r--src/fauxton/app/addons/auth/resources.js7
-rw-r--r--src/fauxton/app/addons/documents/views.js8
-rw-r--r--src/fauxton/app/addons/fauxton/base.js109
-rw-r--r--src/fauxton/app/addons/fauxton/components.js100
-rw-r--r--src/fauxton/app/addons/fauxton/resizeColumns.js (renamed from src/fauxton/app/resizeColumns.js)10
-rw-r--r--src/fauxton/app/addons/fauxton/tests/baseSpec.js79
-rw-r--r--src/fauxton/app/addons/fauxton/tests/navbarSpec.js (renamed from src/fauxton/test/core/navbarSpec.js)0
-rw-r--r--src/fauxton/app/addons/fauxton/tests/paginateSpec.js (renamed from src/fauxton/test/core/paginateSpec.js)10
-rw-r--r--src/fauxton/app/api.js593
-rw-r--r--src/fauxton/app/app.js85
-rw-r--r--src/fauxton/app/config.js5
-rw-r--r--src/fauxton/app/core/api.js53
-rw-r--r--src/fauxton/app/core/auth.js67
-rw-r--r--src/fauxton/app/core/base.js137
-rw-r--r--src/fauxton/app/core/couchdbSession.js54
-rw-r--r--src/fauxton/app/core/layout.js (renamed from src/fauxton/app/addons/fauxton/layout.js)69
-rw-r--r--src/fauxton/app/core/routeObject.js296
-rw-r--r--src/fauxton/app/core/router.js (renamed from src/fauxton/app/router.js)63
-rw-r--r--src/fauxton/app/core/tests/layoutSpec.js (renamed from src/fauxton/test/core/layoutSpec.js)10
-rw-r--r--src/fauxton/app/core/tests/routeObjectSpec.js (renamed from src/fauxton/test/core/routeObjectSpec.js)11
-rw-r--r--src/fauxton/app/core/utils.js94
-rw-r--r--src/fauxton/app/main.js13
-rw-r--r--src/fauxton/app/utils.js66
-rw-r--r--src/fauxton/settings.json.default1
-rw-r--r--src/fauxton/tasks/couchserver.js2
-rw-r--r--src/fauxton/test/mocha/testUtils.js3
-rw-r--r--src/fauxton/test/test.config.underscore1
31 files changed, 1101 insertions, 875 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ba160b9cd..0f5c49153 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -111,14 +111,22 @@ FAUXTON_FILES = \
fauxton/app/addons/verifyinstall/routes.js \
fauxton/app/addons/verifyinstall/templates/main.html \
fauxton/app/addons/verifyinstall/assets/less/verifyinstall.less \
- fauxton/app/api.js \
+ fauxton/app/core/api.js \
+ fauxton/app/core/auth.js \
+ fauxton/app/core/base.js \
+ fauxton/app/core/couchdbSession.js \
+ fauxton/app/core/layout.js \
+ fauxton/app/core/routeObject.js \
+ fauxton/app/core/router.js \
+ fauxton/app/core/utils.js \
+ fauxton/app/core/tests/layoutSpec.js \
+ fauxton/app/core/tests/routeObjectSpec.js \
fauxton/app/app.js \
fauxton/app/config.js \
fauxton/app/helpers.js \
fauxton/app/initialize.js.underscore \
fauxton/app/load_addons.js.underscore \
fauxton/app/main.js \
- fauxton/app/utils.js \
fauxton/app/addons/databases/base.js \
fauxton/app/addons/databases/resources.js \
fauxton/app/addons/databases/routes.js \
@@ -131,11 +139,13 @@ FAUXTON_FILES = \
fauxton/app/addons/fauxton/base.js \
fauxton/app/addons/fauxton/components.js \
fauxton/app/addons/fauxton/layout.js \
+ fauxton/app/addons/fauxton/resizeColumns.js \
+ fauxton/app/addons/fauxton/tests/baseSpec.js \
+ fauxton/app/addons/fauxton/tests/navbarSpec.js \
+ fauxton/app/addons/fauxton/tests/paginateSpec.js \
fauxton/app/addons/pouchdb/base.js \
fauxton/app/addons/pouchdb/pouch.collate.js \
fauxton/app/addons/pouchdb/pouchdb.mapreduce.js \
- fauxton/app/resizeColumns.js \
- fauxton/app/router.js \
fauxton/app/addons/databases/templates/item.html \
fauxton/app/addons/databases/templates/list.html \
fauxton/app/addons/databases/templates/newdatabase.html \
@@ -309,10 +319,6 @@ FAUXTON_FILES = \
fauxton/tasks/couchserver.js \
fauxton/tasks/fauxton.js \
fauxton/tasks/helper.js \
- fauxton/test/core/layoutSpec.js \
- fauxton/test/core/navbarSpec.js \
- fauxton/test/core/paginateSpec.js \
- fauxton/test/core/routeObjectSpec.js \
fauxton/test/mocha/chai.js \
fauxton/test/mocha/mocha.css \
fauxton/test/mocha/mocha.js \
diff --git a/src/fauxton/Gruntfile.js b/src/fauxton/Gruntfile.js
index 32065b67d..2c5a24906 100644
--- a/src/fauxton/Gruntfile.js
+++ b/src/fauxton/Gruntfile.js
@@ -357,7 +357,7 @@ module.exports = function(grunt) {
mochaSetup: {
default: {
- files: { src: helper.watchFiles(['[Ss]pec.js'], ['./test/core/**/*[Ss]pec.js', './app/**/*[Ss]pec.js'])},
+ files: { src: helper.watchFiles(['[Ss]pec.js'], ['./app/**/*[Ss]pec.js'])},
template: 'test/test.config.underscore',
config: './app/config.js'
}
@@ -424,7 +424,7 @@ module.exports = function(grunt) {
grunt.registerTask('lint', ['clean', 'jshint']);
grunt.registerTask('test', ['lint', 'dependencies', 'test_inline']);
// lighter weight test task for use inside dev/watch
- grunt.registerTask('test_inline', ['mochaSetup','jst', 'concat:test_config_js', 'mocha_phantomjs']);
+ grunt.registerTask('test_inline', ['mochaSetup','jst', 'concat:test_config_js','mocha_phantomjs']);
// Fetch dependencies (from git or local dir), lint them and make load_addons
grunt.registerTask('dependencies', ['get_deps', 'gen_load_addons:default']);
// build templates, js and css
diff --git a/src/fauxton/app/addons/activetasks/tests/viewsSpec.js b/src/fauxton/app/addons/activetasks/tests/viewsSpec.js
index 395b60a79..13c904974 100644
--- a/src/fauxton/app/addons/activetasks/tests/viewsSpec.js
+++ b/src/fauxton/app/addons/activetasks/tests/viewsSpec.js
@@ -132,8 +132,5 @@ define([
});
});
-
-
-
});
});
diff --git a/src/fauxton/app/addons/auth/base.js b/src/fauxton/app/addons/auth/base.js
index 9f9a33214..7f69a7f66 100644
--- a/src/fauxton/app/addons/auth/base.js
+++ b/src/fauxton/app/addons/auth/base.js
@@ -20,6 +20,7 @@ function(app, FauxtonAPI, Auth) {
Auth.session = new Auth.Session();
FauxtonAPI.setSession(Auth.session);
+ app.session = Auth.session;
Auth.initialize = function() {
Auth.navLink = new Auth.NavLink({model: Auth.session});
diff --git a/src/fauxton/app/addons/auth/resources.js b/src/fauxton/app/addons/auth/resources.js
index 970d55bb9..321d3026d 100644
--- a/src/fauxton/app/addons/auth/resources.js
+++ b/src/fauxton/app/addons/auth/resources.js
@@ -12,10 +12,11 @@
define([
"app",
- "api"
+ "api",
+ "core/CouchdbSession"
],
-function (app, FauxtonAPI) {
+function (app, FauxtonAPI, CouchdbSession) {
var Auth = new FauxtonAPI.addon();
@@ -46,7 +47,7 @@ function (app, FauxtonAPI) {
}
});
- Auth.Session = FauxtonAPI.Session.extend({
+ Auth.Session = CouchdbSession.Session.extend({
url: app.host + '/_session',
initialize: function (options) {
diff --git a/src/fauxton/app/addons/documents/views.js b/src/fauxton/app/addons/documents/views.js
index 95163557a..aeb5983b1 100644
--- a/src/fauxton/app/addons/documents/views.js
+++ b/src/fauxton/app/addons/documents/views.js
@@ -21,7 +21,7 @@ define([
"addons/pouchdb/base",
// Libs
- "resizeColumns",
+ "addons/Fauxton/resizeColumns",
// Plugins
"plugins/beautify",
@@ -720,11 +720,11 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum
},
cleanup: function () {
- //if (!this.pagination) { return; }
- this.pagination.remove();
- //this.pagination = null;
this.allDocsNumber.remove();
_.each(this.rows, function (row) {row.remove();});
+
+ if (!this.pagination) { return; }
+ this.pagination.remove();
},
beforeRender: function() {
diff --git a/src/fauxton/app/addons/fauxton/base.js b/src/fauxton/app/addons/fauxton/base.js
index 1811e845f..35babb5f6 100644
--- a/src/fauxton/app/addons/fauxton/base.js
+++ b/src/fauxton/app/addons/fauxton/base.js
@@ -12,18 +12,86 @@
define([
"app",
- // Libs
- "backbone",
- "resizeColumns",
+ "api",
+ "addons/fauxton/resizeColumns"
],
-function(app, Backbone, resizeColumns) {
+function(app, FauxtonAPI, resizeColumns) {
- //resizeAnimation
- app.resizeColumns = new resizeColumns({});
- app.resizeColumns.onResizeHandler();
+ var Fauxton = FauxtonAPI.addon();
+ FauxtonAPI.addNotification = function (options) {
+ options = _.extend({
+ msg: "Notification Event Triggered!",
+ type: "info",
+ selector: "#global-notifications"
+ }, options);
- var Fauxton = {};
+ var view = new Fauxton.Notification(options);
+ return view.renderNotification();
+ };
+
+ FauxtonAPI.UUID = FauxtonAPI.Model.extend({
+ initialize: function(options) {
+ options = _.extend({count: 1}, options);
+ this.count = options.count;
+ },
+
+ url: function() {
+ return app.host + "/_uuids?count=" + this.count;
+ },
+
+ next: function() {
+ return this.get("uuids").pop();
+ }
+ });
+
+
+ Fauxton.initialize = function () {
+ app.footer = new Fauxton.Footer({el: "#footer-content"}),
+ app.navBar = new Fauxton.NavBar();
+ app.apiBar = new Fauxton.ApiBar();
+
+ FauxtonAPI.when.apply(null, app.footer.establish()).done(function() {
+ FauxtonAPI.masterLayout.setView("#primary-navbar", app.navBar, true);
+ FauxtonAPI.masterLayout.setView("#api-navbar", app.apiBar, true);
+ app.navBar.render();
+ app.apiBar.render();
+
+ app.footer.render();
+ });
+
+ FauxtonAPI.masterLayout.navBar = app.navBar;
+ FauxtonAPI.masterLayout.apiBar = app.apiBar;
+
+ FauxtonAPI.RouteObject.on('beforeFullRender', function (routeObject) {
+ $('#primary-navbar li').removeClass('active');
+
+ if (routeObject.selectedHeader) {
+ app.selectedHeader = routeObject.selectedHeader;
+ $('#primary-navbar li[data-nav-name="' + routeObject.selectedHeader + '"]').addClass('active');
+ }
+ });
+
+ FauxtonAPI.RouteObject.on('beforeEstablish', function (routeObject) {
+ FauxtonAPI.masterLayout.removeView('#breadcrumbs');
+ var crumbs = routeObject.get('crumbs');
+
+ if (crumbs.length) {
+ FauxtonAPI.masterLayout.setView('#breadcrumbs', new Fauxton.Breadcrumbs({
+ crumbs: crumbs
+ }), true).render();
+ }
+ });
+
+ FauxtonAPI.RouteObject.on('renderComplete', function (routeObject) {
+ var masterLayout = FauxtonAPI.masterLayout;
+ if (routeObject.get('apiUrl')){
+ masterLayout.apiBar.update(routeObject.get('apiUrl'));
+ } else {
+ masterLayout.apiBar.hide();
+ }
+ });
+ };
Fauxton.Breadcrumbs = Backbone.View.extend({
template: "templates/fauxton/breadcrumbs",
@@ -41,7 +109,9 @@ function(app, Backbone, resizeColumns) {
});
Fauxton.VersionInfo = Backbone.Model.extend({
- url: app.host
+ url: function () {
+ return app.host;
+ }
});
// TODO: this View should extend from FauxtonApi.View.
@@ -76,6 +146,16 @@ function(app, Backbone, resizeColumns) {
bottomNavLinks: [],
footerNavLinks: [],
+ initialize: function () {
+ _.bindAll(this);
+ //resizeAnimation
+ this.resizeColumns = new resizeColumns({});
+ this.resizeColumns.onResizeHandler();
+
+ FauxtonAPI.extensions.on('add:navbar:addHeaderLink', this.addLink);
+ FauxtonAPI.extensions.on('removeItem:navbar:addHeaderLink', this.removeLink);
+ },
+
serialize: function() {
return {
navLinks: this.navLinks,
@@ -124,7 +204,6 @@ function(app, Backbone, resizeColumns) {
},
afterRender: function(){
-
$('#primary-navbar li[data-nav-name="' + app.selectedHeader + '"]').addClass('active');
var menuOpen = true;
@@ -141,21 +220,22 @@ function(app, Backbone, resizeColumns) {
function toggleMenu(){
$selectorList.toggleClass('closeMenu');
menuOpen = $selectorList.hasClass('closeMenu');
- app.resizeColumns.onResizeHandler();
+ this.resizeColumns.onResizeHandler();
}
-
+
+ var that = this;
$('#primary-navbar').on("click", ".nav a", function(){
if (!($selectorList.hasClass('closeMenu'))){
setTimeout(
function(){
$selectorList.addClass('closeMenu');
- app.resizeColumns.onResizeHandler();
+ that.resizeColumns.onResizeHandler();
},3000);
}
});
- app.resizeColumns.initialize();
+ this.resizeColumns.initialize();
},
beforeRender: function () {
@@ -265,6 +345,5 @@ function(app, Backbone, resizeColumns) {
}
});
-
return Fauxton;
});
diff --git a/src/fauxton/app/addons/fauxton/components.js b/src/fauxton/app/addons/fauxton/components.js
index edde428eb..c26fc963f 100644
--- a/src/fauxton/app/addons/fauxton/components.js
+++ b/src/fauxton/app/addons/fauxton/components.js
@@ -25,10 +25,11 @@ define([
// Libs
"api",
"ace_configuration",
+ "spin"
],
-function(app, FauxtonAPI, ace) {
- var Components = app.module();
+function(app, FauxtonAPI, ace, spin) {
+ var Components = FauxtonAPI.addon();
Components.Pagination = FauxtonAPI.View.extend({
template: "templates/fauxton/pagination",
@@ -124,7 +125,7 @@ function(app, FauxtonAPI, ace) {
pageEnd: function () {
if (this.collection.length < this.pageLimit()) {
- return this.collection.length;
+ return (this.previousParams.length * this.pageLimit()) + this.collection.length;
}
return (this.previousParams.length * this.pageLimit()) + this.pageLimit();
@@ -235,6 +236,8 @@ function(app, FauxtonAPI, ace) {
this.theme = options.theme || 'crimson_editor';
this.couchJSHINT = options.couchJSHINT;
this.edited = false;
+
+ _.bindAll(this);
},
afterRender: function () {
@@ -295,13 +298,14 @@ function(app, FauxtonAPI, ace) {
},
removeIncorrectAnnotations: function () {
- var editor = this.editor;
+ var editor = this.editor,
+ isIgnorableError = this.isIgnorableError;
- this.editor.getSession().on("changeAnnotation", function(){
+ this.editor.getSession().on("changeAnnotation", function () {
var annotations = editor.getSession().getAnnotations();
var newAnnotations = _.reduce(annotations, function (annotations, error) {
- if (!FauxtonAPI.isIgnorableError(error.raw)) {
+ if (!isIgnorableError(error.raw)) {
annotations.push(error);
}
return annotations;
@@ -338,12 +342,94 @@ function(app, FauxtonAPI, ace) {
var errors = this.getAnnotations();
// By default CouchDB view functions don't pass lint
return _.every(errors, function(error) {
- return FauxtonAPI.isIgnorableError(error.raw);
+ return this.isIgnorableError(error.raw);
},this);
+ },
+
+ // List of JSHINT errors to ignore
+ // Gets around problem of anonymous functions not being a valid statement
+ excludedViewErrors: [
+ "Missing name in function declaration.",
+ "['{a}'] is better written in dot notation."
+ ],
+
+ isIgnorableError: function(msg) {
+ return _.contains(this.excludedViewErrors, msg);
}
});
+ //need to make this into a backbone view...
+ var routeObjectSpinner;
+ FauxtonAPI.RouteObject.on('beforeEstablish', function (routeObject) {
+ if (!routeObject.disableLoader){
+ var opts = {
+ lines: 16, // The number of lines to draw
+ length: 8, // The length of each line
+ width: 4, // The line thickness
+ radius: 12, // The radius of the inner circle
+ color: '#333', // #rbg or #rrggbb
+ speed: 1, // Rounds per second
+ trail: 10, // Afterglow percentage
+ shadow: false // Whether to render a shadow
+ };
+
+ if (!$('.spinner').length) {
+ $('<div class="spinner"></div>')
+ .appendTo('#app-container');
+ }
+
+ routeObjectSpinner = new Spinner(opts).spin();
+ $('.spinner').append(routeObjectSpinner.el);
+ }
+ });
+
+ var removeRouteObjectSpinner = function () {
+ if (routeObjectSpinner) {
+ routeObjectSpinner.stop();
+ $('.spinner').remove();
+ }
+ };
+
+ var removeViewSpinner = function () {
+ if (viewSpinner){
+ viewSpinner.stop();
+ $('.spinner').remove();
+ }
+ };
+
+ var viewSpinner;
+ FauxtonAPI.RouteObject.on('beforeRender', function (routeObject, view, selector) {
+ removeRouteObjectSpinner();
+
+ if (!view.disableLoader){
+ var opts = {
+ lines: 16, // The number of lines to draw
+ length: 8, // The length of each line
+ width: 4, // The line thickness
+ radius: 12, // The radius of the inner circle
+ color: '#333', // #rbg or #rrggbb
+ speed: 1, // Rounds per second
+ trail: 10, // Afterglow percentage
+ shadow: false // Whether to render a shadow
+ };
+
+ viewSpinner = new Spinner(opts).spin();
+ $('<div class="spinner"></div>')
+ .appendTo(selector)
+ .append(viewSpinner.el);
+ }
+ });
+
+ FauxtonAPI.RouteObject.on('afterRender', function (routeObject, view, selector) {
+ removeViewSpinner();
+ });
+
+ FauxtonAPI.RouteObject.on('viewHasRendered', function () {
+ removeViewSpinner();
+ removeRouteObjectSpinner();
+ });
+
return Components;
});
diff --git a/src/fauxton/app/resizeColumns.js b/src/fauxton/app/addons/fauxton/resizeColumns.js
index bb5076742..abfcd2f63 100644
--- a/src/fauxton/app/resizeColumns.js
+++ b/src/fauxton/app/addons/fauxton/resizeColumns.js
@@ -18,10 +18,10 @@
// "purely functional" helper system.
define([
- "utils"
+ "api"
],
-function(utils) {
+function(FauxtonAPI) {
var Resize = function(options){
this.options = options;
@@ -42,8 +42,8 @@ function(utils) {
var that = this;
//add throttler :)
this.lazyLayout = _.debounce(that.onResizeHandler, 300).bind(this);
- utils.addWindowResize(this.lazyLayout,"animation");
- utils.initWindowResize();
+ FauxtonAPI.utils.addWindowResize(this.lazyLayout,"animation");
+ FauxtonAPI.utils.initWindowResize();
this.onResizeHandler();
},
updateOptions:function(options){
@@ -52,7 +52,7 @@ function(utils) {
this.options.selectorElements = options.selectorElements || ".window-resizeable";
},
turnOff:function(){
- utils.removeWindowResize("animation");
+ FauxtonAPI.utils.removeWindowResize("animation");
},
cleanupCallback: function(){
this.callback = null;
diff --git a/src/fauxton/app/addons/fauxton/tests/baseSpec.js b/src/fauxton/app/addons/fauxton/tests/baseSpec.js
new file mode 100644
index 000000000..7df4dfc9d
--- /dev/null
+++ b/src/fauxton/app/addons/fauxton/tests/baseSpec.js
@@ -0,0 +1,79 @@
+// 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.
+define([
+ 'testUtils',
+ 'api',
+ 'addons/fauxton/base',
+ "backbone"
+], function (testUtils, FauxtonAPI, Base) {
+ var assert = testUtils.assert;
+
+
+ describe('Fauxton RouteObject:beforeEstablish', function () {
+ var TestRouteObject, testRouteObject, mockLayout, _layout;
+
+ before(function () {
+ Base.initialize();
+ _layout = FauxtonAPI.masterLayout;
+ });
+
+ beforeEach(function () {
+ TestRouteObject = FauxtonAPI.RouteObject.extend({
+ crumbs: ['mycrumbs']
+ });
+
+ testRouteObject = new TestRouteObject();
+ var apiBar = {};
+ apiBar.hide = sinon.spy();
+ var setViewSpy = sinon.stub();
+ setViewSpy.returns({
+ render: function () {}
+ });
+
+ // Need to find a better way of doing this
+ mockLayout = {
+ setTemplate: sinon.spy(),
+ clearBreadcrumbs: sinon.spy(),
+ setView: setViewSpy,
+ renderView: sinon.spy(),
+ removeView: sinon.spy(),
+ hooks: [],
+ setBreadcrumbs: sinon.spy(),
+ apiBar: apiBar,
+ layout: {
+ setView: function () {}
+ }
+ };
+
+
+ });
+
+ after(function () {
+ FauxtonAPI.masterLayout = _layout;
+ });
+
+ it('Should clear breadcrumbs', function () {
+ FauxtonAPI.masterLayout = mockLayout;
+ testRouteObject.renderWith('the-route', mockLayout, 'args');
+ assert.ok(mockLayout.removeView.calledWith('#breadcrumbs'), 'Clear Breadcrumbs called');
+ });
+
+ it('Should set breadcrumbs when breadcrumbs exist', function () {
+ FauxtonAPI.masterLayout = mockLayout;
+ testRouteObject.renderWith('the-route', mockLayout, 'args');
+ assert.ok(mockLayout.setView.calledOnce, 'Set Breadcrumbs was called');
+ });
+
+ });
+
+
+});
diff --git a/src/fauxton/test/core/navbarSpec.js b/src/fauxton/app/addons/fauxton/tests/navbarSpec.js
index 3eca6f6ce..3eca6f6ce 100644
--- a/src/fauxton/test/core/navbarSpec.js
+++ b/src/fauxton/app/addons/fauxton/tests/navbarSpec.js
diff --git a/src/fauxton/test/core/paginateSpec.js b/src/fauxton/app/addons/fauxton/tests/paginateSpec.js
index d05b322a6..535e26f4a 100644
--- a/src/fauxton/test/core/paginateSpec.js
+++ b/src/fauxton/app/addons/fauxton/tests/paginateSpec.js
@@ -10,12 +10,12 @@
// License for the specific language governing permissions and limitations under
// the License.
define([
- 'api',
+ 'app',
'addons/fauxton/components',
'addons/documents/resources',
'testUtils',
- 'app'
-], function (FauxtonAPI, Views, Models, testUtils, app) {
+ 'api'
+], function (app, Views, Models, testUtils, FauxtonAPI) {
var assert = testUtils.assert,
ViewSandbox = testUtils.ViewSandbox;
@@ -23,10 +23,6 @@ define([
describe('IndexPaginate', function () {
var viewSandbox, paginate, collection, navigateMock;
beforeEach(function () {
- app.router = {
- navigate: function () {}
- };
-
collection = new Models.IndexCollection([{
id:'myId1',
doc: 'num1'
diff --git a/src/fauxton/app/api.js b/src/fauxton/app/api.js
deleted file mode 100644
index 9ac895eb5..000000000
--- a/src/fauxton/app/api.js
+++ /dev/null
@@ -1,593 +0,0 @@
-// 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.
-
-define([
- "app",
-
- // Modules
- "addons/fauxton/base",
- "spin"
-],
-
-function(app, Fauxton) {
- var FauxtonAPI = app.module();
-
- FauxtonAPI.moduleExtensions = {
- Routes: {
- }
- };
-
- FauxtonAPI.addonExtensions = {
- initialize: function() {}
- };
-
- // List of JSHINT errors to ignore
- // Gets around problem of anonymous functions not being a valid statement
- FauxtonAPI.excludedViewErrors = [
- "Missing name in function declaration.",
- "['{a}'] is better written in dot notation."
- ];
-
- FauxtonAPI.isIgnorableError = function(msg) {
- return _.contains(FauxtonAPI.excludedViewErrors, msg);
- };
-
- FauxtonAPI.View = Backbone.View.extend({
- // This should return an array of promises, an empty array, or null
- establish: function() {
- return null;
- },
-
- loaderClassname: 'loader',
-
- disableLoader: false,
-
- forceRender: function () {
- this.hasRendered = false;
- }
- });
-
- FauxtonAPI.navigate = function(url, _opts) {
- var options = _.extend({trigger: true}, _opts );
- app.router.navigate(url,options);
- };
-
- FauxtonAPI.beforeUnload = function () {
- app.router.beforeUnload.apply(app.router, arguments);
- };
-
- FauxtonAPI.removeBeforeUnload = function () {
- app.router.removeBeforeUnload.apply(app.router, arguments);
- };
-
- FauxtonAPI.addHeaderLink = function(link) {
- app.masterLayout.navBar.addLink(link);
- };
-
- FauxtonAPI.removeHeaderLink = function(link) {
- app.masterLayout.navBar.removeLink(link);
- };
-
- FauxtonAPI.Deferred = function() {
- return $.Deferred();
- };
-
- FauxtonAPI.when = function (deferreds) {
- if (deferreds instanceof Array) {
- return $.when.apply(null, deferreds);
- }
-
- return $.when(deferreds);
- };
-
- FauxtonAPI.addRoute = function(route) {
- app.router.route(route.route, route.name, route.callback);
- };
-
- FauxtonAPI.triggerRouteEvent = function (routeEvent, args) {
- app.router.triggerRouteEvent("route:"+routeEvent, args);
- };
-
- FauxtonAPI.module = function(extra) {
- return app.module(_.extend(FauxtonAPI.moduleExtensions, extra));
- };
-
- FauxtonAPI.addon = function(extra) {
- return FauxtonAPI.module(FauxtonAPI.addonExtensions, extra);
- };
-
- FauxtonAPI.addNotification = function(options) {
- options = _.extend({
- msg: "Notification Event Triggered!",
- type: "info",
- selector: "#global-notifications"
- }, options);
- var view = new Fauxton.Notification(options);
-
- return view.renderNotification();
- };
-
- FauxtonAPI.UUID = Backbone.Model.extend({
- initialize: function(options) {
- options = _.extend({count: 1}, options);
- this.count = options.count;
- },
-
- url: function() {
- return app.host + "/_uuids?count=" + this.count;
- },
-
- next: function() {
- return this.get("uuids").pop();
- }
- });
-
- FauxtonAPI.Session = Backbone.Model.extend({
- url: app.host + '/_session',
-
- user: function () {
- var userCtx = this.get('userCtx');
-
- if (!userCtx || !userCtx.name) { return null; }
-
- return {
- name: userCtx.name,
- roles: userCtx.roles
- };
- },
-
- fetchOnce: function (opt) {
- var options = _.extend({}, opt);
-
- if (!this._deferred || this._deferred.state() === "rejected" || options.forceFetch ) {
- this._deferred = this.fetch();
- }
-
- return this._deferred;
- },
-
- fetchUser: function (opt) {
- var that = this,
- currentUser = this.user();
-
- return this.fetchOnce(opt).then(function () {
- var user = that.user();
-
- // Notify anyone listening on these events that either a user has changed
- // or current user is the same
- if (currentUser !== user) {
- that.trigger('session:userChanged');
- } else {
- that.trigger('session:userFetched');
- }
-
- // this will return the user as a value to all function that calls done on this
- // eg. session.fetchUser().done(user) { .. do something with user ..}
- return user;
- });
- }
- });
-
- FauxtonAPI.setSession = function (newSession) {
- app.session = FauxtonAPI.session = newSession;
- return FauxtonAPI.session.fetchUser();
- };
-
- FauxtonAPI.setSession(new FauxtonAPI.Session());
-
- // This is not exposed externally as it should not need to be accessed or overridden
- var Auth = function (options) {
- this._options = options;
- this.initialize.apply(this, arguments);
- };
-
- // Piggy-back on Backbone's self-propagating extend function,
- Auth.extend = Backbone.Model.extend;
-
- _.extend(Auth.prototype, Backbone.Events, {
- authDeniedCb: function() {},
-
- initialize: function() {
- var that = this;
- },
-
- authHandlerCb : function (roles) {
- var deferred = $.Deferred();
- deferred.resolve();
- return deferred;
- },
-
- registerAuth: function (authHandlerCb) {
- this.authHandlerCb = authHandlerCb;
- },
-
- registerAuthDenied: function (authDeniedCb) {
- this.authDeniedCb = authDeniedCb;
- },
-
- checkAccess: function (roles) {
- var requiredRoles = roles || [],
- that = this;
-
- return FauxtonAPI.session.fetchUser().then(function (user) {
- return FauxtonAPI.when(that.authHandlerCb(FauxtonAPI.session, requiredRoles));
- });
- }
- });
-
- FauxtonAPI.auth = new Auth();
-
- FauxtonAPI.RouteObject = function(options) {
- this._options = options;
-
- this._configure(options || {});
- this.initialize.apply(this, arguments);
- this.addEvents();
- };
-
- var broadcaster = {};
- _.extend(broadcaster, Backbone.Events);
-
- FauxtonAPI.RouteObject.on = function (eventName, fn) {
- broadcaster.on(eventName, fn);
- };
-
- /* How Route Object events work
- To listen to a specific route objects events:
-
- myRouteObject = FauxtonAPI.RouteObject.extend({
- events: {
- "beforeRender": "beforeRenderEvent"
- },
-
- beforeRenderEvent: function (view, selector) {
- console.log('Hey, beforeRenderEvent triggered',arguments);
- },
- });
-
- It is also possible to listen to events triggered from all Routeobjects.
- This is great for more general things like adding loaders, hooks.
-
- FauxtonAPI.RouteObject.on('beforeRender', function (routeObject, view, selector) {
- console.log('hey, this will trigger when any routeobject renders a view');
- });
-
- Current Events to subscribe to:
- * beforeFullRender -- before a full render is being done
- * beforeEstablish -- before the routeobject calls establish
- * AfterEstablish -- after the routeobject has run establish
- * beforeRender -- before a view is rendered
- * afterRender -- a view is finished being rendered
- * renderComplete -- all rendering is complete
-
- */
-
- // Piggy-back on Backbone's self-propagating extend function
- FauxtonAPI.RouteObject.extend = Backbone.Model.extend;
-
- var routeObjectOptions = ["views", "routes", "events", "roles", "crumbs", "layout", "apiUrl", "establish"];
-
- _.extend(FauxtonAPI.RouteObject.prototype, Backbone.Events, {
- // Should these be default vals or empty funcs?
- views: {},
- routes: {},
- events: {},
- crumbs: [],
- layout: "with_sidebar",
- apiUrl: null,
- disableLoader: false,
- loaderClassname: 'loader',
- renderedState: false,
- establish: function() {},
- route: function() {},
- roles: [],
- _promises: [],
- initialize: function() {}
- }, {
-
- renderWith: function(route, masterLayout, args) {
- var routeObject = this,
- triggerBroadcast = _.bind(this.triggerBroadcast, this);
-
- // Only want to redo the template if its a full render
- if (!this.renderedState) {
- masterLayout.setTemplate(this.layout);
- triggerBroadcast('beforeFullRender');
- $('#primary-navbar li').removeClass('active');
-
- if (this.selectedHeader) {
- app.selectedHeader = this.selectedHeader;
- $('#primary-navbar li[data-nav-name="' + this.selectedHeader + '"]').addClass('active');
- }
- }
-
- masterLayout.clearBreadcrumbs();
- var crumbs = this.get('crumbs');
-
- if (crumbs.length) {
- masterLayout.setBreadcrumbs(new Fauxton.Breadcrumbs({
- crumbs: crumbs
- }));
- }
-
- triggerBroadcast('beforeEstablish');
- var establishPromise = this.establish();
- this.addPromise(establishPromise);
- FauxtonAPI.when(establishPromise).then(function(resp) {
- triggerBroadcast('afterEstablish');
- _.each(routeObject.getViews(), function(view, selector) {
- if(view.hasRendered) {
- triggerBroadcast('viewHasRendered', view, selector);
- return;
- }
-
- triggerBroadcast('beforeRender', view, selector);
- var viewPromise = view.establish();
- routeObject.addPromise(viewPromise);
- FauxtonAPI.when(viewPromise).then(function(resp) {
- masterLayout.setView(selector, view);
-
- masterLayout.renderView(selector);
- triggerBroadcast('afterRender', view, selector);
- }, function(resp) {
- view.establishError = {
- error: true,
- reason: resp
- };
-
- if (resp && resp.responseText) {
- var errorText = JSON.parse(resp.responseText).reason;
- FauxtonAPI.addNotification({
- msg: 'An Error occurred: ' + errorText,
- type: 'error',
- clear: true
- });
- }
-
- masterLayout.renderView(selector);
- });
-
- });
- }.bind(this), function (resp) {
- if (!resp || !resp.responseText) { return; }
- FauxtonAPI.addNotification({
- msg: 'An Error occurred' + JSON.parse(resp.responseText).reason,
- type: 'error',
- clear: true
- });
- });
-
- if (this.get('apiUrl')){
- masterLayout.apiBar.update(this.get('apiUrl'));
- } else {
- masterLayout.apiBar.hide();
- }
-
- // Track that we've done a full initial render
- this.renderedState = true;
- triggerBroadcast('renderComplete');
- },
-
- triggerBroadcast: function (eventName) {
- var args = Array.prototype.slice.call(arguments);
- this.trigger.apply(this, args);
-
- args.splice(0,1, eventName, this);
- broadcaster.trigger.apply(broadcaster, args);
- },
-
- get: function(key) {
- return _.isFunction(this[key]) ? this[key]() : this[key];
- },
-
- addEvents: function(events) {
- events = events || this.get('events');
- _.each(events, function(method, event) {
- if (!_.isFunction(method) && !_.isFunction(this[method])) {
- throw new Error("Invalid method: "+method);
- }
- method = _.isFunction(method) ? method : this[method];
-
- this.on(event, method);
- }, this);
- },
-
- _configure: function(options) {
- _.each(_.intersection(_.keys(options), routeObjectOptions), function(key) {
- this[key] = options[key];
- }, this);
- },
-
- getView: function(selector) {
- return this.views[selector];
- },
-
- setView: function(selector, view) {
- this.views[selector] = view;
- return view;
- },
-
- getViews: function() {
- return this.views;
- },
-
- removeViews: function () {
- _.each(this.views, function (view, selector) {
- view.remove();
- delete this.views[selector];
- }, this);
- },
-
- addPromise: function (promise) {
- if (_.isEmpty(promise)) { return; }
-
- if (_.isArray(promise)) {
- return _.each(promise, function (p) {
- this._promises.push(p);
- }, this);
- }
-
- this._promises.push(promise);
- },
-
- cleanup: function () {
- this.removeViews();
- this.rejectPromises();
- },
-
- rejectPromises: function () {
- _.each(this._promises, function (promise) {
- if (promise.state() === "resolved") { return; }
- if (promise.abort) {
- return promise.abort("Route change");
- }
-
- promise.reject();
- }, this);
-
- this._promises = [];
- },
-
- getRouteUrls: function () {
- return _.keys(this.get('routes'));
- },
-
- hasRoute: function (route) {
- if (this.get('routes')[route]) {
- return true;
- }
- return false;
- },
-
- routeCallback: function (route, args) {
- var routes = this.get('routes'),
- routeObj = routes[route],
- routeCallback;
-
- if (typeof routeObj === 'object') {
- routeCallback = this[routeObj.route];
- } else {
- routeCallback = this[routeObj];
- }
-
- routeCallback.apply(this, args);
- },
-
- getRouteRoles: function (routeUrl) {
- var route = this.get('routes')[routeUrl];
-
- if ((typeof route === 'object') && route.roles) {
- return route.roles;
- }
-
- return this.roles;
- }
-
- });
-
- // We could look at moving the spinner code out to its own module
- var routeObjectSpinner;
- FauxtonAPI.RouteObject.on('beforeEstablish', function (routeObject) {
- if (!routeObject.disableLoader){
- var opts = {
- lines: 16, // The number of lines to draw
- length: 8, // The length of each line
- width: 4, // The line thickness
- radius: 12, // The radius of the inner circle
- color: '#333', // #rbg or #rrggbb
- speed: 1, // Rounds per second
- trail: 10, // Afterglow percentage
- shadow: false // Whether to render a shadow
- };
-
- if (!$('.spinner').length) {
- $('<div class="spinner"></div>')
- .appendTo('#app-container');
- }
-
- routeObjectSpinner = new Spinner(opts).spin();
- $('.spinner').append(routeObjectSpinner.el);
- }
- });
-
- var removeRouteObjectSpinner = function () {
- if (routeObjectSpinner) {
- routeObjectSpinner.stop();
- $('.spinner').remove();
- }
- };
-
- var removeViewSpinner = function () {
- if (viewSpinner){
- viewSpinner.stop();
- $('.spinner').remove();
- }
- };
-
- var viewSpinner;
- FauxtonAPI.RouteObject.on('beforeRender', function (routeObject, view, selector) {
- removeRouteObjectSpinner();
-
- if (!view.disableLoader){
- var opts = {
- lines: 16, // The number of lines to draw
- length: 8, // The length of each line
- width: 4, // The line thickness
- radius: 12, // The radius of the inner circle
- color: '#333', // #rbg or #rrggbb
- speed: 1, // Rounds per second
- trail: 10, // Afterglow percentage
- shadow: false // Whether to render a shadow
- };
-
- viewSpinner = new Spinner(opts).spin();
- $('<div class="spinner"></div>')
- .appendTo(selector)
- .append(viewSpinner.el);
- }
- });
-
- FauxtonAPI.RouteObject.on('afterRender', function (routeObject, view, selector) {
- removeViewSpinner();
- });
-
- FauxtonAPI.RouteObject.on('viewHasRendered', function () {
- removeViewSpinner();
- removeRouteObjectSpinner();
- });
-
- var extensions = _.extend({}, Backbone.Events);
- // Can look at a remove function later.
- FauxtonAPI.registerExtension = function (name, view) {
- if (!extensions[name]) {
- extensions[name] = [];
- }
-
- extensions.trigger('add:' + name, view);
- extensions[name].push(view);
- };
-
- FauxtonAPI.getExtensions = function (name) {
- var views = extensions[name];
-
- if (!views) {
- views = [];
- }
-
- return views;
- };
-
- FauxtonAPI.extensions = extensions;
-
- app.fauxtonAPI = FauxtonAPI;
- return app.fauxtonAPI;
-});
diff --git a/src/fauxton/app/app.js b/src/fauxton/app/app.js
index 5325f77fe..521ad6c84 100644
--- a/src/fauxton/app/app.js
+++ b/src/fauxton/app/app.js
@@ -21,77 +21,38 @@ define([
"bootstrap",
"helpers",
- "utils",
+ "core/utils",
// Modules
- "resizeColumns",
-
- // Plugins.
+ "core/api",
+ "core/couchdbsession",
+ // Plugins.
"plugins/backbone.layoutmanager",
"plugins/jquery.form"
],
-function(app, $, _, Backbone, Bootstrap, Helpers, Utils, resizeColumns) {
-
- // Make sure we have a console.log
+function(app, $, _, Backbone, Bootstrap, Helpers, Utils, FauxtonAPI, Couchdb) {
+ // Make sure we have a console.log
if (typeof console == "undefined") {
console = {
- log: function(){}
+ log: function(){},
+ trace: function(){},
+ debug: function(){}
};
}
// Provide a global location to place configuration settings and module
// creation also mix in Backbone.Events
- _.extend(app, Backbone.Events, {
+ _.extend(app, {
utils: Utils,
-
- renderView: function(baseView, selector, view, options, callback) {
- baseView.setView(selector, new view(options)).render().then(callback);
- },
-
- // Create a custom object with a nested Views object.
- module: function(additionalProps) {
- return _.extend({ Views: {} }, additionalProps);
- },
-
- // Thanks to: http://stackoverflow.com/a/2880929
- getParams: function(queryString) {
- if (queryString) {
- // I think this could be combined into one if
- if (queryString.substring(0,1) === "?") {
- queryString = queryString.substring(1);
- } else if (queryString.indexOf('?') > -1) {
- queryString = queryString.split('?')[1];
- }
- }
- var hash = window.location.hash.split('?')[1];
- queryString = queryString || hash || window.location.search.substring(1);
- var match,
- urlParams = {},
- pl = /\+/g, // Regex for replacing addition symbol with a space
- search = /([^&=]+)=?([^&]*)/g,
- decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
- query = queryString;
-
- if (queryString) {
- while ((match = search.exec(query))) {
- urlParams[decode(match[1])] = decode(match[2]);
- }
- }
-
- return urlParams;
- }
+ getParams: FauxtonAPI.utils.getParams
});
- //resizeAnimation
- app.resizeColumns = new resizeColumns({});
- app.resizeColumns.onResizeHandler();
-
// Localize or create a new JavaScript Template object.
var JST = window.JST = window.JST || {};
// Configure LayoutManager with Backbone Boilerplate defaults.
- Backbone.Layout.configure({
+ FauxtonAPI.Layout.configure({
// Allow LayoutManager to augment Backbone.View.prototype.
manage: true,
@@ -123,6 +84,28 @@ function(app, $, _, Backbone, Bootstrap, Helpers, Utils, resizeColumns) {
}
});
+ FauxtonAPI.setSession(new Couchdb.Session());
+
+ // Define your master router on the application namespace and trigger all
+ // navigation from this instance.
+ FauxtonAPI.config({
+ el: "#app-container",
+ masterLayout: new FauxtonAPI.Layout(),
+
+ addHeaderLink: function(link) {
+ FauxtonAPI.registerExtension('navbar:addHeaderLink', link);
+ },
+
+ removeHeaderLink: function(link) {
+ FauxtonAPI.removeExtensionItem('navbar:addHeaderLink', link, function (item) {
+ if (item.title === link.title) {
+ return true;
+ }
+
+ return false;
+ });
+ }
+ });
return app;
});
diff --git a/src/fauxton/app/config.js b/src/fauxton/app/config.js
index 057523bb0..98be9c6a8 100644
--- a/src/fauxton/app/config.js
+++ b/src/fauxton/app/config.js
@@ -25,7 +25,7 @@ require.config({
jquery: "../assets/js/libs/jquery",
lodash: "../assets/js/libs/lodash",
backbone: "../assets/js/libs/backbone",
- "backbone.layoutmanger": "../assets/js/plugins/backbone.layoutmanager",
+ "backbone.layoutmanager": "../assets/js/plugins/backbone.layoutmanager",
bootstrap: "../assets/js/libs/bootstrap",
spin: "../assets/js/libs/spin.min",
d3: "../assets/js/libs/d3",
@@ -37,7 +37,8 @@ require.config({
map: {
"*": {
- 'underscore': 'lodash'
+ 'underscore': 'lodash',
+ 'api':'core/api'
}
},
diff --git a/src/fauxton/app/core/api.js b/src/fauxton/app/core/api.js
new file mode 100644
index 000000000..1b21dca62
--- /dev/null
+++ b/src/fauxton/app/core/api.js
@@ -0,0 +1,53 @@
+// 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.
+
+define([
+ "core/base",
+ "core/layout",
+ "core/router",
+ "core/routeObject",
+ "core/utils"
+],
+
+function(FauxtonAPI, Layout, Router, RouteObject, utils) {
+ FauxtonAPI = _.extend(FauxtonAPI, {
+ Layout: Layout,
+ Router: Router,
+ RouteObject: RouteObject,
+ utils: utils
+ });
+
+ FauxtonAPI.navigate = function(url, _opts) {
+ var options = _.extend({trigger: true}, _opts );
+ FauxtonAPI.router.navigate(url,options);
+ };
+
+ FauxtonAPI.beforeUnload = function () {
+ FauxtonAPI.router.beforeUnload.apply(FauxtonAPI.router, arguments);
+ };
+
+ FauxtonAPI.removeBeforeUnload = function () {
+ FauxtonAPI.router.removeBeforeUnload.apply(FauxtonAPI.router, arguments);
+ };
+
+ FauxtonAPI.addRoute = function(route) {
+ FauxtonAPI.router.route(route.route, route.name, route.callback);
+ };
+
+ FauxtonAPI.triggerRouteEvent = function (routeEvent, args) {
+ FauxtonAPI.router.triggerRouteEvent("route:"+routeEvent, args);
+ };
+
+
+ return FauxtonAPI;
+});
+
diff --git a/src/fauxton/app/core/auth.js b/src/fauxton/app/core/auth.js
new file mode 100644
index 000000000..15cf5667f
--- /dev/null
+++ b/src/fauxton/app/core/auth.js
@@ -0,0 +1,67 @@
+// 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.
+
+define([
+ "core/base",
+ "backbone"
+],
+function(FauxtonAPI, Backbone) {
+
+ // This is not exposed externally as it should not need to be accessed or overridden
+ var Auth = function (options) {
+ this._options = options;
+ this.initialize.apply(this, arguments);
+ };
+
+ // Piggy-back on Backbone's self-propagating extend function,
+ Auth.extend = Backbone.Model.extend;
+
+ _.extend(Auth.prototype, Backbone.Events, {
+ authDeniedCb: function() {},
+
+ initialize: function() {
+ var that = this;
+ },
+
+ authHandlerCb : function (roles) {
+ var deferred = $.Deferred();
+ deferred.resolve();
+ return deferred;
+ },
+
+ registerAuth: function (authHandlerCb) {
+ this.authHandlerCb = authHandlerCb;
+ },
+
+ registerAuthDenied: function (authDeniedCb) {
+ this.authDeniedCb = authDeniedCb;
+ },
+
+ checkAccess: function (roles) {
+ var requiredRoles = roles || [],
+ that = this;
+
+ if (!FauxtonAPI.session) {
+ throw new Error("Fauxton.session is not configured.");
+ }
+
+ return FauxtonAPI.session.fetchUser().then(function (user) {
+ return FauxtonAPI.when(that.authHandlerCb(FauxtonAPI.session, requiredRoles));
+ });
+ }
+ });
+
+// FauxtonAPI.auth = new Auth();
+
+ return Auth;
+});
+
diff --git a/src/fauxton/app/core/base.js b/src/fauxton/app/core/base.js
new file mode 100644
index 000000000..55a8d876d
--- /dev/null
+++ b/src/fauxton/app/core/base.js
@@ -0,0 +1,137 @@
+// 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.
+
+define([
+ "backbone"
+],
+
+function(Backbone) {
+ var FauxtonAPI = {
+ //add default objects
+ router: {
+ navigate: function () {}
+ },
+
+ masterLayout: {},
+
+ addNotification: function () {},
+
+ config: function (options) {
+ return _.extend(this, options);
+ }
+ };
+
+ FauxtonAPI.Deferred = function() {
+ return $.Deferred();
+ };
+
+ FauxtonAPI.when = function (deferreds) {
+ if (deferreds instanceof Array) {
+ return $.when.apply(null, deferreds);
+ }
+
+ return $.when(deferreds);
+ };
+
+ FauxtonAPI.addonExtensions = {
+ initialize: function() {},
+ RouteObjects: {},
+ Views: {}
+ };
+
+ FauxtonAPI.addon = function(extra) {
+ return _.extend(_.clone(FauxtonAPI.addonExtensions), extra);
+ };
+
+ FauxtonAPI.View = Backbone.View.extend({
+ // This should return an array of promises, an empty array, or null
+ establish: function() {
+ return null;
+ },
+
+ loaderClassname: 'loader',
+
+ disableLoader: false,
+
+ forceRender: function () {
+ this.hasRendered = false;
+ }
+ });
+
+ FauxtonAPI.Model = Backbone.Model.extend({
+ fetchOnce: function (opt) {
+ var options = _.extend({}, opt);
+
+ if (!this._deferred || this._deferred.state() === "rejected" || options.forceFetch ) {
+ this._deferred = this.fetch();
+ }
+
+ return this._deferred;
+ }
+ });
+
+ var extensions = _.extend({}, Backbone.Events);
+ // Can look at a remove function later.
+ FauxtonAPI.registerExtension = function (name, view) {
+ if (!extensions[name]) {
+ extensions[name] = [];
+ }
+
+ extensions.trigger('add:' + name, view);
+ extensions[name].push(view);
+ };
+
+ FauxtonAPI.unRegisterExtension = function (name) {
+ var views = extensions[name];
+
+ if (!views) { return; }
+ extensions.trigger('remove:' + name, views);
+ delete extensions[name];
+ };
+
+ FauxtonAPI.getExtensions = function (name) {
+ var views = extensions[name];
+
+ if (!views) {
+ views = [];
+ }
+
+ return views;
+ };
+
+ FauxtonAPI.removeExtensionItem = function (name, view, cb) {
+ var views = extensions[name];
+ if (!views) { return; }
+
+ var _cb = arguments[arguments.length -1];
+ if (_.isObject(view) && !cb) {
+ _cb = function (item) { return _.isEqual(item, view);};
+ }
+
+ views = _.filter(views, function (item) {
+ return !_cb(item);
+ });
+
+ extensions[name] = views;
+ extensions.trigger('removeItem:' + name, view);
+ };
+
+ FauxtonAPI.extensions = extensions;
+
+ FauxtonAPI.setSession = function (newSession) {
+ FauxtonAPI.session = newSession;
+ return FauxtonAPI.session.fetchUser();
+ };
+
+ return FauxtonAPI;
+});
+
diff --git a/src/fauxton/app/core/couchdbSession.js b/src/fauxton/app/core/couchdbSession.js
new file mode 100644
index 000000000..93bfd8ac6
--- /dev/null
+++ b/src/fauxton/app/core/couchdbSession.js
@@ -0,0 +1,54 @@
+// 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.
+
+define([
+ "core/base"
+],
+function (FauxtonAPI) {
+ var CouchdbSession = {
+ Session: FauxtonAPI.Model.extend({
+ url: '/_session',
+
+ user: function () {
+ var userCtx = this.get('userCtx');
+
+ if (!userCtx || !userCtx.name) { return null; }
+
+ return {
+ name: userCtx.name,
+ roles: userCtx.roles
+ };
+ },
+
+ fetchUser: function (opt) {
+ var that = this,
+ currentUser = this.user();
+
+ return this.fetchOnce(opt).then(function () {
+ var user = that.user();
+
+ // Notify anyone listening on these events that either a user has changed
+ // or current user is the same
+ if (currentUser !== user) {
+ that.trigger('session:userChanged');
+ } else {
+ that.trigger('session:userFetched');
+ }
+
+ // this will return the user as a value to all function that calls done on this
+ // eg. session.fetchUser().done(user) { .. do something with user ..}
+ return user;
+ });
+ }
+ })
+ };
+
+ return CouchdbSession;
+});
diff --git a/src/fauxton/app/addons/fauxton/layout.js b/src/fauxton/app/core/layout.js
index 14222414b..ff339c72c 100644
--- a/src/fauxton/app/addons/fauxton/layout.js
+++ b/src/fauxton/app/core/layout.js
@@ -10,38 +10,26 @@
// License for the specific language governing permissions and limitations under
// the License.
-define(["backbone"],
-
-function(Backbone) {
+define([
+ "backbone",
+ "plugins/backbone.layoutmanager"
+], function(Backbone) {
// A wrapper of the main Backbone.layoutmanager
// Allows the main layout of the page to be changed by any plugin.
- // Exposes the different views:
- // navBar -> the top navigation bar
- // dashboardContent -> Main display view
- // breadcrumbs -> Breadcrumbs navigation section
- var Layout = function (navBar, apiBar) {
- this.navBar = navBar;
- this.apiBar = apiBar;
-
+ var Layout = function () {
this.layout = new Backbone.Layout({
template: "templates/layouts/with_sidebar",
-
- views: {
- "#primary-navbar": this.navBar,
- "#api-navbar": this.apiBar
- },
- afterRender: function(){
-
- }
});
this.layoutViews = {};
- //this.hooks = {};
-
this.el = this.layout.el;
};
+ Layout.configure = function (options) {
+ Backbone.Layout.configure(options);
+ };
+
// creatings the dashboard object same way backbone does
_.extend(Layout.prototype, {
render: function () {
@@ -61,25 +49,14 @@ function(Backbone) {
this.render();
},
- setTabs: function(view){
- // TODO: Not sure I like this - seems fragile/repetitive
- this.tabs = this.layout.setView("#tabs", view);
- this.tabs.render();
- },
-
- setBreadcrumbs: function(view) {
- this.breadcrumbs = this.layout.setView("#breadcrumbs", view);
- this.breadcrumbs.render();
- },
-
- clearBreadcrumbs: function () {
- if (!this.breadcrumbs) {return ;}
+ setView: function(selector, view, keep) {
+ this.layout.setView(selector, view, false);
- this.breadcrumbs.remove();
- },
+ if (!keep) {
+ this.layoutViews[selector] = view;
+ }
- setView: function(selector, view) {
- this.layoutViews[selector] = this.layout.setView(selector, view, false);
+ return view;
},
renderView: function(selector) {
@@ -89,6 +66,22 @@ function(Backbone) {
} else {
return view.render();
}
+ },
+
+ removeView: function (selector) {
+ var view = this.layout.getView(selector);
+
+ if (!view) {
+ return false;
+ }
+
+ view.remove();
+
+ if (this.layoutViews[selector]) {
+ delete this.layoutViews[selector];
+ }
+
+ return true;
}
});
diff --git a/src/fauxton/app/core/routeObject.js b/src/fauxton/app/core/routeObject.js
new file mode 100644
index 000000000..f3b8672e0
--- /dev/null
+++ b/src/fauxton/app/core/routeObject.js
@@ -0,0 +1,296 @@
+// 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.
+
+define([
+ "core/base",
+ "backbone"
+],
+function(FauxtonAPI, Backbone) {
+
+ var RouteObject = function(options) {
+ this._options = options;
+
+ this._configure(options || {});
+ this.initialize.apply(this, arguments);
+ this.addEvents();
+ };
+
+ var broadcaster = {};
+ _.extend(broadcaster, Backbone.Events);
+
+ RouteObject.on = function (eventName, fn) {
+ broadcaster.on(eventName, fn);
+ };
+
+ /* How Route Object events work
+ To listen to a specific route objects events:
+
+ myRouteObject = FauxtonAPI.RouteObject.extend({
+ events: {
+ "beforeRender": "beforeRenderEvent"
+ },
+
+ beforeRenderEvent: function (view, selector) {
+ console.log('Hey, beforeRenderEvent triggered',arguments);
+ },
+ });
+
+ It is also possible to listen to events triggered from all Routeobjects.
+ This is great for more general things like adding loaders, hooks.
+
+ FauxtonAPI.RouteObject.on('beforeRender', function (routeObject, view, selector) {
+ console.log('hey, this will trigger when any routeobject renders a view');
+ });
+
+ Current Events to subscribe to:
+ * beforeFullRender -- before a full render is being done
+ * beforeEstablish -- before the routeobject calls establish
+ * AfterEstablish -- after the routeobject has run establish
+ * beforeRender -- before a view is rendered
+ * afterRender -- a view is finished being rendered
+ * renderComplete -- all rendering is complete
+
+ */
+
+ // Piggy-back on Backbone's self-propagating extend function
+ RouteObject.extend = Backbone.Model.extend;
+
+ var routeObjectOptions = ["views", "routes", "events", "roles", "crumbs", "layout", "apiUrl", "establish"];
+
+ _.extend(RouteObject.prototype, Backbone.Events, {
+ // Should these be default vals or empty funcs?
+ views: {},
+ routes: {},
+ events: {},
+ crumbs: [],
+ layout: "with_sidebar",
+ apiUrl: null,
+ disableLoader: false,
+ loaderClassname: 'loader',
+ renderedState: false,
+ establish: function() {},
+ route: function() {},
+ roles: [],
+ _promises: [],
+ initialize: function() {}
+ }, {
+
+ renderWith: function(route, masterLayout, args) {
+ //set the options for this render
+ var options = {
+ masterLayout: masterLayout,
+ route: route,
+ args: args
+ };
+
+ this.setTemplateOnFullRender(masterLayout);
+
+ this.triggerBroadcast('beforeEstablish');
+
+ var renderAllViews = _.bind(this.renderAllViews, this, options),
+ establishError = _.bind(this.establishError, this),
+ renderComplete = _.bind(this.renderComplete, this),
+ promise = this.establish();
+
+ this.callEstablish(promise)
+ .then(renderAllViews, establishError)
+ .then(renderComplete);
+ },
+
+ setTemplateOnFullRender: function(masterLayout){
+ // Only want to redo the template if its a full render
+ if (!this.renderedState) {
+ masterLayout.setTemplate(this.layout);
+ this.triggerBroadcast('beforeFullRender');
+ }
+ },
+
+ callEstablish: function(establishPromise) {
+ this.addPromise(establishPromise);
+ return FauxtonAPI.when(establishPromise);
+ },
+
+ renderAllViews: function(options, resp){
+ var routeObject = this,
+ renderView = _.bind(this.renderView, this, routeObject, options);
+
+ this.triggerBroadcast('afterEstablish');
+
+ var promises = _.map(routeObject.getViews(), renderView, this);
+ return FauxtonAPI.when(promises);
+ },
+
+ renderView: function(routeObject, options, view, selector) {
+ var viewInfo = {
+ view: view,
+ selector: selector,
+ masterLayout: options.masterLayout
+ };
+
+ var renderViewOnLayout = _.bind(this.renderViewOnLayout, this, viewInfo);
+
+ if(view.hasRendered) {
+ this.triggerBroadcast('viewHasRendered', view, selector);
+ return;
+ }
+
+ this.triggerBroadcast('beforeRender', view, selector);
+
+ return this.callEstablish(view.establish()).then(renderViewOnLayout, this.establishError);
+ },
+
+ renderViewOnLayout: function(viewInfo, resp, xhr){
+ var masterLayout = viewInfo.masterLayout;
+
+ masterLayout.setView(viewInfo.selector, viewInfo.view);
+ masterLayout.renderView(viewInfo.selector);
+
+ this.triggerBroadcast('afterRender', viewInfo.view, viewInfo.selector);
+ },
+
+ establishError: function(resp){
+ if (!resp || !resp.responseText) { return; }
+ FauxtonAPI.addNotification({
+ msg: 'An Error occurred' + JSON.parse(resp.responseText).reason,
+ type: 'error',
+ clear: true
+ });
+ },
+
+ renderComplete: function () {
+ // Track that we've done a full initial render
+ this.setRenderedState(true);
+ this.triggerBroadcast('renderComplete');
+ },
+
+ setRenderedState: function(bool){
+ this.renderedState = bool;
+ },
+
+ triggerBroadcast: function (eventName) {
+ var args = Array.prototype.slice.call(arguments);
+ this.trigger.apply(this, args);
+
+ args.splice(0,1, eventName, this);
+ broadcaster.trigger.apply(broadcaster, args);
+ },
+
+ get: function(key) {
+ return _.isFunction(this[key]) ? this[key]() : this[key];
+ },
+
+ addEvents: function(events) {
+ events = events || this.get('events');
+ _.each(events, function(method, event) {
+ if (!_.isFunction(method) && !_.isFunction(this[method])) {
+ throw new Error("Invalid method: "+method);
+ }
+ method = _.isFunction(method) ? method : this[method];
+
+ this.on(event, method);
+ }, this);
+ },
+
+ _configure: function(options) {
+ _.each(_.intersection(_.keys(options), routeObjectOptions), function(key) {
+ this[key] = options[key];
+ }, this);
+ },
+
+ getView: function(selector) {
+ return this.views[selector];
+ },
+
+ setView: function(selector, view) {
+ this.views[selector] = view;
+ return view;
+ },
+
+ getViews: function() {
+ return this.views;
+ },
+
+ removeViews: function () {
+ _.each(this.views, function (view, selector) {
+ view.remove();
+ delete this.views[selector];
+ }, this);
+ },
+
+ addPromise: function (promise) {
+ if (_.isEmpty(promise)) { return; }
+
+ if (!_.isArray(promise)) {
+ return this._promises.push(promise);
+ }
+
+ _.each(promise, function (p) {
+ this._promises.push(p);
+ }, this);
+ },
+
+ cleanup: function () {
+ this.removeViews();
+ this.rejectPromises();
+ },
+
+ rejectPromises: function () {
+ _.each(this._promises, function (promise) {
+ if (promise.state() === "resolved") { return; }
+ if (promise.abort) {
+ return promise.abort("Route change");
+ }
+
+ promise.reject();
+ }, this);
+
+ this._promises = [];
+ },
+
+ getRouteUrls: function () {
+ return _.keys(this.get('routes'));
+ },
+
+ hasRoute: function (route) {
+ if (this.get('routes')[route]) {
+ return true;
+ }
+ return false;
+ },
+
+ routeCallback: function (route, args) {
+ var routes = this.get('routes'),
+ routeObj = routes[route],
+ routeCallback;
+
+ if (typeof routeObj === 'object') {
+ routeCallback = this[routeObj.route];
+ } else {
+ routeCallback = this[routeObj];
+ }
+
+ routeCallback.apply(this, args);
+ },
+
+ getRouteRoles: function (routeUrl) {
+ var route = this.get('routes')[routeUrl];
+
+ if ((typeof route === 'object') && route.roles) {
+ return route.roles;
+ }
+
+ return this.roles;
+ }
+
+ });
+ return RouteObject;
+});
diff --git a/src/fauxton/app/router.js b/src/fauxton/app/core/router.js
index 89c60cf33..cc1ca4f64 100644
--- a/src/fauxton/app/router.js
+++ b/src/fauxton/app/core/router.js
@@ -11,32 +11,16 @@
// the License.
define([
- // Load require for use in nested requiring
- // as per the note in: http://requirejs.org/docs/api.html#multiversion
- "require",
-
- // Application.
- "app",
-
- // Initialize application
- "initialize",
-
- // Load Fauxton API
- "api",
-
- // Modules
- "addons/fauxton/base",
- // Layout
- "addons/fauxton/layout",
-
- "load_addons"
+ "core/base",
+ "core/auth",
+ "backbone"
],
-function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, LoadAddons) {
+function(FauxtonAPI, Auth, Backbone) {
var beforeUnloads = {};
- var Router = app.router = Backbone.Router.extend({
+ var Router = Backbone.Router.extend({
routes: {},
beforeUnload: function (name, fn) {
@@ -64,14 +48,14 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, LoadAddons) {
addModuleRouteObject: function(RouteObject) {
var that = this;
- var masterLayout = this.masterLayout,
+ var masterLayout = FauxtonAPI.masterLayout,
routeUrls = RouteObject.prototype.getRouteUrls();
_.each(routeUrls, function(route) {
this.route(route, route.toString(), function() {
var args = Array.prototype.slice.call(arguments),
roles = RouteObject.prototype.getRouteRoles(route),
- authPromise = app.auth.checkAccess(roles);
+ authPromise = FauxtonAPI.auth.checkAccess(roles);
authPromise.then(function () {
if (!that.activeRouteObject || !that.activeRouteObject.hasRoute(route)) {
@@ -92,8 +76,8 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, LoadAddons) {
}, this);
},
- setModuleRoutes: function() {
- _.each(LoadAddons.addons, function(module) {
+ setModuleRoutes: function(addons) {
+ _.each(addons, function(module) {
if (module){
module.initialize();
// This is pure routes the addon provides
@@ -104,39 +88,26 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, LoadAddons) {
}, this);
},
- initialize: function() {
- //TODO: It would be nice to handle this with a router
- this.navBar = app.navBar = new Fauxton.NavBar();
- this.apiBar = app.apiBar = new Fauxton.ApiBar();
- this.auth = app.auth = FauxtonAPI.auth;
- app.session = FauxtonAPI.session;
-
- app.masterLayout = this.masterLayout = new Layout(this.navBar, this.apiBar);
- app.footer = new Fauxton.Footer({el: "#footer-content"});
-
+ initialize: function(addons) {
+ this.addons = addons;
+ this.auth = FauxtonAPI.auth = new Auth();
// NOTE: This must be below creation of the layout
// FauxtonAPI header links and others depend on existence of the layout
- //this.setAddonHooks();
- this.setModuleRoutes();
-
- $("#app-container").html(this.masterLayout.el);
- this.masterLayout.render();
+ this.setModuleRoutes(addons);
- // TODO: move this to a proper Fauxton.View
- $.when.apply(null, app.footer.establish()).done(function() {
- app.footer.render();
- });
+ $(FauxtonAPI.el).html(FauxtonAPI.masterLayout.el);
+ FauxtonAPI.masterLayout.render();
},
triggerRouteEvent: function(event, args) {
if (this.activeRouteObject) {
var eventArgs = [event].concat(args);
this.activeRouteObject.trigger.apply(this.activeRouteObject, eventArgs );
- this.activeRouteObject.renderWith(eventArgs, this.masterLayout, args);
+ this.activeRouteObject.renderWith(eventArgs, FauxtonAPI.masterLayout, args);
}
}
});
return Router;
-
});
+
diff --git a/src/fauxton/test/core/layoutSpec.js b/src/fauxton/app/core/tests/layoutSpec.js
index 4167100d7..b58966bc8 100644
--- a/src/fauxton/test/core/layoutSpec.js
+++ b/src/fauxton/app/core/tests/layoutSpec.js
@@ -10,18 +10,16 @@
// License for the specific language governing permissions and limitations under
// the License.
define([
- 'addons/fauxton/layout',
- 'testUtils'
-], function (Layout, testUtils) {
+ 'api',
+ 'testUtils'
+], function (FauxtonAPI, testUtils) {
var assert = testUtils.assert;
describe("Faxuton Layout", function () {
var layout;
beforeEach(function () {
- var navBar = new Backbone.View();
- var apiBar = new Backbone.View();
- layout = new Layout(navBar, apiBar);
+ layout = new FauxtonAPI.Layout();
});
describe('#setTemplate', function () {
diff --git a/src/fauxton/test/core/routeObjectSpec.js b/src/fauxton/app/core/tests/routeObjectSpec.js
index 987d5b7e4..2fca94d45 100644
--- a/src/fauxton/test/core/routeObjectSpec.js
+++ b/src/fauxton/app/core/tests/routeObjectSpec.js
@@ -57,16 +57,7 @@ define([
assert.ok(mockLayout.setTemplate.calledOnce, 'SetTemplate not meant to be called');
});
- it('Should clear breadcrumbs', function () {
- testRouteObject.renderWith('the-route', mockLayout, 'args');
- assert.ok(mockLayout.clearBreadcrumbs.calledOnce, 'Clear Breadcrumbs called');
- });
-
- it('Should set breadcrumbs when breadcrumbs exist', function () {
- testRouteObject.renderWith('the-route', mockLayout, 'args');
- assert.ok(mockLayout.setBreadcrumbs.calledOnce, 'Set Breadcrumbs was called');
- });
-
+
it("Should call establish of routeObject", function () {
var establishSpy = sinon.spy(testRouteObject,"establish");
diff --git a/src/fauxton/app/core/utils.js b/src/fauxton/app/core/utils.js
new file mode 100644
index 000000000..44945e846
--- /dev/null
+++ b/src/fauxton/app/core/utils.js
@@ -0,0 +1,94 @@
+// 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.
+
+
+// This file creates a set of helper functions that will be loaded for all html
+// templates. These functions should be self contained and not rely on any
+// external dependencies as they are loaded prior to the application. We may
+// want to change this later, but for now this should be thought of as a
+// "purely functional" helper system.
+
+
+define([
+ "jquery",
+ "lodash"
+],
+
+function($, _ ) {
+
+ var onWindowResize = {};
+
+ var utils = {
+ // Thanks to: http://stackoverflow.com/a/2880929
+ getParams: function(queryString) {
+ if (queryString) {
+ // I think this could be combined into one if
+ if (queryString.substring(0,1) === "?") {
+ queryString = queryString.substring(1);
+ } else if (queryString.indexOf('?') > -1) {
+ queryString = queryString.split('?')[1];
+ }
+ }
+ var hash = window.location.hash.split('?')[1];
+ queryString = queryString || hash || window.location.search.substring(1);
+ var match,
+ urlParams = {},
+ pl = /\+/g, // Regex for replacing addition symbol with a space
+ search = /([^&=]+)=?([^&]*)/g,
+ decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
+ query = queryString;
+
+ if (queryString) {
+ while ((match = search.exec(query))) {
+ urlParams[decode(match[1])] = decode(match[2]);
+ }
+ }
+
+ return urlParams;
+ },
+
+ addWindowResize: function(fun, key){
+ onWindowResize[key]=fun;
+ // You shouldn't need to call it here. Just define it at startup and each time it will loop
+ // through all the functions in the hash.
+ //app.initWindowResize();
+ },
+
+ removeWindowResize: function(key){
+ delete onWindowResize[key];
+ utils.initWindowResize();
+ },
+
+ initWindowResize: function(){
+ //when calling this it should be overriding what was called previously
+ window.onresize = function(e) {
+ // could do this instead of the above for loop
+ _.each(onWindowResize, function (fn) {
+ fn();
+ });
+ };
+ },
+
+ removeSpecialCharacters: function(name){
+ return name.replace(/[^\w\s]/gi,"");
+ },
+
+ safeURLName: function(name){
+ var testName = name || "";
+ var checkforBad = testName.match(/[\$\-/_,+-]/g);
+ return (checkforBad !== null)?encodeURIComponent(name):name;
+ }
+ };
+
+ return utils;
+});
+
diff --git a/src/fauxton/app/main.js b/src/fauxton/app/main.js
index 6fe999102..9df15c5d9 100644
--- a/src/fauxton/app/main.js
+++ b/src/fauxton/app/main.js
@@ -13,19 +13,18 @@
require([
// Application.
"app",
-
- // Main Router.
- "router"
+ "api",
+ "load_addons"
],
-function(app, Router) {
+function(app, FauxtonAPI, LoadAddons) {
- // Define your master router on the application namespace and trigger all
- // navigation from this instance.
- app.router = new Router();
+ app.addons = LoadAddons.addons;
+ FauxtonAPI.router = app.router = new FauxtonAPI.Router(app.addons);
// Trigger the initial route and enable HTML5 History API support, set the
// root folder to '/' by default. Change in app.js.
Backbone.history.start({ pushState: false, root: app.root });
+
// All navigation that is relative should be passed through the navigate
// method, to be processed by the router. If the link has a `data-bypass`
// attribute, bypass the delegation completely.
diff --git a/src/fauxton/app/utils.js b/src/fauxton/app/utils.js
deleted file mode 100644
index ded7dace4..000000000
--- a/src/fauxton/app/utils.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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.
-
-
-// This file creates a set of helper functions that will be loaded for all html
-// templates. These functions should be self contained and not rely on any
-// external dependencies as they are loaded prior to the application. We may
-// want to change this later, but for now this should be thought of as a
-// "purely functional" helper system.
-
-
-define([
- "jquery",
- "lodash"
-],
-
-function($, _ ) {
-
- var utils = {};
-
- var onWindowResize = {};
-
- utils.addWindowResize = function(fun, key){
- onWindowResize[key]=fun;
- // You shouldn't need to call it here. Just define it at startup and each time it will loop
- // through all the functions in the hash.
- //app.initWindowResize();
- };
-
- utils.removeWindowResize = function(key){
- delete onWindowResize[key];
- utils.initWindowResize();
- };
-
- utils.initWindowResize = function(){
- //when calling this it should be overriding what was called previously
- window.onresize = function(e) {
- // could do this instead of the above for loop
- _.each(onWindowResize, function (fn) {
- fn();
- });
- };
- };
-
- utils.removeSpecialCharacters = function(name){
- return name.replace(/[^\w\s]/gi,"");
- };
-
- utils.safeURLName = function(name){
- var testName = name || "";
- var checkforBad = testName.match(/[\$\-/_,+-]/g);
- return (checkforBad !== null)?encodeURIComponent(name):name;
- };
-
- return utils;
-});
-
diff --git a/src/fauxton/settings.json.default b/src/fauxton/settings.json.default
index cb09eb2e1..e817b79a3 100644
--- a/src/fauxton/settings.json.default
+++ b/src/fauxton/settings.json.default
@@ -1,5 +1,6 @@
{
"deps": [
+ { "name": "fauxton" },
{ "name": "databases" },
{ "name": "documents" },
{ "name": "pouchdb" },
diff --git a/src/fauxton/tasks/couchserver.js b/src/fauxton/tasks/couchserver.js
index 5ccbfe11e..21c2fcb23 100644
--- a/src/fauxton/tasks/couchserver.js
+++ b/src/fauxton/tasks/couchserver.js
@@ -61,7 +61,7 @@ module.exports = function (grunt) {
// server js from app directory
filePath = path.join(app_dir, url.replace('/_utils/fauxton/',''));
} else if (!!url.match(/testrunner/)) {
- var testSetup = grunt.util.spawn({cmd: 'grunt', grunt: true, args: ['mochaSetup']}, function (error, result, code) {/* log.writeln(String(result));*/ });
+ var testSetup = grunt.util.spawn({cmd: 'grunt', grunt: true, args: ['test_inline']}, function (error, result, code) {/* log.writeln(String(result));*/ });
testSetup.stdout.pipe(process.stdout);
testSetup.stderr.pipe(process.stderr);
filePath = path.join('./test/runner.html');
diff --git a/src/fauxton/test/mocha/testUtils.js b/src/fauxton/test/mocha/testUtils.js
index f9643e8df..2c418f915 100644
--- a/src/fauxton/test/mocha/testUtils.js
+++ b/src/fauxton/test/mocha/testUtils.js
@@ -11,11 +11,12 @@
// the License.
define([
+ "api",
"chai",
"sinon-chai",
"underscore"
],
-function(chai, sinonChai) {
+function(FauxtonAPI,chai, sinonChai) {
chai.use(sinonChai);
var ViewSandbox = function () {
diff --git a/src/fauxton/test/test.config.underscore b/src/fauxton/test/test.config.underscore
index dda16f275..5cebe7837 100644
--- a/src/fauxton/test/test.config.underscore
+++ b/src/fauxton/test/test.config.underscore
@@ -5,6 +5,7 @@ require.config(
);
require([
+ "app",
<% _.each(testFiles, function (test) {%>
'../<%= test %>',
<% }) %>