couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gar...@apache.org
Subject [1/6] git commit: updated refs/heads/route-events to f6081cb
Date Tue, 14 May 2013 12:49:17 GMT
Updated Branches:
  refs/heads/route-events c33e3903c -> f6081cbdc (forced update)


Prototyping Route Object to reduct number of full page reloads


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/4642d5db
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/4642d5db
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/4642d5db

Branch: refs/heads/route-events
Commit: 4642d5db7bc3e4e1eab0a27b15a5bc27c71b1caf
Parents: 53e5188
Author: Russell Branca <chewbranca@gmail.com>
Authored: Thu Apr 4 16:18:47 2013 -0700
Committer: Garren Smith <garren.smith@gmail.com>
Committed: Tue May 14 14:46:22 2013 +0200

----------------------------------------------------------------------
 src/fauxton/app/addons/config/routes.js            |   57 +-
 src/fauxton/app/addons/logs/resources.js           |    5 +
 src/fauxton/app/addons/logs/routes.js              |   57 +-
 src/fauxton/app/addons/stats/base.js               |    4 +-
 src/fauxton/app/addons/stats/resources.js          |    2 +-
 src/fauxton/app/addons/stats/routes.js             |   59 +-
 src/fauxton/app/addons/stats/views.js              |    4 +-
 src/fauxton/app/api.js                             |  156 ++++
 src/fauxton/app/initialize.js                      |    6 +-
 src/fauxton/app/main.js                            |   25 +-
 src/fauxton/app/modules/databases/resources.js     |    2 +-
 src/fauxton/app/modules/databases/routes.js        |  108 ++--
 src/fauxton/app/modules/databases/views.js         |   29 +-
 src/fauxton/app/modules/documents/resources.js     |    4 +-
 src/fauxton/app/modules/documents/routes.js        |  562 ++++++++-------
 src/fauxton/app/modules/documents/views.js         |   12 +
 src/fauxton/app/modules/fauxton/base.js            |   28 +-
 src/fauxton/app/router.js                          |   38 +-
 src/fauxton/app/templates/databases/list.html      |    1 +
 .../templates/documents/doc_field_editor_tabs.html |    4 +-
 .../app/templates/documents/index_menu_item.html   |    4 +-
 src/fauxton/app/templates/documents/sidebar.html   |    6 +-
 src/fauxton/app/templates/fauxton/pagination.html  |   17 +
 src/fauxton/app/templates/layouts/with_tabs.html   |    2 +-
 src/fauxton/assets/index.underscore                |    1 +
 25 files changed, 770 insertions(+), 423 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/addons/config/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/config/routes.js b/src/fauxton/app/addons/config/routes.js
index 7ed6498..495e3a9 100644
--- a/src/fauxton/app/addons/config/routes.js
+++ b/src/fauxton/app/addons/config/routes.js
@@ -11,34 +11,45 @@
 // the License.
 
 define([
-  "app",
+       "app",
 
-  "api",
+       "api",
 
-  // Modules
-  "addons/config/resources"
+       // Modules
+       "addons/config/resources"
 ],
 
 function(app, FauxtonAPI, Config) {
-  var configRoute = function () {
-    var configs = new Config.Collection();
-
-    return {
-      layout: "one_pane",
-      crumbs: [
-        {"name": "Config","link": "_config"}
-      ],
-      views: {
-        "#dashboard-content": new Config.View({collection: configs})
-      },
-      apiUrl: configs.url()
-    };
-  };
-
-  Config.Routes = {
-    "_config": configRoute
-  };
 
-  return Config;
+  var ConfigRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "one_pane",
+
+    initialize: function () {
+      this.configs = new Config.Collection();
+    },
+
+    crumbs: [
+      {"name": "Config","link": "_config"}
+    ],
+
+    apiUrl: function () {
+      this.configs.url();
+    },
+
+    routes: ["_config"],
 
+    defaultRoute: "config",
+
+    config: function () {
+      this.setView("#dashboard-content", new Config.View({collection: this.configs}));
+    },
+
+    establish: function () {
+      return [this.configs.fetch()];
+    }
+  });
+
+
+  Config.RouteObjects = [ConfigRouteObject];
+  return Config;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/addons/logs/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/logs/resources.js b/src/fauxton/app/addons/logs/resources.js
index d1e6d20..91a9bcb 100644
--- a/src/fauxton/app/addons/logs/resources.js
+++ b/src/fauxton/app/addons/logs/resources.js
@@ -98,11 +98,16 @@ function (app, FauxtonAPI, Backbone) {
 
       this.filters = [];
       this.filteredCollection = new Log.Collection(this.collection.toJSON());
+
       this.collection.on("add", function () {
         this.createFilteredCollection();
       }, this);
     },
 
