/*!
 * backbone.layoutmanager.js v0.6.6
 * Copyright 2012, Tim Branyen (@tbranyen)
 * backbone.layoutmanager.js may be freely distributed under the MIT license.
 */
(function(window) {

"use strict";

// Used to keep track of all LayoutManager key names.
var keys;

// Alias the libraries from the global object.
var Backbone = window.Backbone;
var _ = window._;
var $ = window.$;

// Store references to original View functions.
var _configure = Backbone.View.prototype._configure;
var render = Backbone.View.prototype.render;

// A LayoutManager is simply a Backbone.View with some sugar.
var LayoutManager = Backbone.View.extend({
  // This named function allows for significantly easier debugging.
  constructor: function Layout(options) {
    // Options should always a valid object.
    options = options || {};

    // Give this View superpowers.
    LayoutManager.setupView(this, options);

    // Have Backbone set up the rest of this View.
    Backbone.View.call(this, options);
  },

  // Swap the current layout to  new layout.
  swapLayout: function(newLayout) {
    // Set Views to be a hybrid of original and new layout.
    newLayout.views = _.defaults({}, this.views, newLayout.views);

    // Re-use the same layout DOM element.
    newLayout.setElement(this.el);

    // Allow for chainability.
    return newLayout;
  },

  // Shorthand to root.view function with append flag.
  insertView: function(selector, view) {
    // If a selector was passed, forward that onto setView.
    if (view) {
      return this.setView(selector, view, true);
    }

    // Omitting a selector will place the View directly into the parent.
    return this.setView(selector, true);
  },

  // Works like insertView, except allows you to bulk insert via setViews.
  insertViews: function(views) {
    // Ensure each view is wrapped in an array.
    _.each(views, function(view, selector) {
      views[selector] = [].concat(view);
    });

    return this.setViews(views);
  },

  // Will return a single view that matches the filter function.
  getView: function(fn) {
    return this.getViews(fn).first().value();
  },

  // Provide a filter function to get a flattened array of all the subviews.
  // If the filter function is omitted it will return all subviews.
  getViews: function(fn) {
    // Flatten all views.
    var views = _.chain(this.views).map(function(view) {
      return [].concat(view);
    }, this).flatten().value();

    // Return a wrapped function to allow for easier chaining.
    return _.chain(_.filter(views, fn ? fn : _.identity));
  },

  // This takes in a partial name and view instance and assigns them to
  // the internal collection of views.  If a view is not a LayoutManager
  // instance, then mix in the LayoutManager prototype.  This ensures
  // all Views can be used successfully.
  //
  // Must definitely wrap any render method passed in or defaults to a
  // typical render function `return layout(this).render()`.
  setView: function(name, view, append) {
    var partials, options;
    var root = this;

    // If no name was passed, use an empty string and shift all arguments.
    if (!_.isString(name)) {
      append = view;
      view = name;
      name = "";
    }

    // If the parent View's object, doesn't exist... create it.
    this.views = this.views || {};

    // Ensure remove is called when swapping View's.
    if (!append && this.views[name]) {
      // If the views are an array, iterate and remove each individually.
      if (_.isArray(this.views[name])) {
        _.each(this.views[name], function(view) {
          view.remove();
        });
      // Otherwise it's a single view and can safely call remove.
      } else {
        this.views[name].remove();
      }
    }

    // If the View has not been properly set up, throw an Error message
    // indicating that the View needs `manage: true` set.
    if (!view.__manager__) {
      throw new Error("manage property not set.  " +
        "http://tbranyen.github.com/backbone.layoutmanager/#usage/struc" +
        "turing-a-view");
    }

    // Instance overrides take precedence, fallback to prototype options.
    options = view._options();

    // Custom template render function.
    view.render = function(done) {
      var viewDeferred = options.deferred();
      var manager = view.__manager__;

      // Ensure the latest deferred is assigned.
      manager.viewDeferred = viewDeferred;
      
      // Break this callback out so that its not duplicated inside the 
      // following safety try/catch.
      function renderCallback() {
        // List items should not be re-added, unless they have `keep: true`
        // set.
        if ((!append || view.keep) || !manager.hasRendered) {
          options.partial(root.el, name, view.el, append);
        }

        // Ensure events are always correctly bound after rendering.
        view.delegateEvents();

        // If the View has a managed handler, resolve and remove it.
        if (manager.handler) {
          // Resolve the View's render handler deferred.
          manager.handler.resolveWith(view, [view.el]);

          // Remove the handler once it has resolved.
          delete manager.handler;
        }

        // When a view has been resolved, ensure that it is correctly updated
        // and that any done callbacks are triggered.
        viewDeferred.resolveWith(view, [view.el]);

        // Only call the done function if a callback was provided.
        if (_.isFunction(done)) {
          done.call(view, view.el);
        }
      }

      // Remove subViews without the `keep` flag set to `true`.
      view._removeViews();

      // Call the original render method.
      LayoutManager.prototype.render.call(view).then(renderCallback);

      // Return the promise for chainability.
      return viewDeferred.promise();
    };

    // Add reference to the parentView.
    view.__manager__.parent = root;
    // Add reference to the placement selector used.
    view.__manager__.selector = name;

    // Special logic for appending items. List items are represented as an
    // array.
    if (append) {
      // Start with an array if none exists.
      partials = this.views[name] = this.views[name] || [];
      
      if (!_.isArray(this.views[name])) {
        // Ensure this.views[name] is an array.
        partials = this.views[name] = [this.views[name]];
      }

      // Ensure the View is not already added to the list.  If it is, bail out
      // early.
      if (_.indexOf(partials, view) > -1) {
        return view;
      }

      // Add the view to the list of partials.
      partials.push(view);

      // Put the view into `append` mode.
      view.__manager__.append = true;

      return view;
    }

    // Assign to main views object and return for chainability.
    return this.views[name] = view;
  },

  // Allows the setting of multiple views instead of a single view.
  setViews: function(views) {
    // Iterate over all the views and use the View's view method to assign.
    _.each(views, function(view, name) {
      // If the view is an array put all views into insert mode.
      if (_.isArray(view)) {
        return _.each(view, function(view) {
          this.insertView(name, view);
        }, this);
      }

      // Assign each view using the view function.
      this.setView(name, view);
    }, this);

    // Allow for chaining
    return this;
  },

  // By default this should find all nested views and render them into
  // the this.el and call done once all of them have successfully been
  // resolved.
  //
  // This function returns a promise that can be chained to determine
  // once all subviews and main view have been rendered into the view.el.
  render: function(done) {
    var promise;
    var root = this;
    var options = this._options();
    var viewDeferred = options.deferred();
    var manager = this.__manager__;

    // Ensure duplicate renders don't override.
    if (manager.renderDeferred) {
      // Set the most recent done callback.
      manager.callback = done;

      // Return the deferred.
      return manager.renderDeferred;
    }

    // Disable the ability for any new sub-views to be added.
    manager.renderDeferred = viewDeferred;
    
    // Wait until this View has rendered before dealing with nested Views.
    this._render(LayoutManager._viewRender).fetch.then(function() {
      // Create a list of promises to wait on until rendering is done. Since
      // this method will run on all children as well, its sufficient for a
      // full hierarchical. 
      var promises = _.map(root.views, function(view) {
        // Hoist deferred var, used later on...
        var def;

        // Ensure views are rendered in sequence
        function seqRender(views, done) {
          // Once all views have been rendered invoke the sequence render
          // callback.
          if (!views.length) {
            return done();
          }

          // Get each view in order, grab the first one off the stack.
          var view = views.shift();

          // Render the View and once complete call the next view.
          view.render(function() {
            // Invoke the recursive sequence render function with the
            // remaining views.
            seqRender(views, done);
          });
        }

        // If rendering a list out, ensure they happen in a serial order.
        if (_.isArray(view)) {
          // A singular deferred that represents all the items.
          def = options.deferred();

          seqRender(_.clone(view), function() {
            def.resolve();
          });

          return def.promise();
        }

        // Only return the fetch deferred, resolve the main deferred after
        // the element has been attached to it's parent.
        return view.render();
      });

      // Once all subViews have been rendered, resolve this View's deferred.
      options.when(promises).then(function() {
        viewDeferred.resolveWith(root, [root.el]);
      });
    });

    // Return a promise that resolves once all immediate subViews have
    // rendered.
    promise = viewDeferred.then(function() {
      // Only call the done function if a callback was provided.
      if (_.isFunction(done)) {
        done.call(root, root.el);
      }

      if (root.__manager__.handler) {
        root.__manager__.handler.resolveWith(root, [root.el]);

        // Remove the handler, so it's never accidentally referenced.
        delete root.__manager__.handler;
      }

      // If the render was called twice, there is a possibility that the
      // callback style was used twice.  This will ensure the latest callback
      // is also triggered.
      if (_.isFunction(root.__manager__.callback)) {
        root.__manager__.callback.call(root, root.el);

        // Remove the most recent callback.
        delete root.__manager__.callback;
      }

      // Remove the rendered deferred.
      delete root.__manager__.renderDeferred;
    });

    // Proxy the View's properties to this promise for chaining purposes.
    return _.defaults(promise, root);
  },

  // Ensure the cleanup function is called whenever remove is called.
  remove: function() {
    // Force remove itself from it's parent.
    LayoutManager._removeView(this, true);

    // Call the original remove function.
    return this._remove.apply(this, arguments);
  },

  // Merge instance and global options.
  _options: function() {
    // Instance overrides take precedence, fallback to prototype options.
    return _.extend({}, this, LayoutManager.prototype.options, this.options);
  }
},
{
  // Clearable cache.
  _cache: {},

  // Creates a deferred and returns a function to call when finished.
  _makeAsync: function(options, done) {
    var handler = options.deferred();

    // Used to handle asynchronous renders.
    handler.async = function() {
      handler._isAsync = true;

      return done;
    };

    return handler;
  },

  // This gets passed to all _render methods.
  _viewRender: function(root) {
    var url, contents, handler;
    var options = root._options();

    // Once the template is successfully fetched, use its contents to
    // proceed.  Context argument is first, since it is bound for
    // partial application reasons.
    function done(context, contents) {
      // Ensure the cache is up-to-date.
      LayoutManager.cache(url, contents);

      // Render the View into the el property.
      if (contents) {
        options.html(root.el, options.render(contents, context));
      }

      // Resolve only the fetch (used internally) deferred with the View
      // element.
      handler.fetch.resolveWith(root, [root.el]);
    }

    return {
      // This render function is what gets called inside of the View render,
      // when manage(this).render is called.  Returns a promise that can be
      // used to know when the element has been rendered into its parent.
      render: function(context) {
        var manager = root.__manager__;
        var template = root.template || options.template;

        if (root.serialize) {
          options.serialize = root.serialize;
        }

        // Seek out serialize method and use that object.
        if (!context && _.isFunction(options.serialize)) {
          context = options.serialize.call(root);
        // If serialize is an object, just use that.
        } else if (!context && _.isObject(options.serialize)) {
          context = options.serialize;
        }

        // Create an asynchronous handler.
        handler = LayoutManager._makeAsync(options, _.bind(done, root,
          context));

        // Make a new deferred purely for the fetch function.
        handler.fetch = options.deferred();

        // Assign the handler internally to be resolved once its inside the
        // parent element.
        manager.handler = handler;

        // Set the url to the prefix + the view's template property.
        if (_.isString(template)) {
          url = manager.prefix + template;
        }

        // Check if contents are already cached.
        if (contents = LayoutManager.cache(url)) {
          done(context, contents, url);

          return handler;
        }

        // Fetch layout and template contents.
        if (_.isString(template)) {
          contents = options.fetch.call(handler, manager.prefix + template);
        // If its not a string just pass the object/function/whatever.
        } else if (template != null) {
          contents = options.fetch.call(handler, template);
        }

        // If the function was synchronous, continue execution.
        if (!handler._isAsync) {
          done(context, contents);
        }

        return handler;
      }
    };
  },

  // Accept either a single view or an array of views to clean of all DOM
  // events internal model and collection references and all Backbone.Events.
  cleanViews: function(views) {
    // Clear out all existing views.
    _.each([].concat(views), function(view) {
      // Remove all custom events attached to this View.
      view.unbind();

      // Ensure all nested views are cleaned as well.
      if (view.views) {
        _.each(view.views, function(view) {
          LayoutManager.cleanViews(view);
        });
      }

      // If a custom cleanup method was provided on the view, call it after
      // the initial cleanup is done
      if (_.isFunction(view.cleanup)) {
        view.cleanup.call(view);
      }
    });
  },

  // Cache templates into LayoutManager._cache.
  cache: function(path, contents) {
    // If template path is found in the cache, return the contents.
    if (path in this._cache) {
      return this._cache[path];
    // Ensure path and contents aren't undefined.
    } else if (path != null && contents != null) {
      return this._cache[path] = contents;
    }

    // If the template is not in the cache, return undefined.
  },

  // This static method allows for global configuration of LayoutManager.
  configure: function(opts) {
    _.extend(LayoutManager.prototype.options, opts);

    // Allow LayoutManager to manage Backbone.View.prototype.
    if (opts.manage) {
      Backbone.View.prototype.manage = true;
    }
  },

  // Configure a View to work with the LayoutManager plugin.
  setupView: function(view, options) {
    var views, viewOptions;
    var proto = Backbone.LayoutManager.prototype;
    var viewOverrides = _.pick(view, keys);

    // If the View has already been setup, no need to do it again.
    if (view.__manager__) {
      return;
    }

    // Ensure necessary properties are set.
    _.defaults(view, {
      // Ensure a view always has a views object.
      views: {},

      // Internal state object used to store whether or not a View has been
      // taken over by layout manager and if it has been rendered into the DOM.
      __manager__: {},

      // Add options into the prototype.
      _options: LayoutManager.prototype._options,

      // Add the ability to remove all Views.
      _removeViews: LayoutManager._removeViews,

      // Add the ability to remove itself.
      _removeView: LayoutManager._removeView
    });

    // Set the prefix for a layout.
    if (view instanceof Backbone.Layout) {
      view.__manager__.prefix = view._options().paths.layout || "";
    // Set the prefix for a template.
    } else {
      view.__manager__.prefix = view._options().paths.template || "";
    }

    // Extend the options with the prototype and passed options.
    options = view.options = _.defaults(options || {}, view.options,
      proto.options);

    // Ensure view events are properly copied over.
    viewOptions = _.pick(options, ["events"].concat(_.values(options.events)));
    _.extend(view, viewOptions);

    // If the View still has the Backbone.View#render method, remove it.  Don't
    // want it accidentally overriding the LM render.
    delete viewOverrides.render;

    // Pick out the specific properties that can be dynamically added at
    // runtime and ensure they are available on the view object.
    _.extend(options, viewOverrides);

    // By default the original Remove function is the Backbone.View one.
    view._remove = Backbone.View.prototype.remove;

    // Always use this render function when using LayoutManager.
    view._render = function(manage) {
      var renderDeferred;
      // Cache these properties.
      var beforeRender = this._options().beforeRender;
      var afterRender = this._options().afterRender;

      // Ensure all subViews are properly scrubbed.
      this._removeViews();

      // If a beforeRender function is defined, call it.
      if (_.isFunction(beforeRender)) {
        beforeRender.call(this, this);
      }

      // Always emit a beforeRender event.
      this.trigger("beforeRender", this);

      // Render!
      renderDeferred = manage(this).render();

      // Once rendering is complete...
      renderDeferred.then(function() {
        // Keep the view consistent between callbacks and deferreds.
        var view = this;
        // Shorthand the manager.
        var manager = view.__manager__;
        // Shorthand the View's parent.
        var parent = manager.parent;
        // This can be called immediately if the conditions allow, or it will
        // be deferred until a parent has finished rendering.
        var done = function() {
          // Ensure events are always correctly bound after rendering.
          view.delegateEvents();

          // Set the view hasRendered.
          view.__manager__.hasRendered = true;

          // If an afterRender function is defined, call it.
          if (_.isFunction(afterRender)) {
            afterRender.call(view, view);
          }

          // Always emit an afterRender event.
          view.trigger("afterRender", view);
        };
        // This function recursively loops through Views to find
        // the most top level parent.
        var findRootParent = function(view) {
          var manager = view.__manager__;

          // If a parent exists and the parent has not rendered, return that
          // parent.
          if (manager.parent && !manager.parent.__manager__.hasRendered) {
            return manager.parent;
          }

          // This is the most root parent.
          return view;
        };

        // If no parent exists, immediately call the done callback.
        if (!parent) {
          return done.call(view);
        }

        // If this view has already rendered, simply call the callback.
        if (parent.__manager__.hasRendered) 


        // Find the parent highest in the chain that has not yet rendered.
        parent = findRootParent(view);

        // Once the parent has finished rendering, trickle down and
        // call sub-view afterRenders.
        parent.on("afterRender", function() {
          // Ensure its properly unbound immediately.
          parent.off(null, null, view);

          // Call the done callback.
          done.call(view);
        }, view);
      });
      return renderDeferred;
    };

    // Ensure the render is always set correctly.
    view.render = LayoutManager.prototype.render;

    // If the user provided their own remove override, use that instead of the
    // default.
    if (view.remove !== proto.remove) {
      view._remove = view.remove;
      view.remove = proto.remove;
    }
    
    // Normalize views to exist on either instance or options, default to
    // options.
    views = options.views || view.views;

    // Set the internal views, only if selectors have been provided.
    if (_.keys(views).length) {
      view.setViews(views);
    }

    // Ensure the template is mapped over.
    if (view.template) {
      options.template = view.template;

      // Remove it from the instance.
      delete view.template;
    }
  },

  // Remove all subViews.
  _removeViews: function(root) {
    // Allow removeView to be called on instances.
    root = root || this;

    // Iterate over all of the view's subViews.
    root.getViews().each(function(view) {
      LayoutManager._removeView(view, true);
    });
  },

  // Remove a single subView.
  _removeView: function(view, force) {
    // Shorthand the manager for easier access.
    var manager = view.__manager__;
    // Test for keep.
    var keep = _.isBoolean(view.keep) ? view.keep : view.options.keep;
    // Only allow force if View contains a parent.
    force = force && manager.parent;

    // Ensure that cleanup is called correctly when `_removeView` is triggered.
    LayoutManager.cleanViews(view);

    // Only remove views that do not have `keep` attribute set, unless the
    // force flag is set.
    if (!keep && (manager.append === true || force) && manager.hasRendered) {
      // Remove the View completely.
      view.$el.remove();

      // If this is an array of items remove items that are not marked to
      // keep.
      if (_.isArray(manager.parent.views[manager.selector])) {
        // Remove directly from the Array reference.
        return manager.parent.getView(function(view, i) {
          // If the managers match, splice off this View.
          if (view.__manager__ === manager) {
            manager.parent.views[manager.selector].splice(i, 1);
          }
        });
      }

      // Otherwise delete the parent selector.
      delete manager.parent.views[manager.selector];
    }
  }
});

// Ensure all Views always have access to get/set/insert(View/Views).
_.each(["get", "set", "insert"], function(method) {
  var backboneProto = Backbone.View.prototype;
  var layoutProto = LayoutManager.prototype;

  // Attach the singular form.
  backboneProto[method + "View"] = layoutProto[method + "View"];

  // Attach the plural form.
  backboneProto[method + "Views"] = layoutProto[method + "Views"];
});

// Convenience assignment to make creating Layout's slightly shorter.
Backbone.Layout = Backbone.LayoutManager = LayoutManager;
// A LayoutView is just a Backbone.View with manage set to true.
Backbone.LayoutView = Backbone.View.extend({
  manage: true
});

// Override _configure to provide extra functionality that is necessary in
// order for the render function reference to be bound during initialize.
Backbone.View.prototype._configure = function() {
  // Run the original _configure.
  var retVal = _configure.apply(this, arguments);

  // If manage is set, do it!
  if (this.manage) {
    // Set up this View.
    LayoutManager.setupView(this);
  }

  // Act like nothing happened.
  return retVal;
};

// Default configuration options; designed to be overriden.
LayoutManager.prototype.options = {
  // Layout and template properties can be assigned here to prefix
  // template/layout names.
  paths: {},

  // Can be used to supply a different deferred implementation.
  deferred: function() {
    return $.Deferred();
  },

  // Fetch is passed a path and is expected to return template contents as a
  // function or string.
  fetch: function(path) {
    return _.template($(path).html());
  },

  // This is really the only way you will want to partially apply a view into
  // a layout.  Its entirely possible you'll want to do it differently, so
  // this method is available to change.
  partial: function(root, name, el, append) {
    // If no selector is specified, assume the parent should be added to.
    var $root = name ? $(root).find(name) : $(root);

    // If no root found, return false.
    if (!$root.length) {
      return false;
    }

    // Use the append method if append argument is true.
    this[append ? "append" : "html"]($root, el);

    // If successfully added, return true.
    return true;
  },

  // Override this with a custom HTML method, passed a root element and an
  // element to replace the innerHTML with.
  html: function(root, el) {
    $(root).html(el);
  },

  // Very similar to HTML except this one will appendChild.
  append: function(root, el) {
    $(root).append(el);
  },

  // Return a deferred for when all promises resolve/reject.
  when: function(promises) {
    return $.when.apply(null, promises);
  },

  // By default, render using underscore's templating.
  render: function(template, context) {
    return template(context);
  }
};

// Maintain a list of the keys at define time.
keys = _.keys(LayoutManager.prototype.options);

})(this);
{
          return options.when([manager.viewDeferred,
            parent.__manager__.viewDeferred]).then(function() {
            done.call(view);
          });
        }