couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gar...@apache.org
Subject [5/5] fauxton commit: updated refs/heads/master to 2a7fc64
Date Tue, 20 Jan 2015 08:03:46 GMT
Add NavBar done in react

The navbar completly redone in react.js


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

Branch: refs/heads/master
Commit: 2a7fc648ece2501b22d91d3e0034ad36bb470950
Parents: 66425e8
Author: Garren Smith <garren.smith@gmail.com>
Authored: Mon Jan 19 12:34:30 2015 +0200
Committer: Garren Smith <garren.smith@gmail.com>
Committed: Tue Jan 20 10:02:54 2015 +0200

----------------------------------------------------------------------
 app/addons/auth/base.js                         |  83 ++++---
 app/addons/auth/resources.js                    |  19 +-
 app/addons/auth/templates/nav_link_title.html   |  32 ---
 app/addons/auth/test/baseSpec.js                |  71 +++++-
 app/addons/databases/base.js                    |   9 +
 app/addons/fauxton/actions.js                   |  56 +++++
 app/addons/fauxton/actiontypes.js               |  24 ++
 app/addons/fauxton/base.js                      | 176 ++------------
 app/addons/fauxton/components.js                |   1 +
 app/addons/fauxton/components.react.jsx         | 160 +++++++++++++
 app/addons/fauxton/stores.js                    | 190 +++++++++++++++
 app/addons/fauxton/templates/footer.html        |  15 --
 app/addons/fauxton/templates/nav_bar.html       |  76 ------
 .../fauxton/tests/componentsSpec.react.jsx      |  47 ++++
 app/addons/fauxton/tests/navbarSpec.js          | 107 ---------
 app/addons/fauxton/tests/storeSpec.js           | 237 +++++++++++++++++++
 app/app.js                                      |  31 ++-
 app/config.js                                   |   3 +
 app/constants.js                                |   5 +-
 app/core/api.js                                 |  22 +-
 app/core/layout.js                              |   3 +-
 app/core/router.js                              |   2 +-
 app/templates/layouts/doc_editor.html           |  20 +-
 app/templates/layouts/one_pane.html             |  29 +--
 app/templates/layouts/two_pane.html             |   3 -
 app/templates/layouts/with_sidebar.html         |   3 -
 app/templates/layouts/with_tabs.html            |   2 -
 app/templates/layouts/with_tabs_sidebar.html    |  44 ++--
 assets/index.underscore                         |   6 +-
 assets/less/animations.less                     |  12 +
 assets/less/fauxton.less                        |   5 +-
 assets/less/templates.less                      |   6 +-
 32 files changed, 967 insertions(+), 532 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/auth/base.js