+    establish: function () {
+      return [this.collection.fetch()];
+    },
+
     serialize: function () {
       return { logs: this.filteredCollection};
     },

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/addons/logs/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/logs/routes.js b/src/fauxton/app/addons/logs/routes.js
index 4e04d08..9bd8f65 100644
--- a/src/fauxton/app/addons/logs/routes.js
+++ b/src/fauxton/app/addons/logs/routes.js
@@ -11,37 +11,48 @@
 // the License.
 
 define([
-  "app",
+       "app",
 
-  "api",
+       "api",
 
-  // Modules
-  "addons/logs/resources"
+       // Modules
+       "addons/logs/resources"
 ],
 
 function(app, FauxtonAPI, Log) {
 
-  Log.Routes = {
-    "_log": function() {
-      var logs = new Log.Collection();
-
-      return {
-        layout: "with_sidebar",
-        crumbs: [
-          {"name": "Logs", "link": "_log"}
-        ],
-        views: {
-          "#dashboard-content": new Log.Views.View({collection: logs}),
-          "#sidebar-content": new Log.Views.FilterView({})
-        },
-        apiUrl: logs.url(),
-        establish: function() {
-          return [logs.fetch()];
-        }
-      };
+  var  LogRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_sidebar",
+
+    crumbs: [
+      {"name": "Logs", "link": "_log"}
+    ],
+
+    routes: ["_log"],
+
+    defaultRoute: "showLog",
+
+    apiUrl: function() {
+      return this.logs.url();
+    },
+
+    initialize: function () {
+      this.logs = new Log.Collection();
+      this.setView("#sidebar-content", new Log.Views.FilterView({}));
+    },
+
+    showLog: function (event) {
+      this.setView("#dashboard-content", new Log.Views.View({collection: this.logs}));
+    },
+
+    e1stablish: function() {
+      return [this.logs.fetch()];
     }
-  };
+  });
+
+  Log.RouteObjects = [LogRouteObject];
 
   return Log;
 
 });
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/addons/stats/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/stats/base.js b/src/fauxton/app/addons/stats/base.js
index 33316c4..4721399 100644
--- a/src/fauxton/app/addons/stats/base.js
+++ b/src/fauxton/app/addons/stats/base.js
@@ -16,13 +16,11 @@ define([
   "addons/stats/routes"
 ],
 
-function(app, FauxtonAPI, AddonRoutes) {
-  var Stats = new FauxtonAPI.addon();
+function(app, FauxtonAPI, Stats) {
 
   Stats.initialize = function() {
     FauxtonAPI.addHeaderLink({title: "Statistics", href: "#stats"});
   };
 
-  Stats.Routes = AddonRoutes;
   return Stats;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/addons/stats/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/stats/resources.js b/src/fauxton/app/addons/stats/resources.js
index 238a032..94be6bb 100644
--- a/src/fauxton/app/addons/stats/resources.js
+++ b/src/fauxton/app/addons/stats/resources.js
@@ -19,7 +19,7 @@ define([
 ],
 
 function (app, FauxtonAPI, backbone, _, Fauxton) {
-  var Stats = {};
+  var Stats = new FauxtonAPI.addon();
 
   Stats.Collection = Backbone.Collection.extend({
     model: Backbone.Model,

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/addons/stats/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/stats/routes.js b/src/fauxton/app/addons/stats/routes.js
index 84947fa..7ad2a1f 100644
--- a/src/fauxton/app/addons/stats/routes.js
+++ b/src/fauxton/app/addons/stats/routes.js
@@ -11,49 +11,42 @@
 // the License.
 
 define([
-  "app",
-  "api",
-  "addons/stats/resources",
-  "addons/stats/views"
+       "app",
+       "api",
+       "addons/stats/views"
 ],
 
-function(app, FauxtonAPI, Stats, Views) {
-  var data = {
-    stats: new Stats.Collection()
-  };
+function(app, FauxtonAPI, Stats) {
 
-  var deferred = FauxtonAPI.Deferred();
+  var StatsRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_sidebar",
 
-  var routeCallback = function() {
-    return {
-      layout: "with_sidebar",
+    routes: ["stats", "_stats"],
+    defaultRoute: "showStats",
 
-      data: data,
+    initialize: function () {
+      this.stats = new Stats.Collection();
 
-      crumbs: [],
+      this.setView("#sidebar-content", new Views.StatSelect({
+        collection: this.stats
+      }));
 
-      views: {
-        "#sidebar-content": new Views.StatSelect({
-          collection: data.stats
-        }),
+    },
 
-        "#dashboard-content": new Views.Statistics({
-          collection: data.stats
-        })
-      },
+    showStats: function (event) {
+      this.setView("#dashboard-content", new Views.Statistics({
+        collection: this.stats
+      }));
+    },
 
-      establish: function() {
-        return [data.stats.fetch()];
-      },
+    establish: function() {
+      return [this.stats.fetch()];
+    },
 
-      apiUrl: "_stats"
-    };
-  };
+    apiUrl: "_stats"
+  });
 
-  Routes = {
-    "stats": routeCallback,
-    "_stats": routeCallback
-  };
+  Stats.RouteObjects = [StatsRouteObject];
 
-  return Routes;
+  return Stats;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/addons/stats/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/stats/views.js b/src/fauxton/app/addons/stats/views.js
index 21454f9..9fda708 100644
--- a/src/fauxton/app/addons/stats/views.js
+++ b/src/fauxton/app/addons/stats/views.js
@@ -168,5 +168,7 @@ function(app, FauxtonAPI,Stats) {
     }
   });
 
-  return Views;
+  Stats.Views = Views;
+
+  return Stats;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/api.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/api.js b/src/fauxton/app/api.js
index 52e8611..6591daf 100644
--- a/src/fauxton/app/api.js
+++ b/src/fauxton/app/api.js
@@ -43,6 +43,14 @@ function(app, Fauxton) {
     // This should return an array of promises, an empty array, or null
     establish: function() {
       return null;
+    },
+
+    hasRendered: function () {
+      return !!this.__manager__.hasRendered;
+    },
+
+    reRender: function () {
+      this.__manager__.hasRendered = false;
     }
   });
 
@@ -62,6 +70,10 @@ function(app, Fauxton) {
     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));
   };
@@ -96,6 +108,150 @@ function(app, Fauxton) {
     }
   });
 
+  // Not needed, could be removed.
+  FauxtonAPI.routeCallChain = {
+    callChain: {},
+
+    registerBeforeRoute: function (name, fn) {
+      this.callChain[name] = fn;
+    },
+
+    unregisterBeforeRoute: function (name) {
+      delete callChain[name];
+    },
+
+    run: function () {
+      var callChainDeferreds = _.map(this.callChain, function (cb) { return cb(); }); 
+      return $.when(null, callChainDeferreds );
+    }
+  };
+
+
+  FauxtonAPI.RouteObject = function(options) {
+    this._options = options;
+
+    this._configure(options || {});
+    this.initialize.apply(this, arguments);
+    this.addEvents();
+  };
+
+  // Piggy-back on Backbone's self-propagating extend function
+  FauxtonAPI.RouteObject.extend = Backbone.Model.extend;
+
+  var routeObjectOptions = ["views", "routes", "events", "data", "crumbs", "layout", "apiUrl", "establish"];
+
+  _.extend(FauxtonAPI.RouteObject.prototype, Backbone.Events, {
+    // Should these be default vals or empty funcs?
+    views: {},
+    routes: {},
+    events: {},
+    data: {},
+    crumbs: [],
+    layout: "with_sidebar",
+    apiUrl: null,
+    renderedState: false,
+    currTab: "databases",
+    establish: function() {},
+    route: function() {},
+    initialize: function() {}
+  }, {
+
+    // TODO:: combine this and the renderWith function
+    // All the things should go through establish, as it will resolve
+    // immediately if its already done, but this way the RouteObject.route
+    // function can rebuild the deferred as needed
+    render: function(route, masterLayout, args) {
+      this.route.call(this, route, args);
+      this.renderWith.apply(this, Array.prototype.slice.call(arguments));
+    },
+
+    renderWith: function(route, masterLayout, args) {
+      var routeObject = this;
+
+      // Only want to redo the template if its a full render
+      if (!this.renderedState) {
+        masterLayout.setTemplate(this.layout);
+      }
+
+      masterLayout.clearBreadcrumbs();
+      var crumbs = this.get('crumbs');
+
+      if (crumbs.length) {
+        masterLayout.setBreadcrumbs(new Fauxton.Breadcrumbs({
+          crumbs: crumbs
+        }));
+      }
+
+      $.when.apply(this, this.establish()).done(function(resp) {
+        _.each(routeObject.getViews(), function(view, selector) {
+          if(view.hasRendered()) { console.log('view been rendered'); return; }
+
+          masterLayout.setView(selector, view);
+          console.log('set and render ', selector, view); 
+
+          $.when.apply(null, view.establish()).then(function(resp) {
+            masterLayout.renderView(selector);
+          }, function(resp) {
+            view.establishError = {
+              error: true,
+              reason: resp
+            };
+            masterLayout.renderView(selector);
+          });
+
+          var hooks = masterLayout.hooks[selector];
+
+          _.each(hooks, function(hook){
+            if (_.any(hook.routes, function(route){return route == boundRoute;})){
+              hook.callback(view);
+            }
+          });
+        });
+      });
+
+      if (this.get('apiUrl')) masterLayout.apiBar.update(this.get('apiUrl'));
+
+      // Track that we've done a full initial render
+      this.renderedState = true;
+    },
+
+    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;
+    }
+
+  });
+
   app.fauxtonAPI = FauxtonAPI;
   return app.fauxtonAPI;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/initialize.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/initialize.js b/src/fauxton/app/initialize.js
