You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2014/08/12 15:21:12 UTC

[1/4] fauxton commit: updated refs/heads/backbone.layout-upgrade to 6f4e4f6

Repository: couchdb-fauxton
Updated Branches:
  refs/heads/backbone.layout-upgrade [created] 6f4e4f698


Refactored Layout and View, upgraded to latest LM

The Layout object was a simple wrapper over the Layout constructor
instead of extending the constructor itself.  I've patched this to make
it more consistent and removed duplicative methods.  I've also removed
the tests for `removeView` since that is now a built-in method.

The latest LM contains bugfixes and render performance updates that
unforuntately have to be opt-out.  Areas in the source or tests that are
expecting synchronous renders must be patched to work asynchronously to
take advantage.


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

Branch: refs/heads/backbone.layout-upgrade
Commit: c48962c4b55cc829fa2a75aeaaaea6590a9fcba8
Parents: ca7843b
Author: Tim Branyen <ti...@tabdeveloper.com>
Authored: Fri Aug 1 10:18:13 2014 -0400
Committer: Tim Branyen <ti...@tabdeveloper.com>
Committed: Fri Aug 1 10:23:55 2014 -0400

----------------------------------------------------------------------
 app/core/base.js                            |   4 +
 app/core/layout.js                          |  77 ++------
 app/core/tests/layoutSpec.js                |  38 +---
 assets/js/plugins/backbone.layoutmanager.js | 240 ++++++++++++++++++-----
 4 files changed, 217 insertions(+), 142 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/c48962c4/app/core/base.js
