summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarren Smith <garren.smith@gmail.com>2014-01-20 15:11:13 +0200
committerGarren Smith <garren.smith@gmail.com>2014-01-29 15:48:54 +0200
commit4c7c1ef5f137d71af12707bf0de3e85c69c145dc (patch)
tree3d6c64cc40925a78204ce2d5ff2c7f445910ffa6
parent81f93d303129248121623a7bde52a3a320210382 (diff)
downloadcouchdb-4c7c1ef5f137d71af12707bf0de3e85c69c145dc.tar.gz
Fauxton: Split up api.js
Split up api to core/*. Move tests to fit with this refactor. Change addons/Fauxton with work with api extraction.
-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/resources.js7
-rw-r--r--src/fauxton/app/addons/fauxton/base.js9
-rw-r--r--src/fauxton/app/addons/fauxton/layout.js4
-rw-r--r--src/fauxton/app/addons/fauxton/tests/baseSpec.js74
-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/app.js10
-rw-r--r--src/fauxton/app/config.js2
-rw-r--r--src/fauxton/app/core/api.js108
-rw-r--r--src/fauxton/app/core/auth.js67
-rw-r--r--src/fauxton/app/core/base.js20
-rw-r--r--src/fauxton/app/core/couchdbSession.js95
-rw-r--r--src/fauxton/app/core/layout.js90
-rw-r--r--src/fauxton/app/core/routeObject.js (renamed from src/fauxton/app/core/resources.js)112
-rw-r--r--src/fauxton/app/core/router.js113
-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/tasks/couchserver.js2
-rw-r--r--src/fauxton/test/mocha/testUtils.js5
-rw-r--r--src/fauxton/test/test.config.underscore1
22 files changed, 496 insertions, 261 deletions
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/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/fauxton/base.js b/src/fauxton/app/addons/fauxton/base.js
index ee6b7944a..2fee68070 100644
--- a/src/fauxton/app/addons/fauxton/base.js
+++ b/src/fauxton/app/addons/fauxton/base.js
@@ -66,6 +66,15 @@ function(app, FauxtonAPI, resizeColumns) {
}));
}
});
+
+ 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({
diff --git a/src/fauxton/app/addons/fauxton/layout.js b/src/fauxton/app/addons/fauxton/layout.js
index 3810c84c3..baac86411 100644
--- a/src/fauxton/app/addons/fauxton/layout.js
+++ b/src/fauxton/app/addons/fauxton/layout.js
@@ -29,6 +29,10 @@ function(Backbone) {
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 () {
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..644002cdf
--- /dev/null
+++ b/src/fauxton/app/addons/fauxton/tests/baseSpec.js
@@ -0,0 +1,74 @@
+// 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();
+
+ // Need to find a better way of doing this
+ mockLayout = {
+ setTemplate: sinon.spy(),
+ clearBreadcrumbs: sinon.spy(),
+ setView: sinon.spy(),
+ renderView: 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.clearBreadcrumbs.calledOnce, 'Clear Breadcrumbs called');
+ });
+
+ it('Should set breadcrumbs when breadcrumbs exist', function () {
+ FauxtonAPI.masterLayout = mockLayout;
+ testRouteObject.renderWith('the-route', mockLayout, 'args');
+ assert.ok(mockLayout.setBreadcrumbs.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/app.js b/src/fauxton/app/app.js
index 23a72c78e..74980ea89 100644
--- a/src/fauxton/app/app.js
+++ b/src/fauxton/app/app.js
@@ -24,14 +24,14 @@ define([
"core/utils",
// Modules
"core/api",
- "addons/Fauxton/layout",
+ "core/couchdbSession",
// Plugins.
"plugins/backbone.layoutmanager",
"plugins/jquery.form"
],
-function(app, $, _, Backbone, Bootstrap, Helpers, Utils, FauxtonAPI, Layout, LoadAddons) {
+function(app, $, _, Backbone, Bootstrap, Helpers, Utils, FauxtonAPI, Couchdb) {
// Make sure we have a console.log
if (typeof console == "undefined") {
console = {
@@ -52,7 +52,7 @@ function(app, $, _, Backbone, Bootstrap, Helpers, Utils, FauxtonAPI, Layout, Loa
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,
@@ -84,11 +84,13 @@ function(app, $, _, Backbone, Bootstrap, Helpers, Utils, FauxtonAPI, Layout, Loa
}
});
+ 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 Layout(),
+ masterLayout: new FauxtonAPI.Layout(),
addHeaderLink: function(link) {
FauxtonAPI.registerExtension('navbar:addHeaderLink', link);
diff --git a/src/fauxton/app/config.js b/src/fauxton/app/config.js
index 2f1433e54..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",
diff --git a/src/fauxton/app/core/api.js b/src/fauxton/app/core/api.js
index 81e9aa495..5483f446f 100644
--- a/src/fauxton/app/core/api.js
+++ b/src/fauxton/app/core/api.js
@@ -11,12 +11,22 @@
// the License.
define([
- "core/resources",
+ "core/base",
+ "core/layout",
+ "core/router",
+ "core/routeObject",
+ "core/couchdbSession",
"core/utils"
],
-function(FauxtonAPI, utils) {
- FauxtonAPI.utils = utils;
+function(FauxtonAPI, Layout, Router, RouteObject, CouchdbSession, 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);
@@ -38,97 +48,7 @@ function(FauxtonAPI, utils) {
FauxtonAPI.router.triggerRouteEvent("route:"+routeEvent, args);
};
- var beforeUnloads = {};
-
- FauxtonAPI.Router = Backbone.Router.extend({
- routes: {},
-
- beforeUnload: function (name, fn) {
- beforeUnloads[name] = fn;
- },
-
- removeBeforeUnload: function (name) {
- delete beforeUnloads[name];
- },
-
- navigate: function (fragment, trigger) {
- var continueNav = true,
- msg = _.find(_.map(beforeUnloads, function (fn) { return fn(); }), function (beforeReturn) {
- if (beforeReturn) { return true; }
- });
-
- if (msg) {
- continueNav = window.confirm(msg);
- }
-
- if (continueNav) {
- Backbone.Router.prototype.navigate(fragment, trigger);
- }
- },
-
- addModuleRouteObject: function(RouteObject) {
- var that = this;
- 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 = FauxtonAPI.auth.checkAccess(roles);
-
- authPromise.then(function () {
- if (!that.activeRouteObject || !that.activeRouteObject.hasRoute(route)) {
- if (that.activeRouteObject) {
- that.activeRouteObject.removeViews();
- }
- that.activeRouteObject = new RouteObject(route, masterLayout, args);
- }
-
- var routeObject = that.activeRouteObject;
- routeObject.routeCallback(route, args);
- routeObject.renderWith(route, masterLayout, args);
- }, function () {
- FauxtonAPI.auth.authDeniedCb();
- });
-
- });
- }, this);
- },
-
- setModuleRoutes: function(addons) {
- _.each(addons, function(module) {
- if (module){
- module.initialize();
- // This is pure routes the addon provides
- if (module.RouteObjects) {
- _.each(module.RouteObjects, this.addModuleRouteObject, this);
- }
- }
- }, this);
- },
-
- initialize: function(addons) {
- this.addons = addons;
- this.auth = FauxtonAPI.auth;
- // NOTE: This must be below creation of the layout
- // FauxtonAPI header links and others depend on existence of the layout
- this.setModuleRoutes(addons);
-
- $(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, FauxtonAPI.masterLayout, 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
index 46013863f..e98bdf867 100644
--- a/src/fauxton/app/core/base.js
+++ b/src/fauxton/app/core/base.js
@@ -11,26 +11,19 @@
// the License.
define([
+ "backbone"
],
-function() {
+function(Backbone) {
var FauxtonAPI = {
//add default objects
router: {
navigate: function () {}
},
- masterLayout: {
- // remove these by converting to extensions
- navBar: {
- addLink: function () {},
- removeLink: function () {}
- }
- },
-
- addNotification: function () {
+ masterLayout: {},
- },
+ addNotification: function () {},
config: function (options) {
return _.extend(this, options);
@@ -109,6 +102,11 @@ function() {
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
index 532f3eacd..6f13677a6 100644
--- a/src/fauxton/app/core/couchdbSession.js
+++ b/src/fauxton/app/core/couchdbSession.js
@@ -12,69 +12,62 @@ define([
"core/base"
],
function (FauxtonAPI) {
- //this later needs to be dynamically loaded
- var app = {
- host: ""
- };
-
- FauxtonAPI.UUID = FauxtonAPI.Model.extend({
- initialize: function(options) {
- options = _.extend({count: 1}, options);
- this.count = options.count;
- },
+ var CouchdbSession = {
+ 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;
- },
+ url: function() {
+ return "/_uuids?count=" + this.count;
+ },
- next: function() {
- return this.get("uuids").pop();
- }
- });
+ next: function() {
+ return this.get("uuids").pop();
+ }
+ }),
- FauxtonAPI.Session = FauxtonAPI.Model.extend({
- url: app.host + '/_session',
+ Session: FauxtonAPI.Model.extend({
+ url: '/_session',
- user: function () {
- var userCtx = this.get('userCtx');
+ user: function () {
+ var userCtx = this.get('userCtx');
- if (!userCtx || !userCtx.name) { return null; }
+ if (!userCtx || !userCtx.name) { return null; }
- return {
- name: userCtx.name,
- roles: userCtx.roles
- };
- },
+ return {
+ name: userCtx.name,
+ roles: userCtx.roles
+ };
+ },
- fetchUser: function (opt) {
- var that = this,
- currentUser = this.user();
+ fetchUser: function (opt) {
+ var that = this,
+ currentUser = this.user();
- return this.fetchOnce(opt).then(function () {
- var user = that.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');
- }
+ // 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) {
- FauxtonAPI.session = newSession;
- return FauxtonAPI.session.fetchUser();
+ // 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;
+ });
+ }
+ })
};
+
//set default session
- FauxtonAPI.setSession(new FauxtonAPI.Session());
+ //FauxtonAPI.setSession(new FauxtonAPI.Session());
- return FauxtonAPI;
+ return CouchdbSession;
});
diff --git a/src/fauxton/app/core/layout.js b/src/fauxton/app/core/layout.js
new file mode 100644
index 000000000..a5ed88c36
--- /dev/null
+++ b/src/fauxton/app/core/layout.js
@@ -0,0 +1,90 @@
+// 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",
+ "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 () {
+ this.layout = new Backbone.Layout({
+ template: "templates/layouts/with_sidebar",
+ });
+
+ this.layoutViews = {};
+ 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 () {
+ return this.layout.render();
+ },
+
+ setTemplate: function(template) {
+ if (template.prefix){
+ this.layout.template = template.prefix + template.name;
+ } else{
+ this.layout.template = "templates/layouts/" + template;
+ }
+ // If we're changing layouts all bets are off, so kill off all the
+ // existing views in the layout.
+ _.each(this.layoutViews, function(view){view.remove();});
+ this.layoutViews = {};
+ 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 ;}
+
+ this.breadcrumbs.remove();
+ },
+
+ setView: function(selector, view) {
+ this.layoutViews[selector] = this.layout.setView(selector, view, false);
+ },
+
+ renderView: function(selector) {
+ var view = this.layoutViews[selector];
+ if (!view) {
+ return false;
+ } else {
+ return view.render();
+ }
+ }
+
+ });
+
+ return Layout;
+
+});
diff --git a/src/fauxton/app/core/resources.js b/src/fauxton/app/core/routeObject.js
index 3a2907788..9fe8f6975 100644
--- a/src/fauxton/app/core/resources.js
+++ b/src/fauxton/app/core/routeObject.js
@@ -11,59 +11,12 @@
// the License.
define([
- "app",
- "core/couchdbSession",
+ "core/base",
"backbone"
],
-function(app, FauxtonAPI) {
+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();
-
- FauxtonAPI.RouteObject = function(options) {
+ var RouteObject = function(options) {
this._options = options;
this._configure(options || {});
@@ -74,7 +27,7 @@ function(app, FauxtonAPI) {
var broadcaster = {};
_.extend(broadcaster, Backbone.Events);
- FauxtonAPI.RouteObject.on = function (eventName, fn) {
+ RouteObject.on = function (eventName, fn) {
broadcaster.on(eventName, fn);
};
@@ -109,11 +62,11 @@ function(app, FauxtonAPI) {
*/
// Piggy-back on Backbone's self-propagating extend function
- FauxtonAPI.RouteObject.extend = Backbone.Model.extend;
+ RouteObject.extend = Backbone.Model.extend;
var routeObjectOptions = ["views", "routes", "events", "roles", "crumbs", "layout", "apiUrl", "establish"];
- _.extend(FauxtonAPI.RouteObject.prototype, Backbone.Events, {
+ _.extend(RouteObject.prototype, Backbone.Events, {
// Should these be default vals or empty funcs?
views: {},
routes: {},
@@ -127,6 +80,7 @@ function(app, FauxtonAPI) {
establish: function() {},
route: function() {},
roles: [],
+ _promises: [],
initialize: function() {}
}, {
@@ -141,7 +95,9 @@ function(app, FauxtonAPI) {
}
triggerBroadcast('beforeEstablish');
- FauxtonAPI.when(this.establish()).then(function(resp) {
+ var establishPromise = this.establish();
+ this.addPromise(establishPromise);
+ FauxtonAPI.when(establishPromise).then(function(resp) {
triggerBroadcast('afterEstablish');
_.each(routeObject.getViews(), function(view, selector) {
if(view.hasRendered) {
@@ -150,7 +106,9 @@ function(app, FauxtonAPI) {
}
triggerBroadcast('beforeRender', view, selector);
- FauxtonAPI.when(view.establish()).then(function(resp) {
+ var viewPromise = view.establish();
+ routeObject.addPromise(viewPromise);
+ FauxtonAPI.when(viewPromise).then(function(resp) {
masterLayout.setView(selector, view);
masterLayout.renderView(selector);
@@ -161,7 +119,7 @@ function(app, FauxtonAPI) {
reason: resp
};
- if (resp) {
+ if (resp && resp.responseText) {
var errorText = JSON.parse(resp.responseText).reason;
FauxtonAPI.addNotification({
msg: 'An Error occurred: ' + errorText,
@@ -169,13 +127,12 @@ function(app, FauxtonAPI) {
clear: true
});
}
-
masterLayout.renderView(selector);
});
});
}.bind(this), function (resp) {
- if (!resp) { return; }
+ if (!resp || !resp.responseText) { return; }
FauxtonAPI.addNotification({
msg: 'An Error occurred' + JSON.parse(resp.responseText).reason,
type: 'error',
@@ -183,12 +140,6 @@ function(app, FauxtonAPI) {
});
});
- 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');
@@ -244,6 +195,36 @@ function(app, FauxtonAPI) {
}, 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'));
},
@@ -280,6 +261,5 @@ function(app, FauxtonAPI) {
}
});
-
- return FauxtonAPI;
+ return RouteObject;
});
diff --git a/src/fauxton/app/core/router.js b/src/fauxton/app/core/router.js
new file mode 100644
index 000000000..966e171f4
--- /dev/null
+++ b/src/fauxton/app/core/router.js
@@ -0,0 +1,113 @@
+// 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/auth",
+ "backbone"
+],
+
+function(FauxtonAPI, Auth, Backbone) {
+
+ var beforeUnloads = {};
+
+ var Router = Backbone.Router.extend({
+ routes: {},
+
+ beforeUnload: function (name, fn) {
+ beforeUnloads[name] = fn;
+ },
+
+ removeBeforeUnload: function (name) {
+ delete beforeUnloads[name];
+ },
+
+ navigate: function (fragment, trigger) {
+ var continueNav = true,
+ msg = _.find(_.map(beforeUnloads, function (fn) { return fn(); }), function (beforeReturn) {
+ if (beforeReturn) { return true; }
+ });
+
+ if (msg) {
+ continueNav = window.confirm(msg);
+ }
+
+ if (continueNav) {
+ Backbone.Router.prototype.navigate(fragment, trigger);
+ }
+ },
+
+ addModuleRouteObject: function(RouteObject) {
+ var that = this;
+ 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 = FauxtonAPI.auth.checkAccess(roles);
+
+ authPromise.then(function () {
+ if (!that.activeRouteObject || !that.activeRouteObject.hasRoute(route)) {
+ if (that.activeRouteObject) {
+ that.activeRouteObject.removeViews();
+ }
+ that.activeRouteObject = new RouteObject(route, masterLayout, args);
+ }
+
+ var routeObject = that.activeRouteObject;
+ routeObject.routeCallback(route, args);
+ routeObject.renderWith(route, masterLayout, args);
+ }, function () {
+ FauxtonAPI.auth.authDeniedCb();
+ });
+
+ });
+ }, this);
+ },
+
+ setModuleRoutes: function(addons) {
+ _.each(addons, function(module) {
+ if (module){
+ module.initialize();
+ // This is pure routes the addon provides
+ if (module.RouteObjects) {
+ _.each(module.RouteObjects, this.addModuleRouteObject, this);
+ }
+ }
+ }, this);
+ },
+
+ 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.setModuleRoutes(addons);
+
+ $(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, 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/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..08b80c70d 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 () {
@@ -24,7 +25,7 @@ function(chai, sinonChai) {
_.extend(ViewSandbox.prototype, {
initialize: function () {
- this.$el = $('<div style="display:none"></div>').appendTo('body');
+ this.$el = $('<div style="display:no1ne"></div>').appendTo('body');
this.$ = this.$el.find;
},
views: [],
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 %>',
<% }) %>