index 5699678..e5dbc73 100644
--- a/src/fauxton/app/initialize.js
+++ b/src/fauxton/app/initialize.js
@@ -37,8 +37,12 @@ function(app, _, Bootstrap) {
     // Thanks to: http://stackoverflow.com/a/2880929
     getParams: function(queryString) {
       if (typeof queryString !== "undefined") {
-        if (queryString.substring(0,1) === "?")
+        // 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);

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/main.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/main.js b/src/fauxton/app/main.js
index d3f8ac2..203b295 100644
--- a/src/fauxton/app/main.js
+++ b/src/fauxton/app/main.js
@@ -23,16 +23,25 @@ function(app, Router) {
     // Get the absolute root.
     var root = location.protocol + "//" + location.host + app.root;
 
-    // Ensure the root is part of the anchor href, meaning it's relative.
-    if (href.prop && href.prop.slice(0, root.length) === root) {
-      // Stop the default event to ensure the link will not cause a page
-      // refresh.
+    var routeEvent = $(this).attr("route-event");
+    if (routeEvent) {
       evt.preventDefault();
-
-      // `Backbone.history.navigate` is sufficient for all Routers and will
-      // trigger the correct events. The Router's internal `navigate` method
-      // calls this anyways.  The fragment is sliced from the root.
+      // TODO:: change to false when route events are functional
       Backbone.history.navigate(href.attr, true);
+      // Trigger  route events after update of history so that we can get params from url
+      app.router.triggerRouteEvent("route:"+routeEvent, href);
+    } else {
+      // Ensure the root is part of the anchor href, meaning it's relative.
+      if (href.prop && href.prop.slice(0, root.length) === root) {
+        // Stop the default event to ensure the link will not cause a page
+        // refresh.
+        evt.preventDefault();
+
+        // `Backbone.history.navigate` is sufficient for all Routers and will
+        // trigger the correct events. The Router's internal `navigate` method
+        // calls this anyways.  The fragment is sliced from the root.
+        Backbone.history.navigate(href.attr, true);
+      }
     }
   });
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/modules/databases/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/databases/resources.js b/src/fauxton/app/modules/databases/resources.js
index 59dd179..6927fd5 100644
--- a/src/fauxton/app/modules/databases/resources.js
+++ b/src/fauxton/app/modules/databases/resources.js
@@ -135,7 +135,7 @@ function(app, FauxtonAPI, Documents) {
 
     parse: function(resp) {
       // TODO: pagination!
-      return _.map(_.first(resp, 10), function(database) {
+      return _.map(resp, function(database) {
         return {
           id: encodeURIComponent(database),
           name: database

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/modules/databases/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/databases/routes.js b/src/fauxton/app/modules/databases/routes.js
index 12edd0e..f1be372 100644
--- a/src/fauxton/app/modules/databases/routes.js
+++ b/src/fauxton/app/modules/databases/routes.js
@@ -16,55 +16,71 @@ define([
   "api",
 
   // Modules
-  "modules/databases/resources"
+  "modules/databases/resources",
+  // TODO:: fix the include flow modules so we don't have to require views here
+  "modules/databases/views"
 ],
 
-function(app, FauxtonAPI, Databases) {
-  var allDbsCallback = function() {
-    var data = {
-      databases: new Databases.List()
-    };
-    var deferred = FauxtonAPI.Deferred();
-
-    return {
-      layout: "with_sidebar",
-
-      data: data,
-
-      crumbs: [
-        {"name": "Databases", "link": "/_all_dbs"}
-      ],
-
-      views: {
-        "#dashboard-content": new Databases.Views.List({
-          collection: data.databases
-        }),
-
-        "#sidebar-content": new Databases.Views.Sidebar({
-          collection: data.databases
-        })
-      },
-
-      apiUrl: data.databases.url(),
-
-      establish: function() {
-        data.databases.fetch().done(function(resp) {
-          $.when.apply(null, data.databases.map(function(database) {
-            return database.status.fetch();
-          })).done(function(resp) {
-            deferred.resolve();
-          });
+function(app, FauxtonAPI, Databases, Views) {
+
+  var AllDbsRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_sidebar",
+
+    crumbs: [
+      {"name": "Databases", "link": "/_all_dbs"}
+    ],
+
+    events: {
+      "route:all_databases": "allDatabases"
+    },
+
+    defaultRoute: "allDatabases",
+
+    routes: ["", "index.html", "_all_dbs(:params)"],
+
+    apiUrl: function() {
+      return this.databases.url();
+    },
+
+    initialize: function() {
+      this.databases = new Databases.List();
+      this.deferred = FauxtonAPI.Deferred();
+
+      this.sidebarView = this.setView("#sidebar-content", new Views.Sidebar({
+          collection: this.databases
+      }));
+    },
+
+    allDatabases: function(event) {
+      event = event || {};
+
+      var params = app.getParams(event.attr),
+          dbPage = params.page;
+
+      this.databasesView = this.setView("#dashboard-content", new Views.List({
+          collection: this.databases
+      }));
+
+      this.databasesView.setPage(dbPage);
+    },
+
+    establish: function() {
+      var databases = this.databases;
+      var deferred = this.deferred;
+
+      databases.fetch().done(function(resp) {
+        $.when.apply(null, databases.map(function(database) {
+          return database.status.fetch();
+        })).done(function(resp) {
+          deferred.resolve();
         });
-        return [deferred];
-      }
-    };
-  };
-
-  Databases.Routes = {
-    "": allDbsCallback,
-    "index.html": allDbsCallback,
-    "_all_dbs": allDbsCallback
-  };
+      });
+
+      return [deferred];
+    }
+  });
+  
+  Databases.RouteObjects = [AllDbsRouteObject];
 
   return Databases;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/modules/databases/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/databases/views.js b/src/fauxton/app/modules/databases/views.js
index 322cd5a..23145fe 100644
--- a/src/fauxton/app/modules/databases/views.js
+++ b/src/fauxton/app/modules/databases/views.js
@@ -13,10 +13,11 @@
 define([
   "app",
 
+  "modules/fauxton/base",
   "api"
 ],
 
-function(app, FauxtonAPI) {
+function(app, Fauxton, FauxtonAPI) {
   var Views = {};
 
   Views.Item = FauxtonAPI.View.extend({
@@ -32,6 +33,7 @@ function(app, FauxtonAPI) {
 
   Views.List = FauxtonAPI.View.extend({
     dbLimit: 10,
+    perPage: 10,
     template: "templates/databases/list",
     events: {
       "click button.all": "selectAll",
@@ -39,7 +41,8 @@ function(app, FauxtonAPI) {
     },
 
     initialize: function(options) {
-      this.collection.on("add", this.render, this);
+      var params = app.getParams();
+      this.page = params.page ? parseInt(params.page, 10) : 1;
     },
 
     serialize: function() {
@@ -61,12 +64,32 @@ function(app, FauxtonAPI) {
       }
     },
 
+    paginated: function() {
+      var start = (this.page - 1) * this.perPage;
+      var end = this.page * this.perPage;
+      return this.collection.slice(start, end);
+    },
+
     beforeRender: function() {
-      this.collection.each(function(database) {
+      _.each(this.paginated(), function(database) {
         this.insertView("table.databases tbody", new Views.Item({
           model: database
         }));
       }, this);
+
+      this.insertView("#database-pagination", new Fauxton.Pagination({
+        page: this.page,
+        perPage: this.perPage,
+        total: this.collection.length,
+        urlFun: function(page) {
+          return "#/_all_dbs?page=" + page;
+        },
+        routeEvent: "all_databases"
+      }));
+    },
+
+    setPage: function(page) {
+      this.page = page || 1;
     },
 
     afterRender: function() {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/modules/documents/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/resources.js b/src/fauxton/app/modules/documents/resources.js
index 3f07238..f1a07c3 100644
--- a/src/fauxton/app/modules/documents/resources.js
+++ b/src/fauxton/app/modules/documents/resources.js
@@ -35,9 +35,11 @@ function(app, FauxtonAPI, Views) {
       }
     },
 
-    initialize: function() {
+    initialize: function(_attrs, options) {
       if (this.collection && this.collection.database) {
         this.database = this.collection.database;
+      } else if (options.database) {
+        this.database = options.database;
       }
     },
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/modules/documents/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/routes.js b/src/fauxton/app/modules/documents/routes.js
index 34b00d8..d57487f 100644
--- a/src/fauxton/app/modules/documents/routes.js
+++ b/src/fauxton/app/modules/documents/routes.js
@@ -11,13 +11,13 @@
 // the License.
 
 define([
-  "app",
+       "app",
 
-  "api",
+       "api",
 
-  // Modules
-  "modules/documents/resources",
-  "modules/databases/base"
+       // Modules
+       "modules/documents/resources",
+       "modules/databases/base"
 ],
 
 function(app, FauxtonAPI, Documents, Databases) {
@@ -25,51 +25,69 @@ function(app, FauxtonAPI, Documents, Databases) {
   // var Documents = require("modules/documents/models_collections");
   // var Databases = require("modules/databases/module");
 
-  var codeEditorCallback = function(databaseName, docID) {
-    var data = {
-      database: new Databases.Model({id:databaseName}),
-      doc: new Documents.Doc({
-        "_id": docID
-      }),
-      selected: "code_editor"
-    };
-    data.doc.database = data.database;
-    data.designDocs = new Documents.AllDocs(null, {
-      database: data.database,
-      params: {startkey: '"_design"',
-        endkey: '"_design1"',
-        include_docs: true}
-    });
+  var DocEditorRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "one_pane",
 
-    var options = app.getParams();
-    options.include_docs = true;
-    data.database.buildAllDocs(options);
+    initialize: function(options) {
+      var databaseName = options[0], docID = options[1];
 
-    return {
-      layout: "one_pane",
+      this.database = this.database || new Databases.Model({id: databaseName});
+      this.doc = this.doc || new Documents.Doc({
+        _id: docID
+      }, {
+        database: this.database
+      });
 
-      data: data,
+      this.tabsView = this.setView("#tabs", new Documents.Views.FieldEditorTabs({
+        selected: "code_editor",
+        model: this.doc
+      }));
 
-      crumbs: [
+    },
+
+    routes: function() {
+      return _.keys(this.selectedRoutes);
+    },
+
+    selectedRoutes: {
+      "database/:database/:doc/field_editor": "field_editor",
+      "database/:database/:doc/code_editor": "code_editor",
+      "database/:database/:doc": "code_editor"
+    },
+
+    events: {
+      "route:field_editor": "field_editor",
+      "route:code_editor": "code_editor"
+    },
+
+    defaultRoute: "code_editor",
+
+    crumbs: function() {
+      return [
         {"name": "Databases", "link": "/_all_dbs"},
-        {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-        {"name": docID, "link": "#"}
-      ],
+        {"name": this.database.id, "link": Databases.databaseUrl(this.database)},
+        {"name": this.docID, "link": "#"}
+      ];
+    },
 
-      views: {
-        "#dashboard-content": new Documents.Views.Doc({
-          model: data.doc
-        }),
+    code_editor: function (event) {
+      this.tabsView.updateSelected('code_editor');
+      this.docView = this.setView("#dashboard-content", new Documents.Views.Doc({
+        model: this.doc
+      }));
+    },
 
-        "#tabs": new Documents.Views.FieldEditorTabs({
-          selected: data.selected,
-          model: data.doc
-        })
-      },
+    field_editor: function(events) {
+      this.tabsView.updateSelected('field_editor');
+      this.docView = this.setView("#dashboard-content", new Documents.Views.DocFieldEditor({
+        model: this.doc
+      }));
+    },
 
-      apiUrl: data.doc.url()
-    };
-  };
+    apiUrl: function() {
+      return this.doc.url();
+    }
+  });
 
   var newViewEditorCallback = function(databaseName) {
     var data = {
@@ -78,8 +96,8 @@ function(app, FauxtonAPI, Documents, Databases) {
     data.designDocs = new Documents.AllDocs(null, {
       database: data.database,
       params: {startkey: '"_design"',
-               endkey: '"_design1"',
-               include_docs: true}
+        endkey: '"_design1"',
+        include_docs: true}
     });
 
     return {
@@ -160,267 +178,287 @@ function(app, FauxtonAPI, Documents, Databases) {
     };
   };
 
-  Documents.Routes = {
-    "database/:database/:doc/field_editor": function(databaseName, docID) {
-      var data = {
-        database: new Databases.Model({id:databaseName}),
-        doc: new Documents.Doc({
-          "_id": docID
-        }),
-        selected: "field_editor"
-      };
-      data.doc.database = data.database;
-      data.designDocs = new Documents.AllDocs(null, {
-        database: data.database,
-        params: {startkey: '"_design"',
-          endkey: '"_design1"',
-          include_docs: true}
-      });
 
-      var options = app.getParams();
-      options.include_docs = true;
-      data.database.buildAllDocs(options);
+  var DocumentsRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_tabs_sidebar",
 
-      return {
-        layout: "one_pane",
+    events: {
+      "route:all_docs": "allDocs",
+      "route:all_design_docs": "allDesignDocs",
+      "route:view_fn": "viewFn",
+      "route:new_view": "newViewEditor"
+    },
 
-        data: data,
+    defaultRoute: "allDocs",
 
-        crumbs: [
-          {"name": "Databases", "link": "/_all_dbs"},
-          {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-          {"name": docID, "link": "#"}
-        ],
-
-        views: {
-          "#dashboard-content": new Documents.Views.DocFieldEditor({
-            model: data.doc
-          }),
-
-          "#tabs": new Documents.Views.FieldEditorTabs({
-            selected: data.selected,
-            model: data.doc
-          })
-        },
-
-        apiUrl: data.doc.url()
-      };
-    },
+    initialize: function (options) {
+      var docOptions = app.getParams();
+      docOptions.include_docs = true;
 
-    "database/:database/:doc/code_editor": codeEditorCallback,
-    "database/:database/:doc": codeEditorCallback,
-    "database/:database/_design%2F:doc": function(database, doc) {
-      var docID = "_design/"+doc;
-      return codeEditorCallback(database, docID);
-    },
+      this.databaseName = options[0];
 
-    // HACK
-    // The ordering of routes is different in this object that the
-    // routes object in the Backbone.Router. As a result, the
-    // declaration order of show doc and _handler methods has been
-    // switched. This is a brittle solution that needs to be fixed.
-    // Conflicts with route: "database/:database/_:handler"
-    //
-    // TODO: add support for regex based rotues
-    // Javascript does not handle a regex as an object key very well,
-    // and it turns it into its string representation when you use in
-    // non object literal form, which does get recast back as a regex
-    // when we need it.
-    // The inability to use regex based routes here is a design flaw
-    // and should be rectified.
-    "old_database/:database/:doc": function(databaseName, docID) {
-      var data = {
-        database: new Databases.Model({id:databaseName}),
-        doc: new Documents.Doc({
-          "_id": docID
-        })
+      this.data = {
+        database: new Databases.Model({id:this.databaseName})
       };
-      data.doc.database = data.database;
-      data.designDocs = new Documents.AllDocs(null, {
-        database: data.database,
+
+      this.data.designDocs = new Documents.AllDocs(null, {
+        database: this.data.database,
         params: {startkey: '"_design"',
           endkey: '"_design1"',
           include_docs: true}
       });
 
-      var options = app.getParams();
-      options.include_docs = true;
-      data.database.buildAllDocs(options);
+      this.sidebar = this.setView("#sidebar-content", new Documents.Views.Sidebar({
+        collection: this.data.designDocs
+      }));
 
-      return {
-        layout: "with_sidebar",
+      this.setView("#tabs", new Documents.Views.Tabs({
+        collection: this.data.designDocs,
+        database: this.data.database
+      }));
+    },
 
-        data: data,
+    crumbs: function () {
+      return [
+        {"name": "Databases", "link": "/_all_dbs"},
+        {"name": this.data.database.id, "link": Databases.databaseUrl(this.data.database)}
+      ];
+    },
 
-        crumbs: [
-          {"name": "Databases", "link": "/_all_dbs"},
-          {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-          {"name": docID, "link": "#"}
-        ],
+    allDocs: function(event) {
+      var docOptions;
 
-        views: {
-          "#dashboard-content": new Documents.Views.Doc({
-            model: data.doc
-          }),
+      docOptions = app.getParams(event.attr);
+      docOptions.include_docs = true;
 
-          "#sidebar-content": new Documents.Views.Sidebar({
-            collection: data.designDocs
-          })
-        },
+      this.data.database.buildAllDocs(docOptions);
+      this.sidebar.setSelectedTab('all-docs');
 
-        apiUrl: data.doc.url()
-      };
+      this.documentsView = this.setView("#dashboard-content", new Documents.Views.AllDocsList({
+        collection: this.data.database.allDocs
+      }));
     },
 
-    "database/:database/_all_docs(:extra)": function(databaseName, page) {
-      var data = {
-        database: new Databases.Model({id:databaseName})
-      };
-      data.designDocs = new Documents.AllDocs(null, {
-        database: data.database,
-        params: {startkey: '"_design"',
-          endkey: '"_design1"',
-          include_docs: true}
-      });
+    allDesignDocs: function(event) {
+      var docOptions = app.getParams(event.attr);
+      docOptions.include_docs = true;
 
-      var options = app.getParams();
-      options.include_docs = true;
-      data.database.buildAllDocs(options);
+      this.data.database.buildAllDocs(docOptions);
+      this.sidebar.setSelectedTab('design-docs');
 
-      return {
-        layout: "with_tabs_sidebar",
+      this.documentsView = this.setView("#dashboard-content", new Documents.Views.AllDocsList({
+        collection: this.data.database.allDocs
+      }));
+    },
 
-        data: data,
+    route: function (route, routeArgs) {
+      console.log('ROUTE ARG ', arguments);
+
+      if (route === 'database/:database/_design/:ddoc/_view/:view') {
+
+        if (!this.docCrumbs && !this.docApiUrl) {
+          // Save the old crumbs and API. Easier to do it here than in each event
+          this.docCrumbs = this.crumbs;
+          this.docApiUrl = this.apiUrl;
+        }
+
+        this.routeArgs = {
+          designDoc: routeArgs[1],
+          view: routeArgs[2].replace(/\?.*$/,'')
+        };
+      } else {
+        this.routeArgs = {};
+        if (this.docCrumbs && this.docApiUrl) {
+          this.crumbs = this.docCrumbs;
+          this.docApiUrl = this.apiUrl;
+        }
+      }
+    },
 
-        crumbs: [
-          {"name": "Databases", "link": "/_all_dbs"},
-          {"name": data.database.id, "link": Databases.databaseUrl(data.database)}
-        ],
+    viewFn: function (event) {
+      var view = this.routeArgs.view,
+      ddoc = this.routeArgs.designDoc,
+      params = app.getParams(event.attr);
 
-        views: {
-          "#dashboard-content": new Documents.Views.AllDocsList({
-            collection: data.database.allDocs
-          }),
+      console.log('PARAMS', params);
 
-          "#sidebar-content": new Documents.Views.Sidebar({
-            collection: data.designDocs
-          }),
+      this.data.indexedDocs = new Documents.IndexCollection(null, {
+        database: this.data.database,
+        design: ddoc,
+        view: view,
+        params: params
+      });
 
-          "#tabs": new Documents.Views.Tabs({
-            collection: data.designDocs,
-            database: data.database
-          })
-        },
+      var ddocInfo = {
+        id: "_design/" + ddoc,
+        currView: view,
+        designDocs: this.data.designDocs
+      };
 
-        apiUrl: data.database.allDocs.url()
+      this.setView("#dashboard-content", new Documents.Views.AllDocsList({
+        collection: this.data.indexedDocs,
+        nestedView: Documents.Views.Row,
+        viewList: true,
+        ddocInfo: ddocInfo,
+        params: params
+      }));
+
+      this.crumbs = function () {
+        return [
+          {"name": "Databases", "link": "/_all_dbs"},
+          {"name": this.data.database.id, "link": Databases.databaseUrl(this.data.database)},
+          {"name": ddoc + "/" + view, "link": this.data.indexedDocs.url()}
+        ];
       };
+
+      // TODO: change to view URL
+      this.apiUrl = this.data.indexedDocs.url();
     },
 
-    "database/:database/_changes(:params)": function(databaseName, params) {
-      var data = {
-        database: new Databases.Model({id:databaseName})
-      };
+    newViewEditor: function (event) {
+      // TODO: Get this working
+      this.setView("#dashboard-content", new Documents.Views.ViewEditor({
+        model: this.data.database,
+        ddocs: this.data.designDocs
+      }));
 
-      var options = app.getParams();
-      data.database.buildChanges(options);
+    },
 
-      return {
-        layout: "with_tabs",
+    routes: ["database/:database/_all_docs(:extra)", "database/:database/_design/:ddoc/_view/:view", "database/:database/new_view"],
 
-        data: data,
+    apiUrl: function() {
+      return this.data.database.allDocs.url();
+    }
+  });
 
-        crumbs: [
-          {"name": "Databases", "link": "/_all_dbs"},
-          {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-          {"name": "_changes", "link": "/_changes"}
-        ],
-
-        views: {
-          "#dashboard-content": new Documents.Views.Changes({
-            model: data.database
-          }),
-
-          "#tabs": new Documents.Views.Tabs({
-            collection: data.designDocs,
-            database: data.database,
-            active_id: 'changes'
-          })
-        },
-
-        apiUrl: data.database.changes.url()
-      };
+
+
+  var ChangesRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_tabs",
+
+    crumbs: function () {
+      return [
+        {"name": "Databases", "link": "/_all_dbs"},
+        {"name": this.database.id, "link": Databases.databaseUrl(this.database)},
+        {"name": "_changes", "link": "/_changes"}
+      ];
     },
 
-    "database/:database/new": newDocCodeEditorCallback,
-    "database/:database/new_view": newViewEditorCallback,
-
-    // TODO: fix optional search params
-    // Can't get ":view(?*search)" to work
-    // However ":view?*search" does work
-    //"database/:database/_design/:ddoc/_view/:view(\?*options)": function(databaseName, ddoc, view, options) {
-    "database/:database/_design/:ddoc/_view/:view": function(databaseName, ddoc, view, options) {
-      // hack around backbone router limitations
-      view = view.replace(/\?.*$/,'');
-      var params = app.getParams();
-      var data = {
-        database: new Databases.Model({id:databaseName})
-      };
+    routes: ["database/:database/_changes(:params)"],
 
-      data.indexedDocs = new Documents.IndexCollection(null, {
-        database: data.database,
-        design: ddoc,
-        view: view,
-        params: params
-      });
+    events: {
+      "route:_changes": "changes"
+    },
 
-      data.designDocs = new Documents.AllDocs(null, {
-        database: data.database,
-        params: {startkey: '"_design"',
-          endkey: '"_design1"',
-          include_docs: true}
-      });
+    defaultRoute: "changes",
 
-      var ddocInfo = {
-        id: "_design/" + ddoc,
-        currView: view,
-        designDocs: data.designDocs
-      };
+    initialize: function (options) {
+      this.databaseName = options[0];
+      this.database = new Databases.Model({id: this.databaseName});
 
-      return {
-        layout: "with_tabs_sidebar",
-
-        data: data,
-        // TODO: change dashboard-content
-        views: {
-          "#dashboard-content": new Documents.Views.AllDocsList({
-            collection: data.indexedDocs,
-            nestedView: Documents.Views.Row,
-            viewList: true,
-            ddocInfo: ddocInfo,
-            params: params
-          }),
-
-          "#sidebar-content": new Documents.Views.Sidebar({
-            collection: data.designDocs,
-            ddocInfo: ddocInfo
-          }),
-
-          "#tabs": new Documents.Views.Tabs({
-            collection: data.designDocs,
-            database: data.database
-          })
-        },
-
-        crumbs: [
-          {"name": "Databases", "link": "/_all_dbs"},
-          {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-          {"name": ddoc + "/" + view, "link": data.indexedDocs.url()}
-        ],
-        // TODO: change to view URL
-        apiUrl: data.indexedDocs.url()
-      };
+      var docOptions = app.getParams();
+
+      this.database.buildChanges(docOptions);
+
+      this.setView("#tabs", new Documents.Views.Tabs({
+        collection: this.designDocs,
+        database: this.database,
+        active_id: 'changes'
+      }));
+    },
+
+    changes: function (event) {
+      this.setView("#dashboard-content", new Documents.Views.Changes({
+        model: this.database
+      }));
+    },
+
+    apiUrl: function() {
+      return this.database.changes.url();
     }
+
+  });
+
+  /* Documents.Routes = {
+     "database/:database/_design%2F:doc": function(database, doc) {
+     var docID = "_design/"+doc;
+     return codeEditorCallback(database, docID);
+     },
+
+
+
+     "database/:database/new": newDocCodeEditorCallback,
+     "database/:database/new_view": newViewEditorCallback,
+
+  // TODO: fix optional search params
+  // Can't get ":view(?*search)" to work
+  // However ":view?*search" does work
+  //"database/:database/_design/:ddoc/_view/:view(\?*options)": function(databaseName, ddoc, view, options) {
+  "database/:database/_design/:ddoc/_view/:view": function(databaseName, ddoc, view, options) {
+// hack around backbone router limitations
+view = view.replace(/\?.*$/,'');
+var params = app.getParams();
+var data = {
+database: new Databases.Model({id:databaseName})
+};
+
+data.indexedDocs = new Documents.IndexCollection(null, {
+database: data.database,
+design: ddoc,
+view: view,
+params: params
+});
+
+data.designDocs = new Documents.AllDocs(null, {
+database: data.database,
+params: {startkey: '"_design"',
+endkey: '"_design1"',
+include_docs: true}
+});
+
+var ddocInfo = {
+id: "_design/" + ddoc,
+currView: view,
+designDocs: data.designDocs
+};
+
+return {
+layout: "with_tabs_sidebar",
+
+data: data,
+  // TODO: change dashboard-content
+views: {
+"#dashboard-content": new Documents.Views.AllDocsList({
+collection: data.indexedDocs,
+nestedView: Documents.Views.Row,
+viewList: true,
+ddocInfo: ddocInfo,
+params: params
+}),
+
+"#sidebar-content": new Documents.Views.Sidebar({
+collection: data.designDocs,
+ddocInfo: ddocInfo
+}),
+
+"#tabs": new Documents.Views.Tabs({
+collection: data.designDocs,
+database: data.database
+})
+},
+
+crumbs: [
+{"name": "Databases", "link": "/_all_dbs"},
+{"name": data.database.id, "link": Databases.databaseUrl(data.database)},
+{"name": ddoc + "/" + view, "link": data.indexedDocs.url()}
+],
+  // TODO: change to view URL
+  apiUrl: data.indexedDocs.url()
   };
+}
+};*/
+
+  Documents.RouteObjects = [DocEditorRouteObject, DocumentsRouteObject, ChangesRouteObject];
 
   return Documents;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/modules/documents/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/views.js b/src/fauxton/app/modules/documents/views.js
index 558f0c1..6ac9b44 100644
--- a/src/fauxton/app/modules/documents/views.js
+++ b/src/fauxton/app/modules/documents/views.js
@@ -163,6 +163,12 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       });
     },
 
+    updateSelected: function (selected) {
+      this.selected = selected;
+      this.$('.active').removeClass('active');
+      this.$('#'+this.selected).addClass('active');
+    },
+
     serialize: function() {
       var selected = this.selected;
       return {
@@ -390,6 +396,7 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
       var fragment = window.location.hash.replace(/\?.*$/, '');
       fragment = fragment + '?' + $.param(params);
       FauxtonAPI.navigate(fragment);
+      FauxtonAPI.triggerRouteEvent('view_fn', params);
     },
 
     updateFilters: function(event) {
@@ -921,6 +928,11 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
           }
         }
       }, this);
+    },
+
+    setSelectedTab: function (selectedTab) {
+      this.$('li').removeClass('active');
+      this.$('#' + selectedTab).parent().addClass('active');
     }
 
   });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/modules/fauxton/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/fauxton/base.js b/src/fauxton/app/modules/fauxton/base.js
index 382c852..3bcae5d 100644
--- a/src/fauxton/app/modules/fauxton/base.js
+++ b/src/fauxton/app/modules/fauxton/base.js
@@ -11,8 +11,7 @@
 // the License.
 
 define([
-       "app",
-
+  "app",
        // Libs
        "backbone"
 
@@ -148,5 +147,30 @@ function(app, Backbone) {
     }
   });
 
+  Fauxton.Pagination = Backbone.View.extend({
+    template: "templates/fauxton/pagination",
+
+    initialize: function(options) {
+      this.page = options.page;
+      this.perPage = options.perPage;
+      this.total = options.total;
+      this.totalPages = Math.ceil(this.total / this.perPage);
+      this.urlFun = options.urlFun;
+      this.routeEvent = options.routeEvent;
+
+    },
+
+    serialize: function() {
+      return {
+        page: this.page,
+        perPage: this.perPage,
+        total: this.total,
+        totalPages: this.totalPages,
+        urlFun: this.urlFun,
+        routeEvent: this.routeEvent
+      };
+    }
+  });
+
   return Fauxton;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/router.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/router.js b/src/fauxton/app/router.js
index 77cc36b..09b2338 100644
--- a/src/fauxton/app/router.js
+++ b/src/fauxton/app/router.js
@@ -100,17 +100,32 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
   var Router = app.router = Backbone.Router.extend({
     routes: {},
 
-    // These moduleRoutes functions are aguably better outside but
-    // need access to the Router instance which is not created in this
-    // module
-    addModuleRoute: function(generator, route) {
-      this.route(route, route.toString(), generateRoute(generator, route));
+    addModuleRouteObject: function(RouteObject) {
+      var self = this;
+      var masterLayout = this.masterLayout;
+
+      _.each(RouteObject.prototype.get('routes'), function(route) {
+        this.route(route, route.toString(), function() {
+          var args = Array.prototype.slice.call(arguments);
+
+          if (self.activeRouteObject && self.activeRouteObject.get('routes').indexOf(route) > -1) {
+            //Don't need to do anything here as this route has been initialised
+            self.activeRouteObject.route.call(self.activeRouteObject, route, args);
+            console.log('Avoiding Route creation');
+            return;
+          }
+
+          self.activeRouteObject = new RouteObject(args);
+          self.activeRouteObject[self.activeRouteObject.defaultRoute].apply(self.activeRouteObject, args);
+          self.activeRouteObject.render(route, masterLayout, args);
+        });
+      }, this);
     },
 
     setModuleRoutes: function() {
       _.each(modules, function(module) {
         if (module){
-          _.each(module.Routes, this.addModuleRoute, this);
+          _.each(module.RouteObjects, this.addModuleRouteObject, this);
         }
       }, this);
       _.each(LoadAddons.addons, function(module) {
@@ -118,7 +133,7 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
           module.initialize();
           // This is pure routes the addon provides
           if (module.Routes) {
-            _.each(module.Routes, this.addModuleRoute, this);
+            _.each(module.RouteObjects, this.addModuleRouteObject, this);
           }
         }
       }, this);
@@ -156,6 +171,15 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
       this.masterLayout.render();
 
       app.footer.render();
+    },
+
+    triggerRouteEvent: function(event, args) {
+      if (this.activeRouteObject) {
+        var eventArgs = [event].concat(args);
+        console.log("CALLING ROUTE EVENT ON", this.activeRouteObject, arguments);
+        this.activeRouteObject.trigger.apply(this.activeRouteObject, eventArgs );
+        this.activeRouteObject.renderWith(eventArgs, this.masterLayout, args);
+      }
     }
   });
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/templates/databases/list.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/databases/list.html b/src/fauxton/app/templates/databases/list.html
index ba871b5..808c950 100644
--- a/src/fauxton/app/templates/databases/list.html
+++ b/src/fauxton/app/templates/databases/list.html
@@ -27,3 +27,4 @@ the License.
   <tbody>
   </tbody>
 </table>
+<div id="database-pagination"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/documents/doc_field_editor_tabs.html b/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
index ecb0e48..b094e0e 100644
--- a/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
+++ b/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
@@ -13,8 +13,8 @@ the License.
 -->
 
 <ul class="nav nav-tabs">
-  <!--<li class="<%= isSelectedClass('field_editor') %>"><a href="#<%= doc.url('app') %>/field_editor">Doc fields</a></li>-->
-  <li class="<%= isSelectedClass('code_editor') %>"><a href="#<%= doc.url('app') %>/code_editor"><i class="icon-pencil"></i> Code editor</a></li>
+  <li id="field_editor" class="<%= isSelectedClass('field_editor') %>"><a route-event="field_editor" href="#<%= doc.url('app') %>/field_editor">Doc fields</a></li>
+  <li id="code_editor" class="<%= isSelectedClass('code_editor') %>"><a route-event="code_editor" href="#<%= doc.url('app') %>/code_editor"><i class="icon-pencil"></i> Code editor</a></li>
   <ul class="nav pull-right" style="margin:5px 10px 0px 10px;">
     <li>
       <div class="btn-group">

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/templates/documents/index_menu_item.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/documents/index_menu_item.html b/src/fauxton/app/templates/documents/index_menu_item.html
index bb16b4f..2dac868 100644
--- a/src/fauxton/app/templates/documents/index_menu_item.html
+++ b/src/fauxton/app/templates/documents/index_menu_item.html
@@ -12,6 +12,6 @@ License for the specific language governing permissions and limitations under
 the License.
 -->
 
-<a href="#database/<%= database %>/_design/<%= ddoc %>/_view/<%= index %>" class="toggle-view">
+<a route-event="view_fn" href="#database/<%= database %>/_design/<%= ddoc %>/_view/<%= index %>" class="toggle-view">
   <i class="icon-list"></i> <%= ddoc %><span class="divider">/</span><%= index %>
-</a>
\ No newline at end of file
+</a>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/templates/documents/sidebar.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/documents/sidebar.html b/src/fauxton/app/templates/documents/sidebar.html
index 40daec8..a8841a2 100644
--- a/src/fauxton/app/templates/documents/sidebar.html
+++ b/src/fauxton/app/templates/documents/sidebar.html
@@ -19,11 +19,11 @@ the License.
   </div>
   <hr>
   <ul class="nav nav-list">
-    <li class="active"><a id="all-docs" href="#<%= database.url('index') %>?limit=100" class="toggle-view"><i class="icon-list"></i> All documents</a></li>
-    <li><a id="design-docs" href='#<%= database.url("index") %>?limit=100&startkey="_design"&endkey="_e"'  class="toggle-view"><i class="icon-list"></i> All design docs</a></li>
+    <li class="active"><a route-event="all_docs" id="all-docs" href="#<%= database.url('index') %>?limit=100" class="toggle-view"><i class="icon-list"></i> All documents</a></li>
+    <li><a route-event="all_design_docs" id="design-docs" href='#<%= database.url("index") %>?limit=100&startkey="_design"&endkey="_e"'  class="toggle-view"><i class="icon-list"></i> All design docs</a></li>
   </ul>
   <ul class="nav nav-list views">
     <li class="nav-header">Secondary Indexes</li>
-    <li><a href="#<%= database.url('app') %>/new_view" class="new"><i class="icon-plus"></i> New</a></li>
+    <li><a route-event="new_view" href="#<%= database.url('app') %>/new_view" class="new"><i class="icon-plus"></i> New</a></li>
   </ul>
 </div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/templates/fauxton/pagination.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/fauxton/pagination.html b/src/fauxton/app/templates/fauxton/pagination.html
new file mode 100644
index 0000000..2ec5ebe
--- /dev/null
+++ b/src/fauxton/app/templates/fauxton/pagination.html
@@ -0,0 +1,17 @@
+<div class="pagination pagination-centered">
+  <ul>
+    <% if (page > 1) { %>
+    <li><a route-event="<%= routeEvent %>" href="<%= urlFun(page-1) %>">&laquo;</a></li>
+    <% } else { %>
+      <li class="disabled"><a a route-event="<%= routeEvent %>" href="<%= urlFun(page) %>">&laquo;</a></li>
+    <% } %>
+    <% _.each(_.range(1, totalPages+1), function(i) { %>
+      <li <% if (page == i) { %>class="active"<% } %>><a route-event="<%= routeEvent %>" href="<%= urlFun(i) %>"><%= i %></a></li>
+    <% }) %>
+    <% if (page < totalPages) { %>
+      <li><a route-event="<%= routeEvent %>" href="<%= urlFun(page+1) %>">&raquo;</a></li>
+    <% } else { %>
+      <li class="disabled"><a route-event="<%= routeEvent %>" href="<%= urlFun(page) %>">&raquo;</a></li>
+    <% } %>
+  </ul>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/app/templates/layouts/with_tabs.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/layouts/with_tabs.html b/src/fauxton/app/templates/layouts/with_tabs.html
index 432a68d..36b39c8 100644
--- a/src/fauxton/app/templates/layouts/with_tabs.html
+++ b/src/fauxton/app/templates/layouts/with_tabs.html
@@ -13,7 +13,7 @@ the License.
 -->
 
 <div id="primary-navbar"></div>
-<div id="dashboard" class="container">
+<div id="dashboard" class="container-fluid">
 
 <div class="row-fluid">
   <div id="breadcrumbs" class="row-fluid"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4642d5db/src/fauxton/assets/index.underscore
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/index.underscore b/src/fauxton/assets/index.underscore
index 2b5c009..e29b4c4 100644
--- a/src/fauxton/assets/index.underscore
+++ b/src/fauxton/assets/index.underscore
@@ -19,6 +19,7 @@
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
   <meta name="viewport" content="width=device-width,initial-scale=1">
+  <meta http-equiv="Content-Language" content="en" />
 
   <title>Project Fauxton</title>
 


Mime
View raw message