----------------------------------------------------------------------
diff --git a/app/core/base.js b/app/core/base.js
index 15499b3..9fbb7a0 100644
--- a/app/core/base.js
+++ b/app/core/base.js
@@ -62,6 +62,10 @@ function(Backbone, LayoutManager) {
     manage: true,
     disableLoader: false,
 
+    // Either tests or source are expecting synchronous renders, so disable
+    // asynchronous rendering improvements.
+    useRAF: false,
+
     forceRender: function () {
       this.hasRendered = false;
     }

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/c48962c4/app/core/layout.js
----------------------------------------------------------------------
diff --git a/app/core/layout.js b/app/core/layout.js
index ff339c7..0a45e62 100644
--- a/app/core/layout.js
+++ b/app/core/layout.js
@@ -10,82 +10,31 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-define([
-  "backbone", 
-  "plugins/backbone.layoutmanager"
-], function(Backbone) {
+define(function(require, exports, module) {
+  var Backbone = require("backbone");
+  var LayoutManager = require("plugins/backbone.layoutmanager");
 
-  // A wrapper of the main Backbone.layoutmanager
-  // 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",
-    });
+  var Layout = Backbone.Layout.extend({
+    template: "templates/layouts/with_sidebar",
 
-    this.layoutViews = {};
-    this.el = this.layout.el;
-  };
-
-  Layout.configure = function (options) {
-    Backbone.Layout.configure(options);
-  };
-
-  // creatings the dashboard object same way backbone does
-  _.extend(Layout.prototype, {
-    render: function () {
-      return this.layout.render();
-    },
+    // Either tests or source are expecting synchronous renders, so disable
+    // asynchronous rendering improvements.
+    useRAF: false,
 
     setTemplate: function(template) {
       if (template.prefix){
-        this.layout.template = template.prefix + template.name;
+        this.template = template.prefix + template.name;
       } else{
-        this.layout.template = "templates/layouts/" + template;
+        this.template = "templates/layouts/" + template;
       }
+
       // If we're changing layouts all bets are off, so kill off all the
       // existing views in the layout.
-      _.each(this.layoutViews, function(view){view.remove();});
-      this.layoutViews = {};
+      this.removeView();
       this.render();
-    },
-
-    setView: function(selector, view, keep) {
-      this.layout.setView(selector, view, false);
-
-      if (!keep) {
-        this.layoutViews[selector] = view;
-      }
-
-      return view;
-    },
-
-    renderView: function(selector) {
-      var view = this.layoutViews[selector];
-      if (!view) {
-        return false;
-      } else {
-        return view.render();
-      }
-    },
-
-    removeView: function (selector) {
-      var view = this.layout.getView(selector);
-
-      if (!view) {
-        return false;
-      }
-
-      view.remove();
-      
-      if (this.layoutViews[selector]) {
-        delete this.layoutViews[selector];
-      }
-
-      return true;
     }
-
   });
 
-  return Layout;
+  module.exports = Layout;
 
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/c48962c4/app/core/tests/layoutSpec.js
----------------------------------------------------------------------
diff --git a/app/core/tests/layoutSpec.js b/app/core/tests/layoutSpec.js
index 40b1947..2b87173 100644
--- a/app/core/tests/layoutSpec.js
+++ b/app/core/tests/layoutSpec.js
@@ -27,24 +27,20 @@ define([
       it("Should set template without prefix", function () {
         layout.setTemplate('myTemplate');
 
-        assert.equal(layout.layout.template, 'templates/layouts/myTemplate');
+        assert.equal(layout.template, 'templates/layouts/myTemplate');
 
       });
 
       it("Should set template with prefix", function () {
         layout.setTemplate({name: 'myTemplate', prefix: 'myPrefix/'});
 
-        assert.equal(layout.layout.template, 'myPrefix/myTemplate');
+        assert.equal(layout.template, 'myPrefix/myTemplate');
       });
 
       it("Should remove old views", function () {
-        var view = {
-          remove: function () {}
-        };
+        var view = new FauxtonAPI.Layout();
 
-        layout.layoutViews = {
-          'selector': view
-        };
+        layout.setView('selector', view);
 
         var mockRemove = sinon.spy(view, 'remove');
         layout.setTemplate('myTemplate');
@@ -62,31 +58,5 @@ define([
       });
 
     });
-
-    describe('#renderView', function () {
-
-      it('Should render existing view', function () {
-        var view = new Backbone.View();
-        var mockRender = sinon.spy(view, 'render');
-        layout.layoutViews = {
-          '#selector': view
-        };
-
-        var out = layout.renderView('#selector');
-
-        assert.ok(mockRender.calledOnce);
-      });
-
-      it('Should return false for non-existing view', function () {
-        var view = new Backbone.View();
-        layout.layoutViews = {
-          'selector': view
-        };
-
-        var out = layout.renderView('wrongSelector');
-        assert.notOk(out, 'No view found');
-      });
-    });
-
   });
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/c48962c4/assets/js/plugins/backbone.layoutmanager.js
----------------------------------------------------------------------
diff --git a/assets/js/plugins/backbone.layoutmanager.js b/assets/js/plugins/backbone.layoutmanager.js
index c5d4a80..e20bf5a 100644
--- a/assets/js/plugins/backbone.layoutmanager.js
+++ b/assets/js/plugins/backbone.layoutmanager.js
@@ -1,22 +1,35 @@
 /*!
- * backbone.layoutmanager.js v0.9.4
+ * backbone.layoutmanager.js v0.9.5
  * Copyright 2013, Tim Branyen (@tbranyen)
  * backbone.layoutmanager.js may be freely distributed under the MIT license.
  */
 (function(window, factory) {
   "use strict";
-  var Backbone = window.Backbone;
 
   // AMD. Register as an anonymous module.  Wrap in function so we have access
   // to root via `this`.
   if (typeof define === "function" && define.amd) {
-    return define(["backbone", "underscore", "jquery"], function() {
+    define(["backbone", "underscore", "jquery"], function() {
       return factory.apply(window, arguments);
     });
   }
 
+  // Node. Does not work with strict CommonJS, but only CommonJS-like
+  // environments that support module.exports, like Node.
+  else if (typeof exports === "object") {
+    var Backbone = require("backbone");
+    var _ = require("underscore");
+    // In a browserify build, since this is the entry point, Backbone.$
+    // is not bound. Ensure that it is.
+    Backbone.$ = Backbone.$ || require("jquery");
+
+    module.exports = factory.call(window, Backbone, _, Backbone.$);
+  }
+
   // Browser globals.
-  Backbone.Layout = factory.call(window, Backbone, window._, Backbone.$);
+  else {
+    factory.call(window, window.Backbone, window._, window.Backbone.$);
+  }
 }(typeof global === "object" ? global : this, function (Backbone, _, $) {
 "use strict";
 
@@ -236,20 +249,33 @@ var LayoutManager = Backbone.View.extend({
     return this.__manager__.renderDeferred.promise();
   },
 
+  // Proxy `then` for easier invocation.
+  then: function() {
+    return this.promise().then.apply(this, arguments);
+  },
+
   // Sometimes it's desirable to only render the child views under the parent.
   // This is typical for a layout that does not change.  This method will
-  // iterate over the child Views and aggregate all child render promises and
-  // return the parent View.  The internal `promise()` method will return the
-  // aggregate promise that resolves once all children have completed their
-  // render.
-  renderViews: function() {
+  // iterate over the provided views or delegate to `getViews` to fetch child
+  // Views and aggregate all render promises and return the parent View.
+  // The internal `promise()` method will return the aggregate promise that
+  // resolves once all children have completed their render.
+  renderViews: function(views) {
     var root = this;
     var manager = root.__manager__;
     var newDeferred = root.deferred();
 
+    // If the caller provided an array of views then render those, otherwise
+    // delegate to getViews.
+    if (views && _.isArray(views)) {
+      views = _.chain(views);
+    } else {
+      views = root.getViews(views);
+    }
+
     // Collect all promises from rendering the child views and wait till they
     // all complete.
-    var promises = root.getViews().map(function(view) {
+    var promises = views.map(function(view) {
       return view.render().__manager__.renderDeferred;
     }).value();
 
@@ -392,16 +418,12 @@ var LayoutManager = Backbone.View.extend({
     // Code path is less complex for Views that are not being inserted.  Simply
     // remove existing Views and bail out with the assignment.
     if (!insert) {
-      // If the View we are adding has already been rendered, simply inject it
-      // into the parent.
-      if (view.hasRendered) {
-        // Apply the partial.
-        view.partial(root.$el, view.$el, root.__manager__, manager);
+      // Ensure remove is called only when swapping in a new view (when the
+      // view is the same, it does not need to be removed or cleaned up).
+      if (root.getView(name) !== view) {
+        root.removeView(name);
       }
 
-      // Ensure remove is called when swapping View's.
-      root.removeView(name);
-
       // Assign to main views object and return for chainability.
       return root.views[selector] = view;
     }
@@ -450,7 +472,6 @@ var LayoutManager = Backbone.View.extend({
 
     // Triggered once the render has succeeded.
     function resolve() {
-      var next;
 
       // Insert all subViews into the parent at once.
       _.each(root.views, function(views, selector) {
@@ -465,8 +486,7 @@ var LayoutManager = Backbone.View.extend({
       if (parent && !manager.insertedViaFragment) {
         if (!root.contains(parent.el, root.el)) {
           // Apply the partial using parent's html() method.
-          parent.partial(parent.$el, root.$el, rentManager,
-            manager);
+          parent.partial(parent.$el, root.$el, rentManager, manager);
         }
       }
 
@@ -475,21 +495,24 @@ var LayoutManager = Backbone.View.extend({
 
       // Set this View as successfully rendered.
       root.hasRendered = true;
+      manager.renderInProgress = false;
+
+      // Clear triggeredByRAF flag.
+      delete manager.triggeredByRAF;
 
       // Only process the queue if it exists.
-      if (next = manager.queue.shift()) {
+      if (manager.queue && manager.queue.length) {
         // Ensure that the next render is only called after all other
         // `done` handlers have completed.  This will prevent `render`
         // callbacks from firing out of order.
-        next();
+        (manager.queue.shift())();
       } else {
         // Once the queue is depleted, remove it, the render process has
         // completed.
         delete manager.queue;
       }
 
-      // Reusable function for triggering the afterRender callback and event
-      // and setting the hasRendered flag.
+      // Reusable function for triggering the afterRender callback and event.
       function completeRender() {
         var console = window.console;
         var afterRender = root.afterRender;
@@ -520,10 +543,10 @@ var LayoutManager = Backbone.View.extend({
 
       // If the parent is currently rendering, wait until it has completed
       // until calling the nested View's `afterRender`.
-      if (rentManager && rentManager.queue) {
+      if (rentManager && (rentManager.renderInProgress || rentManager.queue)) {
         // Wait until the parent View has finished rendering, which could be
         // asynchronous, and trigger afterRender on this View once it has
-        // compeleted.
+        // completed.
         parent.once("afterRender", completeRender);
       } else {
         // This View and its parent have both rendered.
@@ -574,23 +597,21 @@ var LayoutManager = Backbone.View.extend({
       });
     }
 
-    // Another render is currently happening if there is an existing queue, so
-    // push a closure to render later into the queue.
-    if (manager.queue) {
-      aPush.call(manager.queue, actuallyRender);
-    } else {
-      manager.queue = [];
+    // Mark this render as in progress. This will prevent
+    // afterRender from being fired until the entire chain has rendered.
+    manager.renderInProgress = true;
 
-      // This the first `render`, preceeding the `queue` so render
-      // immediately.
-      actuallyRender(root, def);
-    }
+    // Start the render.
+    // Register this request & cancel any that conflict.
+    root._registerWithRAF(actuallyRender, def);
 
     // Put the deferred inside of the `__manager__` object, since we don't want
     // end users accessing this directly anymore in favor of the `afterRender`
     // event.  So instead of doing `render().then(...` do
     // `render().once("afterRender", ...`.
-    root.__manager__.renderDeferred = def;
+    // FIXME: I think we need to move back to promises so that we don't
+    // miss events, regardless of sync/async (useRAF setting)
+    manager.renderDeferred = def;
 
     // Return the actual View for chainability purposes.
     return root;
@@ -603,6 +624,75 @@ var LayoutManager = Backbone.View.extend({
 
     // Call the original remove function.
     return this._remove.apply(this, arguments);
+  },
+
+  // Register a view render with RAF.
+  _registerWithRAF: function(callback, deferred) {
+    var root = this;
+    var manager = root.__manager__;
+    var rentManager = manager.parent && manager.parent.__manager__;
+
+    // Allow RAF processing to be shut off using `useRAF`:false.
+    if (this.useRAF === false) {
+      if (manager.queue) {
+        aPush.call(manager.queue, callback);
+      } else {
+        manager.queue = [];
+        callback();
+      }
+      return;
+    }
+
+    // Keep track of all deferreds so we can resolve them.
+    manager.deferreds = manager.deferreds || [];
+    manager.deferreds.push(deferred);
+
+    // Schedule resolving all deferreds that are waiting.
+    deferred.done(resolveDeferreds);
+
+    // Cancel any other renders on this view that are queued to execute.
+    this._cancelQueuedRAFRender();
+
+    // Trigger immediately if the parent was triggered by RAF.
+    // The flag propagates downward so this view's children are also
+    // rendered immediately.
+    if (rentManager && rentManager.triggeredByRAF) {
+      return finish();
+    }
+
+    // Register this request with requestAnimationFrame.
+    manager.rafID = root.requestAnimationFrame(finish);
+
+    function finish() {
+      // Remove this ID as it is no longer valid.
+      manager.rafID = null;
+
+      // Set flag (will propagate to children) so they render
+      // without waiting for RAF.
+      manager.triggeredByRAF = true;
+
+      // Call original cb.
+      callback();
+    }
+
+    // Resolve all deferreds that were cancelled previously, if any.
+    // This allows the user to bind callbacks to any render callback,
+    // even if it was cancelled above.
+    function resolveDeferreds() {
+      for (var i = 0; i < manager.deferreds.length; i++){
+        manager.deferreds[i].resolveWith(root, [root]);
+      }
+      manager.deferreds = [];
+    }
+  },
+
+  // Cancel any queued render requests.
+  _cancelQueuedRAFRender: function() {
+    var root = this;
+    var manager = root.__manager__;
+    if (manager.rafID != null) {
+      root.cancelAnimationFrame(manager.rafID);
+    }
   }
 },
 
@@ -652,6 +742,9 @@ var LayoutManager = Backbone.View.extend({
       // Remove the View completely.
       view.$el.remove();
 
+      // Cancel any pending renders, if present.
+      view._cancelQueuedRAFRender();
+
       // Bail out early if no parent exists.
       if (!manager.parent) { return; }
 
@@ -735,12 +828,18 @@ var LayoutManager = Backbone.View.extend({
     if (options.suppressWarnings === true) {
       Backbone.View.prototype.suppressWarnings = true;
     }
+
+    // Allow global configuration of `useRAF`.
+    if (options.useRAF === false) {
+      Backbone.View.prototype.useRAF = false;
+    }
   },
 
   // Configure a View to work with the LayoutManager plugin.
   setupView: function(views, options) {
-    // Don't break the options object (passed into Backbone.View#initialize).
-    options = options || {};
+    // Ensure that options is always an object, and clone it so that
+    // changes to the original object don't screw up this view.
+    options = _.extend({}, options);
 
     // Set up all Views passed.
     _.each(aConcat.call([], views), function(view) {
@@ -819,14 +918,14 @@ var LayoutManager = Backbone.View.extend({
   }
 });
 
-LayoutManager.VERSION = "0.9.4";
+LayoutManager.VERSION = "0.9.5";
 
 // Expose through Backbone object.
 Backbone.Layout = LayoutManager;
 
 // Override _configure to provide extra functionality that is necessary in
 // order for the render function reference to be bound during initialize.
-Backbone.View = function(options) {
+Backbone.View.prototype.constructor = function(options) {
   var noel;
 
   // Ensure options is always an object.
@@ -854,6 +953,8 @@ Backbone.View = function(options) {
   ViewConstructor.apply(this, arguments);
 };
 
+Backbone.View = Backbone.View.prototype.constructor;
+
 // Copy over the extend method.
 Backbone.View.extend = ViewConstructor.extend;
 
@@ -865,6 +966,10 @@ var defaultOptions = {
   // Prefix template/layout paths.
   prefix: "",
 
+  // Use requestAnimationFrame to queue up view rendering and cancel
+  // repeat requests. Leave on for better performance.
+  useRAF: true,
+
   // Can be used to supply a different deferred implementation.
   deferred: function() {
     return $.Deferred();
@@ -878,7 +983,7 @@ var defaultOptions = {
 
   // By default, render using underscore's templating and trim output.
   renderTemplate: function(template, context) {
-    return trim(template(context));
+    return trim(template.call(this, context));
   },
 
   // By default, pass model attributes to the templates
@@ -964,7 +1069,54 @@ var defaultOptions = {
   // A method to determine if a View contains another.
   contains: function(parent, child) {
     return $.contains(parent, child);
-  }
+  },
+
+  // Based on:
+  // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+  // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and
+  // Tino Zijdel.
+  requestAnimationFrame: (function() {
+    var lastTime = 0;
+    var vendors = ["ms", "moz", "webkit", "o"];
+    var requestAnimationFrame = window.requestAnimationFrame;
+
+    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
+      requestAnimationFrame = window[vendors[i] + "RequestAnimationFrame"];
+    }
+
+    if (!requestAnimationFrame){
+      requestAnimationFrame = function(callback) {
+        var currTime = new Date().getTime();
+        var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+        var id = window.setTimeout(function() {
+          callback(currTime + timeToCall);
+        }, timeToCall);
+        lastTime = currTime + timeToCall;
+        return id;
+      };
+    }
+
+    return _.bind(requestAnimationFrame, window);
+  })(),
+
+  cancelAnimationFrame: (function() {
+    var vendors = ["ms", "moz", "webkit", "o"];
+    var cancelAnimationFrame = window.cancelAnimationFrame;
+
+    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
+      cancelAnimationFrame =
+        window[vendors[i] + "CancelAnimationFrame"] ||
+        window[vendors[i] + "CancelRequestAnimationFrame"];
+    }
+
+    if (!cancelAnimationFrame) {
+      cancelAnimationFrame = function(id) {
+        clearTimeout(id);
+      };
+    }
+
+    return _.bind(cancelAnimationFrame, window);
+  })()
 };
 
 // Extend LayoutManager with default options.


[4/4] fauxton commit: updated refs/heads/backbone.layout-upgrade to 6f4e4f6

Posted by ga...@apache.org.
Enable RAF and fix tests


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

Branch: refs/heads/backbone.layout-upgrade
Commit: 6f4e4f698749d778583a7324caf724861b7e0266
Parents: 5588bbc
Author: Garren Smith <ga...@gmail.com>
Authored: Tue Aug 12 15:20:58 2014 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Tue Aug 12 15:20:58 2014 +0200

----------------------------------------------------------------------
 app/addons/activetasks/tests/viewsSpec.js  | 15 ++++++++-------
 app/addons/config/tests/resourcesSpec.js   |  4 ++--
 app/addons/fauxton/tests/baseSpec.js       | 12 ++++++++----
 app/addons/fauxton/tests/filterViewSpec.js | 21 +++++++++++++--------
 app/addons/fauxton/tests/paginateSpec.js   |  4 ++--
 app/addons/permissions/tests/viewsSpec.js  | 12 ++++++------
 app/core/base.js                           |  2 +-
 app/core/layout.js                         |  2 +-
 test/mocha/testUtils.js                    |  8 ++++++--
 9 files changed, 47 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/addons/activetasks/tests/viewsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/tests/viewsSpec.js b/app/addons/activetasks/tests/viewsSpec.js
index 19c5b65..8731200 100644
--- a/app/addons/activetasks/tests/viewsSpec.js
+++ b/app/addons/activetasks/tests/viewsSpec.js
@@ -27,9 +27,9 @@ define([
 
     describe("on change polling rate", function () {
       var viewSandbox;
-      beforeEach(function () {
+      beforeEach(function (done) {
         viewSandbox = new ViewSandbox();
-        viewSandbox.renderView(tabMenu);
+        viewSandbox.renderView(tabMenu, done);
       });
 
       afterEach(function () {
@@ -66,7 +66,7 @@ define([
 
     describe('on request by type', function () {
       var viewSandbox, mainView;
-      beforeEach(function () {
+      beforeEach(function (done) {
 
         mainView = new Views.View({
           collection: new Activetasks.AllTasks(),
@@ -74,8 +74,9 @@ define([
         });
 
         viewSandbox = new ViewSandbox();
-        viewSandbox.renderView(tabMenu);
-        viewSandbox.renderView(mainView);
+        viewSandbox.renderView(tabMenu).promise().then(function () {
+          viewSandbox.renderView(mainView, done);
+        });
       });
 
       afterEach(function () {
@@ -99,14 +100,14 @@ define([
 
   describe('DataSection', function () {
     var viewSandbox, mainView;
-    beforeEach(function () {
+    beforeEach(function (done) {
       mainView = new Views.View({
         collection: new Activetasks.AllTasks(),
         currentView: "all"
       });
 
       viewSandbox = new ViewSandbox();
-      viewSandbox.renderView(mainView);
+      viewSandbox.renderView(mainView, done);
     });
 
     afterEach(function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/addons/config/tests/resourcesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/config/tests/resourcesSpec.js b/app/addons/config/tests/resourcesSpec.js
index 1cc9e62..8699e36 100644
--- a/app/addons/config/tests/resourcesSpec.js
+++ b/app/addons/config/tests/resourcesSpec.js
@@ -37,9 +37,9 @@ define([
 
     describe("editing Items", function () {
       var viewSandbox;
-      beforeEach(function () {
+      beforeEach(function (done) {
         viewSandbox = new ViewSandbox();
-        viewSandbox.renderView(tabMenu);
+        viewSandbox.renderView(tabMenu, done);
       });
 
       afterEach(function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/addons/fauxton/tests/baseSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/tests/baseSpec.js b/app/addons/fauxton/tests/baseSpec.js
index a7ff7a1..b9814bc 100644
--- a/app/addons/fauxton/tests/baseSpec.js
+++ b/app/addons/fauxton/tests/baseSpec.js
@@ -88,15 +88,19 @@ describe('Fauxton Notifications', function () {
       delete window.fauxton_xss_test_escaped;
     });
 
-    it('should be able to render unescaped', function () {
+    it('should be able to render unescaped', function (done) {
       var view = FauxtonAPI.addNotification({
         msg: '<script>window.fauxton_xss_test_unescaped = true;</script>',
         selector: 'body',
         escape: false
       });
-      view.$el.remove();
-      assert.ok(window.fauxton_xss_test_unescaped);
-      delete window.fauxton_xss_test_unescaped;
+
+      view.promise().then(function () {
+        view.$el.remove();
+        assert.ok(window.fauxton_xss_test_unescaped);
+        delete window.fauxton_xss_test_unescaped;
+        done();
+      });
     });
 
     it('should render escaped if the escape value is not explicitly false,' +

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/addons/fauxton/tests/filterViewSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/tests/filterViewSpec.js b/app/addons/fauxton/tests/filterViewSpec.js
index 1eefa05..0d9e7d0 100644
--- a/app/addons/fauxton/tests/filterViewSpec.js
+++ b/app/addons/fauxton/tests/filterViewSpec.js
@@ -27,26 +27,29 @@ define([
       FauxtonAPI.router.triggerRouteEvent = function () {};
     }
 
-    beforeEach(function () {
+    beforeEach(function (done) {
       filterView = new Components.FilterView({
         eventNamespace: 'mynamespace'
       });
 
       viewSandbox = new ViewSandbox();
-      viewSandbox.renderView(filterView);
+      viewSandbox.renderView(filterView, done);
     });
 
     afterEach(function () {
       viewSandbox.remove();
     });
 
-    it('should add filter markup', function () {
+    it('should add filter markup', function (done) {
       filterView.$('[name="filter"]').val('i was a lonely filter');
       filterView.$('.js-filter-form').submit();
-
       filterView.$('[name="filter"]').val('i am a filter');
       filterView.$('.js-filter-form').submit();
-      assert.equal(2, filterView.$('.js-remove-filter').length);
+
+      setTimeout(function () {
+        assert.equal(2, filterView.$('.js-remove-filter').length);
+        done();
+      }, 300);
     });
 
     it('should remove filter markup', function () {
@@ -70,13 +73,15 @@ define([
       assert.equal(0, filterView.$('.js-filter-tooltip').length);
     });
 
-    it('should add tooltips when a text for it is defined', function () {
+    it('should add tooltips when a text for it is defined', function (done) {
       filterView = new Components.FilterView({
         eventNamespace: 'mynamespace',
         tooltipText: 'ente ente'
       });
-      viewSandbox.renderView(filterView);
-      assert.equal(1, filterView.$('.js-filter-tooltip').length);
+      viewSandbox.renderView(filterView).promise().then(function () {
+        assert.equal(1, filterView.$('.js-filter-tooltip').length);
+        done();
+      });
     });
   });
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/addons/fauxton/tests/paginateSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/tests/paginateSpec.js b/app/addons/fauxton/tests/paginateSpec.js
index d336ea8..f97b255 100644
--- a/app/addons/fauxton/tests/paginateSpec.js
+++ b/app/addons/fauxton/tests/paginateSpec.js
@@ -22,7 +22,7 @@ define([
 
   describe('IndexPaginate', function () {
     var viewSandbox, paginate, collection, navigateMock;
-    beforeEach(function () {
+    beforeEach(function (done) {
       collection = new Models.IndexCollection([{
         id:'myId1',
         doc: 'num1'
@@ -43,7 +43,7 @@ define([
         canShowNextfn: function () { return true;}
       });
       viewSandbox = new ViewSandbox();
-      viewSandbox.renderView(paginate); 
+      viewSandbox.renderView(paginate, done); 
     });
 
     afterEach(function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/addons/permissions/tests/viewsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/permissions/tests/viewsSpec.js b/app/addons/permissions/tests/viewsSpec.js
index c22d405..147cee4 100644
--- a/app/addons/permissions/tests/viewsSpec.js
+++ b/app/addons/permissions/tests/viewsSpec.js
@@ -21,7 +21,7 @@ define([
   describe('Permission View', function () {
     var security, section, viewSandbox;
 
-    beforeEach(function () {
+    beforeEach(function (done) {
       security = new Models.Security({'admins': {
         'names': ['_user'],
         'roles': []
@@ -34,7 +34,7 @@ define([
       });
 
       viewSandbox = new ViewSandbox();
-      viewSandbox.renderView(section); 
+      viewSandbox.renderView(section, done); 
     });
 
     afterEach(function () {
@@ -67,7 +67,7 @@ define([
         security,
         viewSandbox;
 
-    beforeEach(function () {
+    beforeEach(function (done) {
       security = new Models.Security({'admins': {
         'names': ['_user'],
         'roles': []
@@ -80,7 +80,7 @@ define([
       });
 
       viewSandbox = new ViewSandbox();
-      viewSandbox.renderView(section); 
+      viewSandbox.renderView(section, done); 
     });
 
     afterEach(function () {
@@ -130,13 +130,13 @@ define([
     var item,
         viewSandbox;
 
-    beforeEach(function () {
+    beforeEach(function (done) {
       item = new Views.PermissionItem({
         item: '_user'
       });
 
       viewSandbox = new ViewSandbox();
-      viewSandbox.renderView(item); 
+      viewSandbox.renderView(item, done); 
     });
 
     afterEach(function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/core/base.js
----------------------------------------------------------------------
diff --git a/app/core/base.js b/app/core/base.js
index 9fbb7a0..e1f5331 100644
--- a/app/core/base.js
+++ b/app/core/base.js
@@ -64,7 +64,7 @@ function(Backbone, LayoutManager) {
 
     // Either tests or source are expecting synchronous renders, so disable
     // asynchronous rendering improvements.
-    useRAF: false,
+    useRAF: true,
 
     forceRender: function () {
       this.hasRendered = false;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/app/core/layout.js
----------------------------------------------------------------------
diff --git a/app/core/layout.js b/app/core/layout.js
index 84fc71e..8d589e7 100644
--- a/app/core/layout.js
+++ b/app/core/layout.js
@@ -19,7 +19,7 @@ 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"
     });
 
     this.layoutViews = {};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/6f4e4f69/test/mocha/testUtils.js
----------------------------------------------------------------------
diff --git a/test/mocha/testUtils.js b/test/mocha/testUtils.js
index 2c418f9..6b9e57f 100644
--- a/test/mocha/testUtils.js
+++ b/test/mocha/testUtils.js
@@ -29,15 +29,19 @@ function(FauxtonAPI,chai, sinonChai) {
       this.$ = this.$el.find;
     },
     views: [],
-    renderView: function (view) {
+    renderView: function (view, done) {
       this.views.push(view);
       this.$el.append(view.el);
       view.render();
+      if (done) { 
+        view.promise().done(function () { done(); });
+      }
+      return view;
     },
 
     remove: function () {
       _.each(this.views, function (view) {
-        view.remove();
+        view.removeView();
       }, this);
     }
   });


[2/4] fauxton commit: updated refs/heads/backbone.layout-upgrade to 6f4e4f6

Posted by ga...@apache.org.
Upgrade backbone.layoutmanager

Work off Tim's initial commits and adjust to cater for the use of
establish functions in views and routeObjects.
We don't want to immediately render the view. We first want any pending
promises returned from a routeObjects establish and a view's establish
to complete before rendering the view.


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

Branch: refs/heads/backbone.layout-upgrade
Commit: 9738389f918c542df106ddb04fd47a3fb0a2c3c4
Parents: c48962c
Author: Garren Smith <ga...@gmail.com>
Authored: Mon Aug 11 12:06:21 2014 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Mon Aug 11 12:06:21 2014 +0200

----------------------------------------------------------------------
 app/core/layout.js      | 84 ++++++++++++++++++++++++++++++++++++++++++--
 app/core/routeObject.js | 11 ++++--
 2 files changed, 90 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9738389f/app/core/layout.js
----------------------------------------------------------------------
diff --git a/app/core/layout.js b/app/core/layout.js
index 0a45e62..11992cb 100644
--- a/app/core/layout.js
+++ b/app/core/layout.js
@@ -10,7 +10,7 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-define(function(require, exports, module) {
+/*define(function(require, exports, module) {
   var Backbone = require("backbone");
   var LayoutManager = require("plugins/backbone.layoutmanager");
 
@@ -19,7 +19,7 @@ define(function(require, exports, module) {
 
     // Either tests or source are expecting synchronous renders, so disable
     // asynchronous rendering improvements.
-    useRAF: false,
+    useRAF: true,
 
     setTemplate: function(template) {
       if (template.prefix){
@@ -37,4 +37,84 @@ define(function(require, exports, module) {
 
   module.exports = Layout;
 
+});*/
+
+define([
+  "backbone", 
+  "plugins/backbone.layoutmanager"
+], function(Backbone) {
+
+  // A wrapper of the main Backbone.layoutmanager
+  // Allows the main layout of the page to be changed by any plugin.
+  var Layout = function () {
+    this.layout = new Backbone.Layout({
+      template: "templates/layouts/with_sidebar",
+    });
+
+    this.layoutViews = {};
+    this.el = this.layout.el;
+  };
+
+  Layout.configure = function (options) {
+    Backbone.Layout.configure(options);
+  };
+
+  // creatings the dashboard object same way backbone does
+  _.extend(Layout.prototype, {
+    render: function () {
+      return this.layout.render();
+    },
+
+    setTemplate: function(template) {
+      if (template.prefix){
+        this.layout.template = template.prefix + template.name;
+      } else{
+        this.layout.template = "templates/layouts/" + template;
+      }
+      // If we're changing layouts all bets are off, so kill off all the
+      // existing views in the layout.
+      _.each(this.layoutViews, function(view){view.removeView();});
+      this.layoutViews = {};
+      this.render();
+    },
+
+    setView: function(selector, view, keep) {
+      this.layout.setView(selector, view, false);
+
+      if (!keep) {
+        this.layoutViews[selector] = view;
+      }
+
+      return view;
+    },
+
+    renderView: function(selector) {
+      var view = this.layoutViews[selector];
+      if (!view) {
+        return false;
+      } else {
+        return view.render();
+      }
+    },
+
+    removeView: function (selector) {
+      var view = this.layout.getView(selector);
+
+      if (!view) {
+        return false;
+        }
+
+      this.layout.removeView(selector);
+      
+      if (this.layoutViews[selector]) {
+        delete this.layoutViews[selector];
+      }
+
+      return true;
+    }
+
+  });
+
+  return Layout;
+
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9738389f/app/core/routeObject.js
----------------------------------------------------------------------
diff --git a/app/core/routeObject.js b/app/core/routeObject.js
index f8a238f..898afcb 100644
--- a/app/core/routeObject.js
+++ b/app/core/routeObject.js
@@ -149,12 +149,17 @@ function(FauxtonAPI, Backbone) {
     },
 
     renderViewOnLayout: function(viewInfo, resp, xhr){
-      var masterLayout = viewInfo.masterLayout;
+      var masterLayout = viewInfo.masterLayout,
+          triggerBroadcast = _.bind(this.triggerBroadcast, this);
 
       masterLayout.setView(viewInfo.selector, viewInfo.view);
-      masterLayout.renderView(viewInfo.selector);
+      var promise = masterLayout.renderView(viewInfo.selector).promise();
 
-      this.triggerBroadcast('afterRender', viewInfo.view, viewInfo.selector);
+      promise.then(function () {
+        triggerBroadcast('afterRender', viewInfo.view, viewInfo.selector);
+      });
+
+      return promise;
     },
 
     establishError: function(resp){


[3/4] fauxton commit: updated refs/heads/backbone.layout-upgrade to 6f4e4f6

Posted by ga...@apache.org.
Fix failing tests and add more tests


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

Branch: refs/heads/backbone.layout-upgrade
Commit: 5588bbc1d8583013b7de80d119910acfd55607bb
Parents: 9738389
Author: Garren Smith <ga...@gmail.com>
Authored: Tue Aug 12 13:13:48 2014 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Tue Aug 12 13:13:48 2014 +0200

----------------------------------------------------------------------
 app/core/layout.js                | 33 ++------------
 app/core/tests/layoutSpec.js      | 79 +++++++++++++++++++++++++++++++---
 app/core/tests/routeObjectSpec.js |  6 +++
 3 files changed, 83 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/5588bbc1/app/core/layout.js
----------------------------------------------------------------------
diff --git a/app/core/layout.js b/app/core/layout.js
index 11992cb..84fc71e 100644
--- a/app/core/layout.js
+++ b/app/core/layout.js
@@ -10,35 +10,6 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-/*define(function(require, exports, module) {
-  var Backbone = require("backbone");
-  var LayoutManager = require("plugins/backbone.layoutmanager");
-
-  var Layout = Backbone.Layout.extend({
-    template: "templates/layouts/with_sidebar",
-
-    // Either tests or source are expecting synchronous renders, so disable
-    // asynchronous rendering improvements.
-    useRAF: true,
-
-    setTemplate: function(template) {
-      if (template.prefix){
-        this.template = template.prefix + template.name;
-      } else{
-        this.template = "templates/layouts/" + template;
-      }
-
-      // If we're changing layouts all bets are off, so kill off all the
-      // existing views in the layout.
-      this.removeView();
-      this.render();
-    }
-  });
-
-  module.exports = Layout;
-
-});*/
-
 define([
   "backbone", 
   "plugins/backbone.layoutmanager"
@@ -52,6 +23,8 @@ define([
     });
 
     this.layoutViews = {};
+    //this views don't ever get removed. An example of this is the main navigation sidebar
+    this.permanentViews = {};
     this.el = this.layout.el;
   };
 
@@ -83,6 +56,8 @@ define([
 
       if (!keep) {
         this.layoutViews[selector] = view;
+      } else {
+        this.permanentViews[selector] = view;
       }
 
       return view;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/5588bbc1/app/core/tests/layoutSpec.js
----------------------------------------------------------------------
diff --git a/app/core/tests/layoutSpec.js b/app/core/tests/layoutSpec.js
index 2b87173..1ae5675 100644
--- a/app/core/tests/layoutSpec.js
+++ b/app/core/tests/layoutSpec.js
@@ -27,24 +27,24 @@ define([
       it("Should set template without prefix", function () {
         layout.setTemplate('myTemplate');
 
-        assert.equal(layout.template, 'templates/layouts/myTemplate');
+        assert.equal(layout.layout.template, 'templates/layouts/myTemplate');
 
       });
 
       it("Should set template with prefix", function () {
         layout.setTemplate({name: 'myTemplate', prefix: 'myPrefix/'});
 
-        assert.equal(layout.template, 'myPrefix/myTemplate');
+        assert.equal(layout.layout.template, 'myPrefix/myTemplate');
       });
 
       it("Should remove old views", function () {
-        var view = new FauxtonAPI.Layout();
+        var view = new FauxtonAPI.View();
 
-        layout.setView('selector', view);
+        layout.setView('#selector', view);
 
-        var mockRemove = sinon.spy(view, 'remove');
+        var removeSpy = sinon.spy(view, 'removeView');
         layout.setTemplate('myTemplate');
-        assert.ok(mockRemove.calledOnce);
+        assert.ok(removeSpy.calledOnce);
 
       });
 
@@ -58,5 +58,72 @@ define([
       });
 
     });
+
+    describe('#setView', function () {
+      var view;
+      beforeEach(function () {
+        view = new FauxtonAPI.View();
+      });
+
+      it("Should keep record of view", function () {
+        layout.setView('.selector', view);
+        assert.equal(view, layout.layoutViews['.selector']);
+      });
+
+      it("Should not keep record of view if keep is false", function () {
+        layout.setView('.selector', view, true);
+        assert.ok(_.isUndefined(layout.layoutViews['.selector']));
+        assert.equal(view, layout.permanentViews['.selector']);
+      });
+
+    });
+
+    describe('#removeView', function () {
+      var view;
+
+      beforeEach(function () {
+        view = new FauxtonAPI.View();
+        layout.setView('#selector', view);
+      });
+
+      it('Should remove view from layout', function () {
+        var removeSpy = sinon.spy(layout.layout, 'removeView');
+
+        layout.removeView('#selector');
+        assert.ok(removeSpy.calledOnce);
+      });
+
+      it('Should remove view from list of active views', function () {
+        layout.setView('#selector', view);
+        layout.removeView('#selector');
+
+        assert.ok(_.isUndefined(layout.layoutViews['#selector']));
+      });
+
+      it("should return false if view doesn't exist", function () {
+        assert.notOk(layout.removeView('#fake'));
+      });
+
+    });
+
+    describe('#renderView', function () {
+      var view;
+
+      beforeEach(function () {
+        view = new FauxtonAPI.View();
+        layout.setView('#selector', view);
+      });
+
+      it('should render view', function () {
+        var renderSpy = sinon.spy(view, 'render');
+        layout.renderView('#selector');
+        assert.ok(renderSpy.calledOnce);
+      });
+
+      it('should not render a non-existing view', function () {
+        assert.notOk(layout.renderView('#fake'));
+      });
+
+    });
   });
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/5588bbc1/app/core/tests/routeObjectSpec.js
----------------------------------------------------------------------
diff --git a/app/core/tests/routeObjectSpec.js b/app/core/tests/routeObjectSpec.js
index 2fca94d..f3f4b48 100644
--- a/app/core/tests/routeObjectSpec.js
+++ b/app/core/tests/routeObjectSpec.js
@@ -71,7 +71,13 @@ define([
             viewSpy = sinon.stub(view, "establish");
         
         view.hasRendered = false;
+        view.promise = function () { 
+          var promise = $.Deferred();
+          promise.resolve();
+          return promise;
+        };
         getViewsSpy.returns({'#view': view});
+        mockLayout.renderView = function () { return view;};
 
         testRouteObject.renderWith('the-route', mockLayout, 'args');
         assert.ok(viewSpy.calledOnce, 'Should render view');