----------------------------------------------------------------------
diff --git a/app/addons/auth/base.js b/app/addons/auth/base.js
index a482901..52c443f 100644
--- a/app/addons/auth/base.js
+++ b/app/addons/auth/base.js
@@ -11,9 +11,9 @@
 // the License.
 
 define([
-       "app",
-       "api",
-       "addons/auth/routes"
+  "app",
+  "api",
+  "addons/auth/routes"
 ],
 
 function(app, FauxtonAPI, Auth) {
@@ -24,17 +24,60 @@ function(app, FauxtonAPI, Auth) {
 
   Auth.initialize = function() {
 
-    Auth.navLink = new Auth.NavLink({model: Auth.session});
-
     FauxtonAPI.addHeaderLink({
-      title: "Auth",
-      href: "#_auth",
-      view: Auth.navLink,
+      id: "auth",
+      title: "Login", 
+      href: "#login",
       icon: "fonticon-user",
       bottomNav: true,
-      establish: [FauxtonAPI.session.fetchUser()]
     });
 
+    Auth.session.on('change', function () {
+      var session = Auth.session;
+      var link = {};
+
+      if (session.isAdminParty()) {
+        link = {
+          id: "auth",
+          title: "Admin Party!", 
+          href: "#createAdmin",
+          icon: "fonticon-user",
+          bottomNav: true,
+        };
+      } else if (session.isLoggedIn()) {
+        link = {
+          id: "auth",
+          title: session.user().name, 
+          href: "#changePassword",
+          icon: "fonticon-user",
+          bottomNav: true,
+        };
+
+        FauxtonAPI.addHeaderLink({
+          id: 'logout',
+          footerNav: true, 
+          href: "#logout", 
+          title: "Logout", 
+          icon: "", 
+          className: 'logout'
+        });
+      } else {
+        link = {
+          id: "auth",
+          title: 'Login', 
+          href: "#login",
+          icon: "fonticon-user",
+          bottomNav: true,
+        };
+        FauxtonAPI.removeHeaderLink({id: "logout", footerNav: true});
+      }
+      FauxtonAPI.updateHeaderLink(link);
+
+    });
+
+    Auth.session.fetchUser().then(function () {
+      Auth.session.trigger('change');
+    });
 
     var auth = function (session, roles) {
       var deferred = $.Deferred();
@@ -59,28 +102,6 @@ function(app, FauxtonAPI, Auth) {
 
     FauxtonAPI.auth.registerAuth(auth);
     FauxtonAPI.auth.registerAuthDenied(authDenied);
-
-
-    var addLogoutLink = function () {
-      FauxtonAPI.addHeaderLink({footerNav: true, href: "#logout", title: "Logout", icon: "", className: 'logout'});
-    };
-
-    var removeLogoutLink = function () {
-      FauxtonAPI.removeHeaderLink({title: "Logout", footerNav: true});
-    };
-
-    if (FauxtonAPI.session.isLoggedIn()) {
-      addLogoutLink();
-    }
-
-    FauxtonAPI.session.on('change', function () {
-      if (FauxtonAPI.session.isLoggedIn()) {
-        addLogoutLink();
-      } else {
-        removeLogoutLink();
-      }
-    });
-
   };
 
   return Auth;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/auth/resources.js
----------------------------------------------------------------------
diff --git a/app/addons/auth/resources.js b/app/addons/auth/resources.js
index 26609a9..ff8ffc8 100644
--- a/app/addons/auth/resources.js
+++ b/app/addons/auth/resources.js
@@ -332,22 +332,6 @@ function (app, FauxtonAPI, CouchdbSession) {
     }
   });
 
-  Auth.NavLink = FauxtonAPI.View.extend({
-    template: 'addons/auth/templates/nav_link_title',
-    tagName: 'li',
-
-    beforeRender: function () {
-      this.listenTo(this.model, 'change', this.render);
-    },
-
-    serialize: function () {
-      return {
-        admin_party: this.model.isAdminParty(),
-        user: this.model.user()
-      };
-    }
-  });
-
   Auth.NavDropDown = FauxtonAPI.View.extend({
     template: 'addons/auth/templates/nav_dropdown',
 
@@ -386,7 +370,8 @@ function (app, FauxtonAPI, CouchdbSession) {
 
     serialize: function () {
       return {
-        urlBack: this.urlBack
+        urlBack: this.urlBack,
+        user: FauxtonAPI.session.user()
       };
     }
   });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/auth/templates/nav_link_title.html
----------------------------------------------------------------------
diff --git a/app/addons/auth/templates/nav_link_title.html b/app/addons/auth/templates/nav_link_title.html
deleted file mode 100644
index db3587d..0000000
--- a/app/addons/auth/templates/nav_link_title.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
-Licensed under the Apache License, Version 2.0 (the "License"); you may not
-use this file except in compliance with the License. You may obtain a copy of
-the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-License for the specific language governing permissions and limitations under
-the License.
--->
-<% if (admin_party) { %>
-  <a id="user-create-admin" class="alert_nav" href="#createAdmin">
-  	<span class="fonticon-user fonticon"></span>
-  	Admin Party! <br>[FIX THIS]<br>
-    <small>Everyone is an admin.</small>
-  </a>
-<% } else if (user) { %>
-  <a  href="#changePassword" >
-  	<span class="fonticon-user fonticon"></span>
-  	<%- user.name %>
-	</a>
-<% } else { %>
-  <a  href="#login" >
-  	<span class="fonticon-user fonticon"></span>
-  	Login
-  </a>
-<% } %>
-
-

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/auth/test/baseSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/auth/test/baseSpec.js b/app/addons/auth/test/baseSpec.js
index 177ec30..cc5e83f 100644
--- a/app/addons/auth/test/baseSpec.js
+++ b/app/addons/auth/test/baseSpec.js
@@ -15,8 +15,7 @@ define([
       'core/auth',
       'testUtils'
 ], function (FauxtonAPI, Base, Auth, testUtils) {
-  var assert = testUtils.assert,
-      ViewSandbox = testUtils.ViewSandbox;
+  var assert = testUtils.assert;
 
   describe("Auth: Login", function () {
 
@@ -32,4 +31,72 @@ define([
       });
     });
   });
+
+  describe('auth session change', function () {
+
+    afterEach(function () {
+      FauxtonAPI.updateHeaderLink.restore && FauxtonAPI.updateHeaderLink.restore();
+      FauxtonAPI.session.isAdminParty.restore && FauxtonAPI.session.isAdminParty.restore();
+    });
+
+    it('for admin party changes title to admin party', function () {
+      var spy = sinon.spy(FauxtonAPI, 'updateHeaderLink');
+      var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(true);
+      FauxtonAPI.session.trigger('change');
+
+      assert.ok(spy.calledOnce);
+      var args = spy.getCall(0).args[0];
+
+      assert.ok(args.title.match(/Admin Party/));
+      FauxtonAPI.session.isAdminParty.restore();
+    });
+
+    it('for login changes title to login', function () {
+      var spy = sinon.spy(FauxtonAPI, 'updateHeaderLink');
+      var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
+      sinon.stub(FauxtonAPI.session, 'user').returns({name: 'test-user'});
+      sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(true);
+      FauxtonAPI.session.trigger('change');
+
+      assert.ok(spy.calledOnce);
+      var args = spy.getCall(0).args[0];
+
+      assert.equal(args.title, 'test-user');
+      FauxtonAPI.session.isLoggedIn.restore();
+      FauxtonAPI.session.user.restore();
+      FauxtonAPI.session.isAdminParty.restore();
+    });
+
+    it('for login adds logout link', function () {
+      var spy = sinon.spy(FauxtonAPI, 'addHeaderLink');
+      var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
+      sinon.stub(FauxtonAPI.session, 'user').returns({name: 'test-user'});
+      sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(true);
+      FauxtonAPI.session.trigger('change');
+
+      assert.ok(spy.calledOnce);
+      var args = spy.getCall(0).args[0];
+
+      assert.equal(args.title, 'Logout');
+      FauxtonAPI.session.isLoggedIn.restore();
+      FauxtonAPI.session.user.restore();
+      FauxtonAPI.session.isAdminParty.restore();
+    });
+
+    it('for logout, removes logout link', function () {
+      var spy = sinon.spy(FauxtonAPI, 'removeHeaderLink');
+      var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
+      sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(false);
+      FauxtonAPI.session.trigger('change');
+
+      assert.ok(spy.calledOnce);
+      var args = spy.getCall(0).args[0];
+
+      assert.equal(args.id, 'logout');
+      FauxtonAPI.session.isLoggedIn.restore();
+      FauxtonAPI.session.isAdminParty.restore();
+    });
+
+
+  });
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/databases/base.js
----------------------------------------------------------------------
diff --git a/app/addons/databases/base.js b/app/addons/databases/base.js
index ea1719c..e1aa846 100644
--- a/app/addons/databases/base.js
+++ b/app/addons/databases/base.js
@@ -25,6 +25,15 @@ define([
 function(app, FauxtonAPI, Databases, Views) {
   Databases.Views = Views;
 
+  Databases.initialize = function () {
+    FauxtonAPI.addHeaderLink({
+      href:"#/_all_dbs", 
+      title:"Databases", 
+      icon: "fonticon-database", 
+      className: 'databases'
+    });
+  };
+
   // Utility functions
   Databases.databaseUrl = function(database) {
     var name = _.isObject(database) ? database.id : database,

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/actions.js b/app/addons/fauxton/actions.js
new file mode 100644
index 0000000..74eaeef
--- /dev/null
+++ b/app/addons/fauxton/actions.js
@@ -0,0 +1,56 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  'app',
+  'api',
+  'addons/fauxton/actiontypes'
+],
+function (app, FauxtonAPI, ActionTypes) {
+
+  return {
+    toggleNavbarMenu: function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.TOGGLE_NAVBAR_MENU 
+      });
+    },
+
+    addHeaderLink: function (link) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.ADD_NAVBAR_LINK,
+        link: link
+      });
+    },
+
+    removeHeaderLink: function(link) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.REMOVE_NAVBAR_LINK,
+        link: link
+      });
+    },
+
+    setNavbarVersionInfo: function (versionInfo) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.NAVBAR_SET_VERSION_INFO,
+        version: versionInfo
+      });
+    },
+
+    setNavbarActiveLink: function (header) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.NAVBAR_ACTIVE_LINK,
+        name: header
+      });
+    }
+  };
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/actiontypes.js b/app/addons/fauxton/actiontypes.js
new file mode 100644
index 0000000..ca85a21
--- /dev/null
+++ b/app/addons/fauxton/actiontypes.js
@@ -0,0 +1,24 @@
+// 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([], function() {
+  return {
+    ADD_NAVBAR_LINK: 'ADD_NAVBAR_LINK',
+    TOGGLE_NAVBAR_MENU: 'TOGGLE_NAVBAR_MENU',
+    UPDATE_NAVBAR_LINK: 'UPDATE_NAVBAR_LINK',
+    CLEAR_NAVBAR_LINK: 'CLEAR_NAVBAR_LINK',
+    REMOVE_NAVBAR_LINK: 'REMOVE_NAVBAR_LINK',
+    NAVBAR_SET_VERSION_INFO: 'NAVBAR_SET_VERSION_INFO',
+    NAVBAR_ACTIVE_LINK: 'NAVBAR_ACTIVE_LINK'
+  };
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/base.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/base.js b/app/addons/fauxton/base.js
index 44ad8ea..316ec55 100644
--- a/app/addons/fauxton/base.js
+++ b/app/addons/fauxton/base.js
@@ -14,10 +14,12 @@ define([
   "app",
   "api",
   "addons/fauxton/components",
+  "addons/fauxton/components.react",
+  "addons/fauxton/actions",
   "plugins/zeroclipboard/ZeroClipboard"
 ],
 
-function(app, FauxtonAPI, Components, ZeroClipboard) {
+function(app, FauxtonAPI, Components, ReactComponents, Actions, ZeroClipboard) {
 
   var Fauxton = FauxtonAPI.addon();
   FauxtonAPI.addNotification = function (options) {
@@ -48,26 +50,14 @@ function(app, FauxtonAPI, Components, ZeroClipboard) {
   });
 
   Fauxton.initialize = function () {
-    app.navBar = new Fauxton.NavBar();
     app.apiBar = new Components.ApiBar();
 
-    FauxtonAPI.when.apply(null, app.navBar.establish()).done(function() {
-      FauxtonAPI.masterLayout.setView("#primary-navbar", app.navBar, true);
-      FauxtonAPI.masterLayout.setView("#api-navbar", app.apiBar, true);
-      app.navBar.render();
-      app.apiBar.render();
-    });
-
-    FauxtonAPI.masterLayout.navBar = app.navBar;
+    FauxtonAPI.masterLayout.setView("#api-navbar", app.apiBar, true);
+    app.apiBar.render();
     FauxtonAPI.masterLayout.apiBar = app.apiBar;
 
     FauxtonAPI.RouteObject.on('beforeFullRender', function (routeObject) {
-      $('#primary-navbar li').removeClass('active');
-
-      if (routeObject.selectedHeader) {
-        app.selectedHeader = routeObject.selectedHeader;
-        $('#primary-navbar li[data-nav-name="' + routeObject.selectedHeader + '"]').addClass('active');
-      }
+      Actions.setNavbarActiveLink(routeObject.selectedHeader);
     });
 
     FauxtonAPI.RouteObject.on('beforeEstablish', function (routeObject) {
@@ -93,6 +83,17 @@ function(app, FauxtonAPI, Components, ZeroClipboard) {
         masterLayout.apiBar.hide();
       }
     });
+
+    var primaryNavBarEl = $('#primary-navbar')[0];
+    if (primaryNavBarEl) {
+      ReactComponents.renderNavBar(primaryNavBarEl);
+    }
+
+    var versionInfo = new Fauxton.VersionInfo();
+
+    versionInfo.fetch().then(function () {
+      Actions.setNavbarVersionInfo(versionInfo.get("version"));
+    });
   };
   
   Fauxton.VersionInfo = Backbone.Model.extend({
@@ -101,149 +102,6 @@ function(app, FauxtonAPI, Components, ZeroClipboard) {
     }
   });
 
-  Fauxton.Footer = FauxtonAPI.View.extend({
-    tagName: "p",
-    template: "addons/fauxton/templates/footer",
-
-    initialize: function() {
-      this.versionInfo = new Fauxton.VersionInfo();
-    },
-
-    establish: function() {
-      return [this.versionInfo.fetch()];
-    },
-
-    serialize: function() {
-      return {
-        version: this.versionInfo.get("version")
-      };
-    }
-  });
-
-  Fauxton.NavBar = FauxtonAPI.View.extend({
-    className:"navbar",
-    template: "addons/fauxton/templates/nav_bar",
-
-    events:  {
-      "click .burger" : "toggleMenu"
-    },
-
-    toggleMenu: function(){
-       var $selectorList = $('body');
-      var minimized = !$selectorList.hasClass('closeMenu');
-      this.setState(minimized);
-       $selectorList.toggleClass('closeMenu');
-       FauxtonAPI.Events.trigger(FauxtonAPI.constants.EVENTS.BURGER_CLICKED, { minimized: minimized });
-    },
-
-    // TODO: can we generate this list from the router?
-    navLinks: [
-      {href:"#/_all_dbs", title:"Databases", icon: "fonticon-database", className: 'databases'}
-    ],
-
-    bottomNavLinks: [],
-    footerNavLinks: [],
-
-    initialize: function () {
-      _.bindAll(this);
-
-      FauxtonAPI.extensions.on('add:navbar:addHeaderLink', this.addLink);
-      FauxtonAPI.extensions.on('removeItem:navbar:addHeaderLink', this.removeLink);
-      this.versionFooter = new Fauxton.Footer({});
-
-      // if needed, minimize the sidebar
-      if (this.isMinimized()) {
-        $('body').addClass('closeMenu');
-      }
-    },
-
-    serialize: function() {
-      return {
-        navLinks: this.navLinks,
-        bottomNavLinks: this.bottomNavLinks,
-        footerNavLinks: this.footerNavLinks
-      };
-    },
-
-    establish: function(){
-      return [this.versionFooter.establish()];
-    },
-
-    addLink: function(link) {
-      // link.top means it gets pushed to the top of the array,
-      // link.bottomNav means it goes to the additional bottom nav
-      // link.footerNav means goes to the footer nav
-      if (link.top && !link.bottomNav){
-        this.navLinks.unshift(link);
-      } else if (link.top && link.bottomNav){
-        this.bottomNavLinks.unshift(link);
-      } else if (link.bottomNav) {
-        this.bottomNavLinks.push(link);
-      } else if (link.footerNav) {
-        this.footerNavLinks.push(link);
-      } else {
-        this.navLinks.push(link);
-      }
-    },
-
-    removeLink: function (removeLink) {
-      var links = this.navlinks;
-
-      if (removeLink.bottomNav) {
-        links = this.bottomNavLinks;
-      } else if (removeLink.footerNav) {
-        links = this.footerNavLinks;
-      }
-
-      var foundIndex = -1;
-
-      _.each(links, function (link, index) {
-        if (link.title === removeLink.title) {
-          foundIndex = index;
-        }
-      });
-
-      if (foundIndex === -1) {return;}
-      links.splice(foundIndex, 1);
-      this.render();
-    },
-
-    afterRender: function(){
-      $('#primary-navbar li[data-nav-name="' + app.selectedHeader + '"]').addClass('active');
-    },
-
-    beforeRender: function () {
-      this.insertView(".js-version", this.versionFooter);
-      this.addLinkViews();
-    },
-
-    addLinkViews: function () {
-      var that = this;
-
-      _.each(_.union(this.navLinks, this.bottomNavLinks), function (link) {
-        if (!link.view) { return; }
-
-        //TODO check if establish is a function
-        var establish = link.establish || [];
-        $.when.apply(null, establish).then( function () {
-          var selector =  link.bottomNav ? '#bottom-nav-links' : '#nav-links';
-          that.insertView(selector, link.view).render();
-        });
-      }, this);
-    },
-
-    setState: function (minimized) {
-      app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, minimized);
-    },
-
-    isMinimized: function () {
-      var isMinimized = app.utils.localStorageGet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED);
-      return (_.isUndefined(isMinimized)) ? false : isMinimized;
-    }
-
-    // TODO: ADD ACTIVE CLASS
-  });
-
   Fauxton.Notification = FauxtonAPI.View.extend({
     animationTimer: 5000,
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/components.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/components.js b/app/addons/fauxton/components.js
index 2915db3..b261c31 100644
--- a/app/addons/fauxton/components.js
+++ b/app/addons/fauxton/components.js
@@ -826,6 +826,7 @@ function(app, FauxtonAPI, ace, spin, ZeroClipboard) {
     cleanup: function () {
       $(window).off('beforeunload.editor');
       FauxtonAPI.removeBeforeUnload("editor");
+      this.editor.destroy();
     },
 
     setHeightToLineCount: function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/components.react.jsx b/app/addons/fauxton/components.react.jsx
new file mode 100644
index 0000000..f1c35fd
--- /dev/null
+++ b/app/addons/fauxton/components.react.jsx
@@ -0,0 +1,160 @@
+// 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([
+  "api",
+  "react",
+  "addons/fauxton/stores",
+  "addons/fauxton/actions"
+],
+
+function(FauxtonAPI, React, Stores, Actions) {
+  var navBarStore = Stores.navBarStore;
+
+  var Footer = React.createClass({
+    render: function () {
+      var version = this.props.version;
+
+      if (!version) { return null; }
+      return (
+        <div className="version-footer">
+          Fauxton on 
+          <a href="http://couchdb.apache.org/"> Apache CouchDB</a>
+          <br/> 
+          v. {version}
+        </div>
+      );
+    }
+  });
+
+  var Burger = React.createClass({
+    render: function () {
+      return (
+        <div className="burger" onClick={this.props.toggleMenu}>
+          <div></div>
+          <div></div>
+          <div></div>
+        </div>
+      );
+    }
+  });
+
+  var NavLink = React.createClass({
+    render: function () {
+      var link = this.props.link;
+      var liClassName = this.props.active === link.title ? 'active' : '';
+
+      return (
+        <li data-nav-name={link.title} className={liClassName} >
+          <a href={link.href} target={link.target ? '_blank' : ''} data-bypass={link.target ? 'true' : 'false'}>
+            <i className={link.icon + " fonticon "}></i>
+            <span dangerouslySetInnerHTML={{__html: link.title }} /> 
+          </a>
+        </li>
+      );
+    }
+  });
+
+  var NavBar = React.createClass({
+    getStoreState: function () {
+      return {
+        navLinks: navBarStore.getNavLinks(),
+        bottomNavLinks: navBarStore.getBottomNavLinks(),
+        footerNavLinks: navBarStore.getFooterNavLinks(),
+        activeLink: navBarStore.getActiveLink(),
+        version: navBarStore.getVersion(),
+        isMinimized: navBarStore.isMinimized()
+      };
+    },
+
+    getInitialState: function () {
+      return this.getStoreState();
+    },
+
+    createLinks: function (links) {
+      return _.map(links, function (link, i) {
+        return <NavLink key={i} link={link} active={this.state.activeLink} />;
+      }, this);
+    },
+
+    onChange: function () {
+      this.setState(this.getStoreState());
+    },
+
+    setMenuState: function () {
+      $('body').toggleClass('closeMenu', this.state.isMinimized);
+    },
+
+    componentDidMount: function () {
+      navBarStore.on('change', this.onChange, this);
+      this.setMenuState();
+    },
+
+    componentDidUpdate: function () {
+      this.setMenuState();
+    },
+
+    componentWillUnmount: function() {
+      navBarStore.off('change', this.onChange);
+    },
+
+    toggleMenu: function () {
+      Actions.toggleNavbarMenu();
+    },
+
+    render: function () {
+      var navLinks = this.createLinks(this.state.navLinks);
+      var bottomNavLinks = this.createLinks(this.state.bottomNavLinks);
+      var footerNavLinks = this.createLinks(this.state.footerNavLinks);
+
+      return (
+        <div className="navbar">
+          <Burger toggleMenu={this.toggleMenu}/>
+          <nav id="main_navigation">
+            <ul id="nav-links" className="nav">
+              {navLinks}
+            </ul>
+
+            <div id="bottom-nav">
+              <ul id="bottom-nav-links" className="nav">
+                {bottomNavLinks}
+              </ul>
+            </div>
+          </nav>
+          <div id="primary-nav-right-shadow"/>
+
+          <div className="bottom-container">
+            <div className="brand">
+              <div className="icon">Apache Fauxton</div>
+            </div>
+            <Footer version={this.state.version}/>
+            <div id="footer-links">
+              <ul id="footer-nav-links" className="nav">
+                {footerNavLinks}
+              </ul>
+            </div>
+          </div>
+        </div>
+      );
+    }
+  });
+
+
+  return {
+    renderNavBar: function (el) {
+      React.render(<NavBar/>, el);
+    },
+
+    Burger: Burger
+  };
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/stores.js b/app/addons/fauxton/stores.js
new file mode 100644
index 0000000..cc31477
--- /dev/null
+++ b/app/addons/fauxton/stores.js
@@ -0,0 +1,190 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  'app',
+  'api',
+  'addons/fauxton/actiontypes'
+],
+
+function(app, FauxtonAPI, ActionTypes) {
+  var Stores = {};
+
+  Stores.NavBarStore = FauxtonAPI.Store.extend({
+    initialize: function () {
+      this.reset();
+    },
+
+    reset: function () {
+      this.activeLink = null;
+      this.version = null;
+      this.navLinks = [];
+      this.footerNavLinks = [];
+      this.bottomNavLinks = [{
+        id: 'Documentation',
+        title: "Documentation", 
+        icon: "fonticon-bookmark",
+        href: app.helpers.getDocUrl('GENERAL'),
+        bottomNav: true,
+        top: true,
+        target: '_blank'
+      }];
+    },
+
+    addLink: function (link) {
+      if (link.top && !link.bottomNav){
+        this.navLinks.unshift(link);
+      } else if (link.top && link.bottomNav){
+        this.bottomNavLinks.unshift(link);
+      } else if (link.bottomNav) {
+        this.bottomNavLinks.push(link);
+      } else if (link.footerNav) {
+        this.footerNavLinks.push(link);
+      } else {
+        this.navLinks.push(link);
+      }
+    },
+
+    removeLink: function (removeLink) {
+      var links = this.getLinkSection(removeLink);
+      var indexOf = 0;
+
+      var res = _.first(links, function (link) {
+        if (link.id === removeLink.id) {
+          return true;
+        }
+
+        indexOf++;
+        return false;
+      });
+
+      if (!res) { return; }
+
+      links.splice(indexOf, 1);
+    },
+
+    getNavLinks: function () {
+      return this.navLinks;
+    },
+
+    getBottomNavLinks: function () {
+      return this.bottomNavLinks;
+    },
+
+    getFooterNavLinks: function () {
+      return this.footerNavLinks;
+    },
+
+    toggleMenu: function () {
+      app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, 
+                                !this.isMinimized());
+    },
+
+    getLinkSection: function (link) {
+      var links = this.navLinks;
+
+      if (link.bottomNav) {
+        links = this.bottomNavLinks;
+      }
+
+      if (link.footerNav) {
+        links = this.footerNavLinks;
+      }
+
+      return links;
+    },
+
+    updateLink: function (link) {
+      var oldLink;
+      var links = this.getLinkSection(link);
+
+      oldLink = _.find(links, function (oldLink) {
+        return oldLink.id === link.id;
+      });
+
+      if(!oldLink) { return; }
+
+      oldLink.title = link.title;
+      oldLink.href = link.href;
+    },
+
+    getVersion: function () {
+      return this.version;
+    },
+
+    setVersion: function (version) {
+      this.version = version;
+    },
+
+    getActiveLink: function () {
+      return this.activeLink;
+    },
+
+    setActiveLink: function (activeLink) {
+      this.activeLink = activeLink;
+    },
+
+    isMinimized: function () {
+      var isMinimized = app.utils.localStorageGet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED);
+      return (_.isUndefined(isMinimized)) ? false : isMinimized;
+    },
+
+    dispatch: function (action) {
+      switch(action.type) {
+
+        case ActionTypes.ADD_NAVBAR_LINK:
+          this.addLink(action.link);
+          this.triggerChange();
+        break;
+        case ActionTypes.TOGGLE_NAVBAR_MENU:
+          this.toggleMenu();
+          this.triggerChange();
+        break;
+        case ActionTypes.UPDATE_NAVBAR_LINK:
+          this.updateLink(action.link);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.CLEAR_NAVBAR_LINK:
+          this.reset();
+          this.triggerChange();
+        break;
+
+        case ActionTypes.REMOVE_NAVBAR_LINK:
+          this.removeLink(action.link);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.NAVBAR_SET_VERSION_INFO:
+          this.setVersion(action.version);
+          this.triggerChange();
+        break;
+
+        case ActionTypes.NAVBAR_ACTIVE_LINK:
+          this.setActiveLink(action.name);
+          this.triggerChange();
+        break;
+
+        default:
+          return;
+        // do nothing
+      }
+
+    }
+  });
+
+  Stores.navBarStore = new Stores.NavBarStore();
+  Stores.navBarStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.navBarStore.dispatch);
+
+  return Stores;
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/templates/footer.html
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/templates/footer.html b/app/addons/fauxton/templates/footer.html
deleted file mode 100644
index 9b83f7f..0000000
--- a/app/addons/fauxton/templates/footer.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!--
-Licensed under the Apache License, Version 2.0 (the "License"); you may not
-use this file except in compliance with the License. You may obtain a copy of
-the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-License for the specific language governing permissions and limitations under
-the License.
--->
-
-Fauxton on <a href="http://couchdb.apache.org/">Apache CouchDB</a><br> v. <%-version%>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/templates/nav_bar.html
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/templates/nav_bar.html b/app/addons/fauxton/templates/nav_bar.html
deleted file mode 100644
index 77796d7..0000000
--- a/app/addons/fauxton/templates/nav_bar.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<%/*
-Licensed under the Apache License, Version 2.0 (the "License"); you may not
-use this file except in compliance with the License. You may obtain a copy of
-the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-License for the specific language governing permissions and limitations under
-the License.
-*/%>
-
-<div class="burger">
-  <div><!-- * --></div>
-  <div><!-- * --></div>
-  <div><!-- * --></div>
-</div>
-
-<nav id="main_navigation">
-  <ul id="nav-links" class="nav">
-    <% _.each(navLinks, function(link) { %>
-    <% if (link.view) {return;}  %>
-      <li data-nav-name= "<%- link.title %>" >
-        <a href="<%- link.href %>">
-          <i class="<%- link.icon %> fonticon"></i>
-          <span><%- link.title %></span>
-        </a>
-      </li>
-    <% }); %>
-  </ul>
-
-  <div id="bottom-nav">
-    <ul id="bottom-nav-links" class="nav">
-      <li data-nav-name= "Documentation">
-        <a data-bypass="true" href="<%-getDocUrl('GENERAL')%>" target="_blank">
-          <i class="fonticon-bookmark fonticon"></i>
-            <span>Documentation</span>
-        </a>
-      </li>
-
-    <% _.each(bottomNavLinks, function(link) { %>
-    <% if (link.view) {return;}  %>
-      <li data-nav-name= "<%- link.title %>">
-        <a href="<%- link.href %>">
-          <i class="<%- link.icon %> fonticon"></i>
-          <span><%- link.title %></span>
-        </a>
-      </li>
-    <% }); %>
-    </ul>
-  </div>
-</nav>
-
-<div class="bottom-container">
-  <div class="brand">
-    <div class="icon">Apache Fauxton</div>
-  </div>
-  <div class="js-version"></div>
-  <div id="footer-links">
-    <ul id="footer-nav-links" class="nav">
-      <% _.each(footerNavLinks, function(link) { %>
-      <% if (link.view) {return;}  %>
-        <li data-nav-name= "<%- link.title %>">
-          <a href="<%- link.href %>">
-            <i class="<%- link.icon %> fonticon"></i>
-            <%- link.title %>
-          </a>
-        </li>
-      <% }); %>
-    </ul>
-  </div>
-</div>
-
-<div id="primary-nav-right-shadow"></div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/tests/componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/tests/componentsSpec.react.jsx b/app/addons/fauxton/tests/componentsSpec.react.jsx
new file mode 100644
index 0000000..d186212
--- /dev/null
+++ b/app/addons/fauxton/tests/componentsSpec.react.jsx
@@ -0,0 +1,47 @@
+// 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([
+  'api',
+  'addons/fauxton/components.react',
+  'addons/fauxton/actions',
+  'testUtils',
+  "react"
+], function (FauxtonAPI, Views, Actions, utils, React) {
+
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+
+  describe('NavBar', function () {
+
+    describe('burger', function () {
+      var container, burgerEl, toggleMenu;
+
+      beforeEach(function () {
+        toggleMenu = sinon.spy();
+        container = document.createElement('div');
+        burgerEl = TestUtils.renderIntoDocument(<Views.Burger toggleMenu={toggleMenu} />, container);
+      });
+
+      afterEach(function () {
+        React.unmountComponentAtNode(container);
+      });
+
+      it('dispatch TOGGLE_NAVBAR_MENU on click', function () {
+        TestUtils.Simulate.click(burgerEl.getDOMNode());
+        assert.ok(toggleMenu.calledOnce);
+      });
+
+    });
+  });
+
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/tests/navbarSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/tests/navbarSpec.js b/app/addons/fauxton/tests/navbarSpec.js
deleted file mode 100644
index 3eca6f6..0000000
--- a/app/addons/fauxton/tests/navbarSpec.js
+++ /dev/null
@@ -1,107 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-define([
-       'addons/fauxton/base',
-      'testUtils'
-], function (Fauxton, testUtils) {
-  var assert = testUtils.assert,
-      NavBar = Fauxton.NavBar;
-
-  describe('NavBar', function () {
-
-    describe('adding links', function () {
-      var navBar;
-
-      beforeEach(function () {
-        navBar = new NavBar();
-        navBar.navLinks = [];
-        navBar.bottomNavLinks = [];
-        navBar.footerNavLinks = [];
-      });
-
-      it('Should add link to navlinks', function () {
-        navBar.addLink({href: '#/test', title: 'Test Title'});
-
-        assert.equal(navBar.navLinks.length, 1);
-        assert.equal(navBar.footerNavLinks.length, 0);
-        assert.equal(navBar.bottomNavLinks.length, 0);
-      });
-
-      it('Should add link to bottom links', function () {
-        navBar.addLink({href: '#/test', bottomNav: true, title: 'Test Title'});
-
-        assert.equal(navBar.bottomNavLinks.length, 1);
-        assert.equal(navBar.navLinks.length, 0);
-        assert.equal(navBar.footerNavLinks.length, 0);
-      });
-
-      it('Should add link to footer links', function () {
-        navBar.addLink({href: '#/test', footerNav: true, title: 'Test Title'});
-
-        assert.equal(navBar.footerNavLinks.length, 1);
-        assert.equal(navBar.bottomNavLinks.length, 0);
-        assert.equal(navBar.navLinks.length, 0);
-      });
-    });
-
-    describe('removing links', function () {
-      var navBar;
-
-      beforeEach(function () {
-        navBar = new NavBar();
-        navBar.navLinks = [];
-        navBar.bottomNavLinks = [];
-        navBar.footerNavLinks = [];
-        navBar.addLink({
-          href: '#/test', 
-          footerNav: true, 
-          title: 'Test Title Footer'
-        });
-
-        navBar.addLink({
-          href: '#/test', 
-          bottomNav: true, 
-          title: 'Test Title Bottom'
-        });
-
-        navBar.addLink({
-          href: '#/test', 
-          title: 'Test Title'
-        });
-      });
-
-      it("should remove links from list", function () {
-        navBar.removeLink({
-          title: 'Test Title Footer',
-          footerNav: true
-        });
-
-        assert.equal(navBar.footerNavLinks.length, 0);
-        assert.equal(navBar.bottomNavLinks.length, 1);
-        assert.equal(navBar.navLinks.length, 1);
-      });
-
-      it("Should call render after removing links", function () {
-        var renderSpy = sinon.stub(navBar,'render');
-
-        navBar.removeLink({
-          title: 'Test Title Footer',
-          footerNav: true
-        });
-
-        assert.ok(renderSpy.calledOnce);
-      });
-
-    });
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/addons/fauxton/tests/storeSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/tests/storeSpec.js b/app/addons/fauxton/tests/storeSpec.js
new file mode 100644
index 0000000..bafc768
--- /dev/null
+++ b/app/addons/fauxton/tests/storeSpec.js
@@ -0,0 +1,237 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  'app',
+  'testUtils',
+  'api',
+  'addons/fauxton/stores',
+], function (app, testUtils, FauxtonAPI, Stores) {
+  var assert = testUtils.assert;
+  var navBarStore = Stores.navBarStore;
+
+  describe('NavBarStore', function () {
+    beforeEach(function () {
+      FauxtonAPI.dispatch({
+        type: 'CLEAR_NAVBAR_LINK',
+      });
+
+    });
+
+    describe('add links', function () {
+
+      it('to nav links', function () {
+        var link = {
+          id: 'mylink'
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getNavLinks()[0].id, link.id);
+      });
+
+      it('to top nav links', function () {
+        var link1 = {
+          id: 'mylink1'
+        };
+
+        var link2 = {
+          id: 'mylink2',
+          top: true
+        };
+
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link1
+        });
+
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link2
+        });
+
+        assert.equal(navBarStore.getNavLinks()[0].id, link2.id);
+      });
+
+      it('to bottom nav', function () {
+        var link = {
+          id: 'bottomNav',
+          bottomNav: true
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getBottomNavLinks()[1].id, link.id);
+      });
+
+      it('to top of bottom nav', function () {
+        var link = {
+          id: 'bottomNav',
+          bottomNav: true,
+          top: true
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getBottomNavLinks()[0].id, link.id);
+      });
+
+      it('to footer nav', function () {
+        var link = {
+          id: 'footerNav',
+          footerNav: true
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getFooterNavLinks()[0].id, link.id);
+      });
+    });
+
+    describe('remove link', function () {
+      it('from nav links', function () {
+        var link = {
+          id: 'remove_link',
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        FauxtonAPI.dispatch({
+          type: 'REMOVE_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getNavLinks().length, 0);
+      });
+
+      it('from bottom nav links', function () {
+        var link = {
+          id: 'remove_link',
+          bottomNav: true
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        FauxtonAPI.dispatch({
+          type: 'REMOVE_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getBottomNavLinks().length, 1);
+      });
+
+      it('from bottom nav links', function () {
+        var link = {
+          id: 'remove_link',
+          footerNav: true
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        FauxtonAPI.dispatch({
+          type: 'REMOVE_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getFooterNavLinks().length, 0);
+      });
+    });
+
+    describe('update link', function () {
+      it('for nav links', function () {
+        var link = {
+          id: 'update-link',
+          title: 'first'
+        };
+        FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+        });
+
+        link.title = 'second';
+
+        FauxtonAPI.dispatch({
+          type: 'UPDATE_NAVBAR_LINK',
+          link: link
+        });
+
+        assert.equal(navBarStore.getNavLinks()[0].title, 'second');
+      });
+
+    });
+
+    describe('set version', function () {
+      it('stores version number', function () {
+        FauxtonAPI.dispatch({
+          type: 'NAVBAR_SET_VERSION_INFO',
+          version: 1234
+        });
+
+        assert.equal(navBarStore.getVersion(), 1234);
+      });
+
+    });
+
+    describe('is Minimized', function () {
+
+      it('returns true if localstorage is true', function () {
+        app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, true);
+        assert.ok(navBarStore.isMinimized());
+      });
+
+      it('returns false if localstorage is false', function () {
+        app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, false);
+        assert.notOk(navBarStore.isMinimized(), false);
+      });
+
+      it('returns false if localstorage is undefined', function () {
+        window.localStorage.removeItem(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED);
+        assert.notOk(navBarStore.isMinimized(), false);
+      });
+    });
+
+    describe('toggleMenu', function () {
+
+      it('that is minimized changes to false', function () {
+        app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, true);
+        navBarStore.toggleMenu();
+        assert.notOk(navBarStore.isMinimized());
+      });
+
+      it('that is not minimized changes to true', function () {
+        app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, false);
+        navBarStore.toggleMenu();
+        assert.ok(navBarStore.isMinimized());
+      });
+
+      it('that is undefined changes to true', function () {
+        window.localStorage.removeItem(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED);
+        navBarStore.toggleMenu();
+        assert.ok(navBarStore.isMinimized());
+      });
+
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/app.js
----------------------------------------------------------------------
diff --git a/app/app.js b/app/app.js
index a4108c5..9064b57 100644
--- a/app/app.js
+++ b/app/app.js
@@ -43,11 +43,13 @@ function(app, $, _, Backbone, Bootstrap, Helpers, constants, Utils, FauxtonAPI,
     };
   }
 
+
   // Provide a global location to place configuration settings and module
   // creation also mix in Backbone.Events
   _.extend(app, {
     utils: Utils,
-    getParams: FauxtonAPI.utils.getParams
+    getParams: FauxtonAPI.utils.getParams,
+    helpers: Helpers
   });
 
   // Localize or create a new JavaScript Template object
@@ -90,23 +92,34 @@ function(app, $, _, Backbone, Bootstrap, Helpers, constants, Utils, FauxtonAPI,
 
   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',
+    el: '.wrapper',
     masterLayout: new FauxtonAPI.Layout(),
     
+    // I haven't wrapped these dispatch methods in a action 
+    // because I don't want to require fauxton/actions in this method.
     addHeaderLink: function(link) {
-      FauxtonAPI.registerExtension('navbar:addHeaderLink', link);
+      FauxtonAPI.dispatch({
+          type: 'ADD_NAVBAR_LINK',
+          link: link
+      });
+    },
+    
+    updateHeaderLink: function (link) {
+      FauxtonAPI.dispatch({
+        type: 'UPDATE_NAVBAR_LINK',
+        link: link
+      });
+
     },
 
     removeHeaderLink: function(link) {
-      FauxtonAPI.removeExtensionItem('navbar:addHeaderLink', link, function (item) {
-        if (item.title === link.title) {
-          return true;
-        }
-
-        return false;
+      FauxtonAPI.dispatch({
+          type: 'REMOVE_NAVBAR_LINK',
+          link: link
       });
     }
   });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/config.js
----------------------------------------------------------------------
diff --git a/app/config.js b/app/config.js
index 6a11868..27acfd5 100644
--- a/app/config.js
+++ b/app/config.js
@@ -33,6 +33,9 @@ require.config({
     "cloudant.pagingcollection": "../assets/js/plugins/cloudant.pagingcollection",
     "velocity": "../assets/js/plugins/velocity",
     "velocity.ui": "../assets/js/plugins/velocity.ui",
+    react: "../assets/js/libs/react",
+    flux: "../assets/js/libs/flux",
+    "es5-shim": "../assets/js/libs/es5-shim",
     moment: '../assets/js/libs/moment'
   },
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/constants.js
----------------------------------------------------------------------
diff --git a/app/constants.js b/app/constants.js
index 91cffda..15fd535 100644
--- a/app/constants.js
+++ b/app/constants.js
@@ -20,6 +20,10 @@ define([], function () {
       MODAL_BACKDROP_Z_INDEX: 1025
     },
 
+    DATABASES: {
+      DOCUMENT_LIMIT: 100
+    },
+
     // events
     EVENTS: {
       TRAY_CLOSED: 'tray:closed',
@@ -51,6 +55,5 @@ define([], function () {
       SIDEBAR_MINIMIZED: 'sidebar-minimized'
     }
   };
-
   return constants;
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/core/api.js
----------------------------------------------------------------------
diff --git a/app/core/api.js b/app/core/api.js
index 30daad9..8916b8c 100644
--- a/app/core/api.js
+++ b/app/core/api.js
@@ -1,23 +1,23 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// 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
+// 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([
-  "./base",
-       "core/layout",
-       "core/router",
-       "core/routeObject",
-       "core/utils",
-       "core/store",
-       "flux"
+  'core/base',
+  'core/layout',
+  'core/router',
+  'core/routeObject',
+  'core/utils',
+  'core/store',
+  'flux'
 ],
 
 function(FauxtonAPI, Layout, Router, RouteObject, utils, Store, Flux) {
@@ -51,10 +51,10 @@ function(FauxtonAPI, Layout, Router, RouteObject, utils, Store, Flux) {
   };
 
   FauxtonAPI.triggerRouteEvent = function (routeEvent, args) {
-    FauxtonAPI.router.triggerRouteEvent("route:" + routeEvent, args);
+    FauxtonAPI.router.triggerRouteEvent('route:' + routeEvent, args);
   };
 
-  
+
   return FauxtonAPI;
 });
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/core/layout.js
----------------------------------------------------------------------
diff --git a/app/core/layout.js b/app/core/layout.js
index 2faa230..d863863 100644
--- a/app/core/layout.js
+++ b/app/core/layout.js
@@ -19,7 +19,8 @@ define([
   // Allows the main layout of the page to be changed by any plugin.
   var Layout = function () {
     this.layout = new Backbone.Layout({
-      template: "templates/layouts/with_sidebar"
+      template: "templates/layouts/with_sidebar",
+      className: 'pusher'
     });
 
     this.layoutViews = {};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/core/router.js
----------------------------------------------------------------------
diff --git a/app/core/router.js b/app/core/router.js
index a4c36e7..5e4bb1c 100644
--- a/app/core/router.js
+++ b/app/core/router.js
@@ -94,7 +94,7 @@ function(FauxtonAPI, Auth, Backbone) {
       // FauxtonAPI header links and others depend on existence of the layout
       this.setModuleRoutes(addons);
 
-      $(FauxtonAPI.el).html(FauxtonAPI.masterLayout.el);
+      $(FauxtonAPI.el).append(FauxtonAPI.masterLayout.el);
       FauxtonAPI.masterLayout.render();
 
       this.lastPages = [];

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/templates/layouts/doc_editor.html
----------------------------------------------------------------------
diff --git a/app/templates/layouts/doc_editor.html b/app/templates/layouts/doc_editor.html
index 1da8d0a..af34878 100644
--- a/app/templates/layouts/doc_editor.html
+++ b/app/templates/layouts/doc_editor.html
@@ -3,7 +3,7 @@ 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
+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
@@ -12,17 +12,11 @@ License for the specific language governing permissions and limitations under
 the License.
 */%>
 
-<div class="wrapper">
-  <div id="primary-navbar"></div>
+<div id="dashboard" class="one-pane doc-editor-page">
+  <header class="fixed-header">
+    <div id="breadcrumbs"></div>
+    <div id="api-navbar"></div>
+  </header>
 
-  <div class="pusher">
-    <div id="dashboard" class="one-pane doc-editor-page">
-      <header class="fixed-header">
-        <div id="breadcrumbs"></div>
-        <div id="api-navbar"></div>
-      </header>
-
-      <div id="dashboard-content"></div>
-    </div>
-  </div>
+  <div id="dashboard-content"></div>
 </div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/templates/layouts/one_pane.html
----------------------------------------------------------------------
diff --git a/app/templates/layouts/one_pane.html b/app/templates/layouts/one_pane.html
index 39696e0..14234fa 100644
--- a/app/templates/layouts/one_pane.html
+++ b/app/templates/layouts/one_pane.html
@@ -3,7 +3,7 @@ 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
+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
@@ -12,23 +12,16 @@ License for the specific language governing permissions and limitations under
 the License.
 */%>
 
-<div class="wrapper">
+<div id="dashboard" class="one-pane">
+  <header class="fixed-header">
+    <div id="breadcrumbs"></div>
+    <div id="api-navbar"></div>
+    <div id="right-header"></div>
+  </header>
 
-  <div id="primary-navbar"></div>
-
-  <div class="pusher">
-    <div id="dashboard" class="one-pane">
-      <header class="fixed-header">
-        <div id="breadcrumbs"></div>
-        <div id="api-navbar"></div>
-        <div id="right-header"></div>
-      </header>
-
-      <div class="content-area container-fluid">
-        <div id="tabs"></div>
-        <div id="dashboard-content" class="scrollable"></div>
-        <div id="footer"></div>
-      </div>
-    </div>
+  <div class="content-area container-fluid">
+    <div id="tabs"></div>
+    <div id="dashboard-content" class="scrollable"></div>
+    <div id="footer"></div>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/templates/layouts/two_pane.html
----------------------------------------------------------------------
diff --git a/app/templates/layouts/two_pane.html b/app/templates/layouts/two_pane.html
index 031ad12..7d38e86 100644
--- a/app/templates/layouts/two_pane.html
+++ b/app/templates/layouts/two_pane.html
@@ -11,9 +11,6 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 License for the specific language governing permissions and limitations under
 the License.
 -->
-
-
-<div id="primary-navbar"></div>
 <div id="dashboard" class="container-fluid">
   <div class="fixed-header">
     <div id="breadcrumbs"></div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/templates/layouts/with_sidebar.html
----------------------------------------------------------------------
diff --git a/app/templates/layouts/with_sidebar.html b/app/templates/layouts/with_sidebar.html
index 1192621..129ec1e 100644
--- a/app/templates/layouts/with_sidebar.html
+++ b/app/templates/layouts/with_sidebar.html
@@ -11,9 +11,6 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 License for the specific language governing permissions and limitations under
 the License.
 -->
-
-
-<div id="primary-navbar"></div>
 <div id="dashboard" class="container-fluid">
   <header class="fixed-header">
     <div id="breadcrumbs"></div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/templates/layouts/with_tabs.html
----------------------------------------------------------------------
diff --git a/app/templates/layouts/with_tabs.html b/app/templates/layouts/with_tabs.html
index 9d23d35..94cf801 100644
--- a/app/templates/layouts/with_tabs.html
+++ b/app/templates/layouts/with_tabs.html
@@ -11,8 +11,6 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 License for the specific language governing permissions and limitations under
 the License.
 -->
-
-<div id="primary-navbar"></div>
 <div id="dashboard" class="container-fluid">
 
   <div class="fixed-header">

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/app/templates/layouts/with_tabs_sidebar.html
----------------------------------------------------------------------
diff --git a/app/templates/layouts/with_tabs_sidebar.html b/app/templates/layouts/with_tabs_sidebar.html
index 345acfc..fd4aaa0 100644
--- a/app/templates/layouts/with_tabs_sidebar.html
+++ b/app/templates/layouts/with_tabs_sidebar.html
@@ -3,7 +3,7 @@ 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
+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
@@ -12,35 +12,27 @@ License for the specific language governing permissions and limitations under
 the License.
 */%>
 
+<div id="dashboard" class="container-fluid with-sidebar">
 
-<div class="wrapper">
-
-  <div id="primary-navbar"></div>
+  <div class="with-sidebar tabs-with-sidebar content-area">
+    <div class="header-wrapper">
+      <div id="breadcrumbs" class="sidebar"></div>
+      <div class="right-header-wrapper">
+        <div id="api-navbar"></div>
+        <div id="right-header"></div>
+      </div>
+    </div>
 
-  <div class="pusher">
-    <div id="dashboard" class="container-fluid with-sidebar">
+    <aside id="sidebar-content" class="scrollable"></aside>
 
-      <div class="with-sidebar tabs-with-sidebar content-area">
-        <div class="header-wrapper">
-          <div id="breadcrumbs" class="sidebar"></div>
-          <div class="right-header-wrapper">
-            <div id="api-navbar"></div>
-            <div id="right-header"></div>
-          </div>
+    <section id="dashboard-content" class="list">
+      <div class="scrollable">
+        <div class="inner">
+          <div id="dashboard-upper-content"></div>
+          <div id="dashboard-lower-content"></div>
         </div>
-
-        <aside id="sidebar-content" class="scrollable"></aside>
-
-        <section id="dashboard-content" class="list">
-          <div class="scrollable">
-            <div class="inner">
-              <div id="dashboard-upper-content"></div>
-              <div id="dashboard-lower-content"></div>
-            </div>
-          </div>
-          <div id="footer"></div>
-        </section>
       </div>
-    </div>
+      <div id="footer"></div>
+    </section>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/assets/index.underscore
----------------------------------------------------------------------
diff --git a/assets/index.underscore b/assets/index.underscore
index 4d1f916..73386bb 100644
--- a/assets/index.underscore
+++ b/assets/index.underscore
@@ -36,7 +36,11 @@
 
   <div role="main" id="main">
 
-    <div id="app-container"></div>
+    <div id="app-container">
+      <div class="wrapper">
+        <div id="primary-navbar"></div>
+      </div>
+    </div>
   </div>
 
   <!-- Application source. -->

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/assets/less/animations.less
----------------------------------------------------------------------
diff --git a/assets/less/animations.less b/assets/less/animations.less
new file mode 100644
index 0000000..8de09e7
--- /dev/null
+++ b/assets/less/animations.less
@@ -0,0 +1,12 @@
+.keyframes (@name, @fromRules, @toRules) {
+    @-webkit-keyframes ~'@{name}' { 0% { @fromRules(); } 100% { @toRules(); } }
+       @-moz-keyframes ~'@{name}' { 0% { @fromRules(); } 100% { @toRules(); } }
+            @keyframes ~'@{name}' { 0% { @fromRules(); } 100% { @toRules(); } }
+}
+
+.animation(@options) {
+  -webkit-animation: @options;
+  -moz-animation: @options;
+  animation: @options;
+}
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/assets/less/fauxton.less
----------------------------------------------------------------------
diff --git a/assets/less/fauxton.less b/assets/less/fauxton.less
index 3f39df4..0874fd5 100644
--- a/assets/less/fauxton.less
+++ b/assets/less/fauxton.less
@@ -27,6 +27,7 @@
 @import "pagination.less";
 @import "trays.less";
 @import "mixins.less";
+@import "animations.less";
 
 
 /**
@@ -199,6 +200,7 @@ table.databases {
   .tab-content {
     padding-top: 70px;
   }
+
   .well{
     padding: 20px;
     .border-radius(0);
@@ -337,6 +339,7 @@ table.databases {
 }
 
 /*documents and databases */
+
 .view.show{
   color: @fontGrey;
 }
@@ -690,7 +693,7 @@ div.spinner {
 
 //----footer--///
 footer.pagination-footer {
-  z-index: 1;
+  z-index: 10;
   position: absolute;
   left: 0;
   right: 0;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/2a7fc648/assets/less/templates.less
----------------------------------------------------------------------
diff --git a/assets/less/templates.less b/assets/less/templates.less
index a2db779..f57a8b6 100644
--- a/assets/less/templates.less
+++ b/assets/less/templates.less
@@ -65,13 +65,13 @@
     overflow-x: hidden;
   }
   background-color: @primaryNav;
-  .js-version {
+  .version-footer {
     color: #fff;
     font-size: 10px;
     padding-left: 10px;
   }
   .closeMenu & {
-    .js-version {
+    .version-footer {
       display: none;
     }
   }
@@ -124,10 +124,10 @@
           width: 100%;
         }
         .closeMenu & {
+          width: 44px;
           .icon {
             background: url(../img/minilogo.png) no-repeat 0 0;
           }
-          width: 45px;
         }
       }
       #footer-nav-links {


Mime
View raw message