Adds precompiled .mod files to vendor.
authorDavid Schoonover <dsc@wikimedia.org>
Fri, 6 Jul 2012 17:27:47 +0000 (10:27 -0700)
committerDavid Schoonover <dsc@wikimedia.org>
Fri, 6 Jul 2012 17:27:47 +0000 (10:27 -0700)
20 files changed:
static/vendor/backbone-0.9.2.mod.js [new file with mode: 0644]
static/vendor/backbone-0.9.2.mod.min.js [new file with mode: 0644]
static/vendor/backbone.mod.js [new symlink]
static/vendor/backbone.mod.min.js [new symlink]
static/vendor/colorbrewer.mod.js [new file with mode: 0644]
static/vendor/moment-1.4.0.mod.js [new file with mode: 0644]
static/vendor/moment-1.4.0.mod.min.js [new file with mode: 0644]
static/vendor/moment.mod.js [new symlink]
static/vendor/moment.mod.min.js [new symlink]
static/vendor/showdown.mod.js [new file with mode: 0644]
static/vendor/showdown.mod.min.js [new file with mode: 0644]
static/vendor/underscore-1.3.1.min.js
static/vendor/underscore-1.3.1.mod.js [new file with mode: 0644]
static/vendor/underscore-1.3.1.mod.min.js [new file with mode: 0644]
static/vendor/underscore.mod.js [new symlink]
static/vendor/underscore.mod.min.js [new symlink]
static/vendor/underscore.string-2.0.0.mod.js [new file with mode: 0644]
static/vendor/underscore.string-2.0.0.mod.min.js [new file with mode: 0644]
static/vendor/underscore.string.mod.js [new symlink]
static/vendor/underscore.string.mod.min.js [new symlink]

diff --git a/static/vendor/backbone-0.9.2.mod.js b/static/vendor/backbone-0.9.2.mod.js
new file mode 100644 (file)
index 0000000..ce383eb
--- /dev/null
@@ -0,0 +1,1436 @@
+require.define('/node_modules/backbone.js', function(require, module, exports, __dirname, __filename, undefined){
+
+//     Backbone.js 0.9.2
+
+//     (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
+//     Backbone may be freely distributed under the MIT license.
+//     For all details and documentation:
+//     http://backbonejs.org
+
+(function(){
+
+  // Initial Setup
+  // -------------
+
+  // Save a reference to the global object (`window` in the browser, `global`
+  // on the server).
+  var root = this;
+
+  // Save the previous value of the `Backbone` variable, so that it can be
+  // restored later on, if `noConflict` is used.
+  var previousBackbone = root.Backbone;
+
+  // Create a local reference to slice/splice.
+  var slice = Array.prototype.slice;
+  var splice = Array.prototype.splice;
+
+  // The top-level namespace. All public Backbone classes and modules will
+  // be attached to this. Exported for both CommonJS and the browser.
+  var Backbone;
+  if (typeof exports !== 'undefined') {
+    Backbone = exports;
+  } else {
+    Backbone = root.Backbone = {};
+  }
+
+  // Current version of the library. Keep in sync with `package.json`.
+  Backbone.VERSION = '0.9.2';
+
+  // Require Underscore, if we're on the server, and it's not already present.
+  var _ = root._;
+  if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
+
+  // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable.
+  var $ = root.jQuery || root.Zepto || root.ender;
+
+  // Set the JavaScript library that will be used for DOM manipulation and
+  // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery,
+  // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an
+  // alternate JavaScript library (or a mock library for testing your views
+  // outside of a browser).
+  Backbone.setDomLibrary = function(lib) {
+    $ = lib;
+  };
+
+  // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
+  // to its previous owner. Returns a reference to this Backbone object.
+  Backbone.noConflict = function() {
+    root.Backbone = previousBackbone;
+    return this;
+  };
+
+  // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
+  // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
+  // set a `X-Http-Method-Override` header.
+  Backbone.emulateHTTP = false;
+
+  // Turn on `emulateJSON` to support legacy servers that can't deal with direct
+  // `application/json` requests ... will encode the body as
+  // `application/x-www-form-urlencoded` instead and will send the model in a
+  // form param named `model`.
+  Backbone.emulateJSON = false;
+
+  // Backbone.Events
+  // -----------------
+
+  // Regular expression used to split event strings
+  var eventSplitter = /\s+/;
+
+  // A module that can be mixed in to *any object* in order to provide it with
+  // custom events. You may bind with `on` or remove with `off` callback functions
+  // to an event; trigger`-ing an event fires all callbacks in succession.
+  //
+  //     var object = {};
+  //     _.extend(object, Backbone.Events);
+  //     object.on('expand', function(){ alert('expanded'); });
+  //     object.trigger('expand');
+  //
+  var Events = Backbone.Events = {
+
+    // Bind one or more space separated events, `events`, to a `callback`
+    // function. Passing `"all"` will bind the callback to all events fired.
+    on: function(events, callback, context) {
+
+      var calls, event, node, tail, list;
+      if (!callback) return this;
+      events = events.split(eventSplitter);
+      calls = this._callbacks || (this._callbacks = {});
+
+      // Create an immutable callback list, allowing traversal during
+      // modification.  The tail is an empty object that will always be used
+      // as the next node.
+      while (event = events.shift()) {
+        list = calls[event];
+        node = list ? list.tail : {};
+        node.next = tail = {};
+        node.context = context;
+        node.callback = callback;
+        calls[event] = {tail: tail, next: list ? list.next : node};
+      }
+
+      return this;
+    },
+
+    // Remove one or many callbacks. If `context` is null, removes all callbacks
+    // with that function. If `callback` is null, removes all callbacks for the
+    // event. If `events` is null, removes all bound callbacks for all events.
+    off: function(events, callback, context) {
+      var event, calls, node, tail, cb, ctx;
+
+      // No events, or removing *all* events.
+      if (!(calls = this._callbacks)) return;
+      if (!(events || callback || context)) {
+        delete this._callbacks;
+        return this;
+      }
+
+      // Loop through the listed events and contexts, splicing them out of the
+      // linked list of callbacks if appropriate.
+      events = events ? events.split(eventSplitter) : _.keys(calls);
+      while (event = events.shift()) {
+        node = calls[event];
+        delete calls[event];
+        if (!node || !(callback || context)) continue;
+        // Create a new list, omitting the indicated callbacks.
+        tail = node.tail;
+        while ((node = node.next) !== tail) {
+          cb = node.callback;
+          ctx = node.context;
+          if ((callback && cb !== callback) || (context && ctx !== context)) {
+            this.on(event, cb, ctx);
+          }
+        }
+      }
+
+      return this;
+    },
+
+    // Trigger one or many events, firing all bound callbacks. Callbacks are
+    // passed the same arguments as `trigger` is, apart from the event name
+    // (unless you're listening on `"all"`, which will cause your callback to
+    // receive the true name of the event as the first argument).
+    trigger: function(events) {
+      var event, node, calls, tail, args, all, rest;
+      if (!(calls = this._callbacks)) return this;
+      all = calls.all;
+      events = events.split(eventSplitter);
+      rest = slice.call(arguments, 1);
+
+      // For each event, walk through the linked list of callbacks twice,
+      // first to trigger the event, then to trigger any `"all"` callbacks.
+      while (event = events.shift()) {
+        if (node = calls[event]) {
+          tail = node.tail;
+          while ((node = node.next) !== tail) {
+            node.callback.apply(node.context || this, rest);
+          }
+        }
+        if (node = all) {
+          tail = node.tail;
+          args = [event].concat(rest);
+          while ((node = node.next) !== tail) {
+            node.callback.apply(node.context || this, args);
+          }
+        }
+      }
+
+      return this;
+    }
+
+  };
+
+  // Aliases for backwards compatibility.
+  Events.bind   = Events.on;
+  Events.unbind = Events.off;
+
+  // Backbone.Model
+  // --------------
+
+  // Create a new model, with defined attributes. A client id (`cid`)
+  // is automatically generated and assigned for you.
+  var Model = Backbone.Model = function(attributes, options) {
+    var defaults;
+    attributes || (attributes = {});
+    if (options && options.parse) attributes = this.parse(attributes);
+    if (defaults = getValue(this, 'defaults')) {
+      attributes = _.extend({}, defaults, attributes);
+    }
+    if (options && options.collection) this.collection = options.collection;
+    this.attributes = {};
+    this._escapedAttributes = {};
+    this.cid = _.uniqueId('c');
+    this.changed = {};
+    this._silent = {};
+    this._pending = {};
+    this.set(attributes, {silent: true});
+    // Reset change tracking.
+    this.changed = {};
+    this._silent = {};
+    this._pending = {};
+    this._previousAttributes = _.clone(this.attributes);
+    this.initialize.apply(this, arguments);
+  };
+
+  // Attach all inheritable methods to the Model prototype.
+  _.extend(Model.prototype, Events, {
+
+    // A hash of attributes whose current and previous value differ.
+    changed: null,
+
+    // A hash of attributes that have silently changed since the last time
+    // `change` was called.  Will become pending attributes on the next call.
+    _silent: null,
+
+    // A hash of attributes that have changed since the last `'change'` event
+    // began.
+    _pending: null,
+
+    // The default name for the JSON `id` attribute is `"id"`. MongoDB and
+    // CouchDB users may want to set this to `"_id"`.
+    idAttribute: 'id',
+
+    // Initialize is an empty function by default. Override it with your own
+    // initialization logic.
+    initialize: function(){},
+
+    // Return a copy of the model's `attributes` object.
+    toJSON: function(options) {
+      return _.clone(this.attributes);
+    },
+
+    // Get the value of an attribute.
+    get: function(attr) {
+      return this.attributes[attr];
+    },
+
+    // Get the HTML-escaped value of an attribute.
+    escape: function(attr) {
+      var html;
+      if (html = this._escapedAttributes[attr]) return html;
+      var val = this.get(attr);
+      return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val);
+    },
+
+    // Returns `true` if the attribute contains a value that is not null
+    // or undefined.
+    has: function(attr) {
+      return this.get(attr) != null;
+    },
+
+    // Set a hash of model attributes on the object, firing `"change"` unless
+    // you choose to silence it.
+    set: function(key, value, options) {
+      var attrs, attr, val;
+
+      // Handle both `"key", value` and `{key: value}` -style arguments.
+      if (_.isObject(key) || key == null) {
+        attrs = key;
+        options = value;
+      } else {
+        attrs = {};
+        attrs[key] = value;
+      }
+
+      // Extract attributes and options.
+      options || (options = {});
+      if (!attrs) return this;
+      if (attrs instanceof Model) attrs = attrs.attributes;
+      if (options.unset) for (attr in attrs) attrs[attr] = void 0;
+
+      // Run validation.
+      if (!this._validate(attrs, options)) return false;
+
+      // Check for changes of `id`.
+      if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
+
+      var changes = options.changes = {};
+      var now = this.attributes;
+      var escaped = this._escapedAttributes;
+      var prev = this._previousAttributes || {};
+
+      // For each `set` attribute...
+      for (attr in attrs) {
+        val = attrs[attr];
+
+        // If the new and current value differ, record the change.
+        if (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) {
+          delete escaped[attr];
+          (options.silent ? this._silent : changes)[attr] = true;
+        }
+
+        // Update or delete the current value.
+        options.unset ? delete now[attr] : now[attr] = val;
+
+        // If the new and previous value differ, record the change.  If not,
+        // then remove changes for this attribute.
+        if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) {
+          this.changed[attr] = val;
+          if (!options.silent) this._pending[attr] = true;
+        } else {
+          delete this.changed[attr];
+          delete this._pending[attr];
+        }
+      }
+
+      // Fire the `"change"` events.
+      if (!options.silent) this.change(options);
+      return this;
+    },
+
+    // Remove an attribute from the model, firing `"change"` unless you choose
+    // to silence it. `unset` is a noop if the attribute doesn't exist.
+    unset: function(attr, options) {
+      (options || (options = {})).unset = true;
+      return this.set(attr, null, options);
+    },
+
+    // Clear all attributes on the model, firing `"change"` unless you choose
+    // to silence it.
+    clear: function(options) {
+      (options || (options = {})).unset = true;
+      return this.set(_.clone(this.attributes), options);
+    },
+
+    // Fetch the model from the server. If the server's representation of the
+    // model differs from its current attributes, they will be overriden,
+    // triggering a `"change"` event.
+    fetch: function(options) {
+      options = options ? _.clone(options) : {};
+      var model = this;
+      var success = options.success;
+      options.success = function(resp, status, xhr) {
+        if (!model.set(model.parse(resp, xhr), options)) return false;
+        if (success) success(model, resp);
+      };
+      options.error = Backbone.wrapError(options.error, model, options);
+      return (this.sync || Backbone.sync).call(this, 'read', this, options);
+    },
+
+    // Set a hash of model attributes, and sync the model to the server.
+    // If the server returns an attributes hash that differs, the model's
+    // state will be `set` again.
+    save: function(key, value, options) {
+      var attrs, current;
+
+      // Handle both `("key", value)` and `({key: value})` -style calls.
+      if (_.isObject(key) || key == null) {
+        attrs = key;
+        options = value;
+      } else {
+        attrs = {};
+        attrs[key] = value;
+      }
+      options = options ? _.clone(options) : {};
+
+      // If we're "wait"-ing to set changed attributes, validate early.
+      if (options.wait) {
+        if (!this._validate(attrs, options)) return false;
+        current = _.clone(this.attributes);
+      }
+
+      // Regular saves `set` attributes before persisting to the server.
+      var silentOptions = _.extend({}, options, {silent: true});
+      if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) {
+        return false;
+      }
+
+      // After a successful server-side save, the client is (optionally)
+      // updated with the server-side state.
+      var model = this;
+      var success = options.success;
+      options.success = function(resp, status, xhr) {
+        var serverAttrs = model.parse(resp, xhr);
+        if (options.wait) {
+          delete options.wait;
+          serverAttrs = _.extend(attrs || {}, serverAttrs);
+        }
+        if (!model.set(serverAttrs, options)) return false;
+        if (success) {
+          success(model, resp);
+        } else {
+          model.trigger('sync', model, resp, options);
+        }
+      };
+
+      // Finish configuring and sending the Ajax request.
+      options.error = Backbone.wrapError(options.error, model, options);
+      var method = this.isNew() ? 'create' : 'update';
+      var xhr = (this.sync || Backbone.sync).call(this, method, this, options);
+      if (options.wait) this.set(current, silentOptions);
+      return xhr;
+    },
+
+    // Destroy this model on the server if it was already persisted.
+    // Optimistically removes the model from its collection, if it has one.
+    // If `wait: true` is passed, waits for the server to respond before removal.
+    destroy: function(options) {
+      options = options ? _.clone(options) : {};
+      var model = this;
+      var success = options.success;
+
+      var triggerDestroy = function() {
+        model.trigger('destroy', model, model.collection, options);
+      };
+
+      if (this.isNew()) {
+        triggerDestroy();
+        return false;
+      }
+
+      options.success = function(resp) {
+        if (options.wait) triggerDestroy();
+        if (success) {
+          success(model, resp);
+        } else {
+          model.trigger('sync', model, resp, options);
+        }
+      };
+
+      options.error = Backbone.wrapError(options.error, model, options);
+      var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options);
+      if (!options.wait) triggerDestroy();
+      return xhr;
+    },
+
+    // Default URL for the model's representation on the server -- if you're
+    // using Backbone's restful methods, override this to change the endpoint
+    // that will be called.
+    url: function() {
+      var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError();
+      if (this.isNew()) return base;
+      return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
+    },
+
+    // **parse** converts a response into the hash of attributes to be `set` on
+    // the model. The default implementation is just to pass the response along.
+    parse: function(resp, xhr) {
+      return resp;
+    },
+
+    // Create a new model with identical attributes to this one.
+    clone: function() {
+      return new this.constructor(this.attributes);
+    },
+
+    // A model is new if it has never been saved to the server, and lacks an id.
+    isNew: function() {
+      return this.id == null;
+    },
+
+    // Call this method to manually fire a `"change"` event for this model and
+    // a `"change:attribute"` event for each changed attribute.
+    // Calling this will cause all objects observing the model to update.
+    change: function(options) {
+      options || (options = {});
+      var changing = this._changing;
+      this._changing = true;
+
+      // Silent changes become pending changes.
+      for (var attr in this._silent) this._pending[attr] = true;
+
+      // Silent changes are triggered.
+      var changes = _.extend({}, options.changes, this._silent);
+      this._silent = {};
+      for (var attr in changes) {
+        this.trigger('change:' + attr, this, this.get(attr), options);
+      }
+      if (changing) return this;
+
+      // Continue firing `"change"` events while there are pending changes.
+      while (!_.isEmpty(this._pending)) {
+        this._pending = {};
+        this.trigger('change', this, options);
+        // Pending and silent changes still remain.
+        for (var attr in this.changed) {
+          if (this._pending[attr] || this._silent[attr]) continue;
+          delete this.changed[attr];
+        }
+        this._previousAttributes = _.clone(this.attributes);
+      }
+
+      this._changing = false;
+      return this;
+    },
+
+    // Determine if the model has changed since the last `"change"` event.
+    // If you specify an attribute name, determine if that attribute has changed.
+    hasChanged: function(attr) {
+      if (!arguments.length) return !_.isEmpty(this.changed);
+      return _.has(this.changed, attr);
+    },
+
+    // Return an object containing all the attributes that have changed, or
+    // false if there are no changed attributes. Useful for determining what
+    // parts of a view need to be updated and/or what attributes need to be
+    // persisted to the server. Unset attributes will be set to undefined.
+    // You can also pass an attributes object to diff against the model,
+    // determining if there *would be* a change.
+    changedAttributes: function(diff) {
+      if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
+      var val, changed = false, old = this._previousAttributes;
+      for (var attr in diff) {
+        if (_.isEqual(old[attr], (val = diff[attr]))) continue;
+        (changed || (changed = {}))[attr] = val;
+      }
+      return changed;
+    },
+
+    // Get the previous value of an attribute, recorded at the time the last
+    // `"change"` event was fired.
+    previous: function(attr) {
+      if (!arguments.length || !this._previousAttributes) return null;
+      return this._previousAttributes[attr];
+    },
+
+    // Get all of the attributes of the model at the time of the previous
+    // `"change"` event.
+    previousAttributes: function() {
+      return _.clone(this._previousAttributes);
+    },
+
+    // Check if the model is currently in a valid state. It's only possible to
+    // get into an *invalid* state if you're using silent changes.
+    isValid: function() {
+      return !this.validate(this.attributes);
+    },
+
+    // Run validation against the next complete set of model attributes,
+    // returning `true` if all is well. If a specific `error` callback has
+    // been passed, call that instead of firing the general `"error"` event.
+    _validate: function(attrs, options) {
+      if (options.silent || !this.validate) return true;
+      attrs = _.extend({}, this.attributes, attrs);
+      var error = this.validate(attrs, options);
+      if (!error) return true;
+      if (options && options.error) {
+        options.error(this, error, options);
+      } else {
+        this.trigger('error', this, error, options);
+      }
+      return false;
+    }
+
+  });
+
+  // Backbone.Collection
+  // -------------------
+
+  // Provides a standard collection class for our sets of models, ordered
+  // or unordered. If a `comparator` is specified, the Collection will maintain
+  // its models in sort order, as they're added and removed.
+  var Collection = Backbone.Collection = function(models, options) {
+    options || (options = {});
+    if (options.model) this.model = options.model;
+    if (options.comparator) this.comparator = options.comparator;
+    this._reset();
+    this.initialize.apply(this, arguments);
+    if (models) this.reset(models, {silent: true, parse: options.parse});
+  };
+
+  // Define the Collection's inheritable methods.
+  _.extend(Collection.prototype, Events, {
+
+    // The default model for a collection is just a **Backbone.Model**.
+    // This should be overridden in most cases.
+    model: Model,
+
+    // Initialize is an empty function by default. Override it with your own
+    // initialization logic.
+    initialize: function(){},
+
+    // The JSON representation of a Collection is an array of the
+    // models' attributes.
+    toJSON: function(options) {
+      return this.map(function(model){ return model.toJSON(options); });
+    },
+
+    // Add a model, or list of models to the set. Pass **silent** to avoid
+    // firing the `add` event for every new model.
+    add: function(models, options) {
+      var i, index, length, model, cid, id, cids = {}, ids = {}, dups = [];
+      options || (options = {});
+      models = _.isArray(models) ? models.slice() : [models];
+
+      // Begin by turning bare objects into model references, and preventing
+      // invalid models or duplicate models from being added.
+      for (i = 0, length = models.length; i < length; i++) {
+        if (!(model = models[i] = this._prepareModel(models[i], options))) {
+          throw new Error("Can't add an invalid model to a collection");
+        }
+        cid = model.cid;
+        id = model.id;
+        if (cids[cid] || this._byCid[cid] || ((id != null) && (ids[id] || this._byId[id]))) {
+          dups.push(i);
+          continue;
+        }
+        cids[cid] = ids[id] = model;
+      }
+
+      // Remove duplicates.
+      i = dups.length;
+      while (i--) {
+        models.splice(dups[i], 1);
+      }
+
+      // Listen to added models' events, and index models for lookup by
+      // `id` and by `cid`.
+      for (i = 0, length = models.length; i < length; i++) {
+        (model = models[i]).on('all', this._onModelEvent, this);
+        this._byCid[model.cid] = model;
+        if (model.id != null) this._byId[model.id] = model;
+      }
+
+      // Insert models into the collection, re-sorting if needed, and triggering
+      // `add` events unless silenced.
+      this.length += length;
+      index = options.at != null ? options.at : this.models.length;
+      splice.apply(this.models, [index, 0].concat(models));
+      if (this.comparator) this.sort({silent: true});
+      if (options.silent) return this;
+      for (i = 0, length = this.models.length; i < length; i++) {
+        if (!cids[(model = this.models[i]).cid]) continue;
+        options.index = i;
+        model.trigger('add', model, this, options);
+      }
+      return this;
+    },
+
+    // Remove a model, or a list of models from the set. Pass silent to avoid
+    // firing the `remove` event for every model removed.
+    remove: function(models, options) {
+      var i, l, index, model;
+      options || (options = {});
+      models = _.isArray(models) ? models.slice() : [models];
+      for (i = 0, l = models.length; i < l; i++) {
+        model = this.getByCid(models[i]) || this.get(models[i]);
+        if (!model) continue;
+        delete this._byId[model.id];
+        delete this._byCid[model.cid];
+        index = this.indexOf(model);
+        this.models.splice(index, 1);
+        this.length--;
+        if (!options.silent) {
+          options.index = index;
+          model.trigger('remove', model, this, options);
+        }
+        this._removeReference(model);
+      }
+      return this;
+    },
+
+    // Add a model to the end of the collection.
+    push: function(model, options) {
+      model = this._prepareModel(model, options);
+      this.add(model, options);
+      return model;
+    },
+
+    // Remove a model from the end of the collection.
+    pop: function(options) {
+      var model = this.at(this.length - 1);
+      this.remove(model, options);
+      return model;
+    },
+
+    // Add a model to the beginning of the collection.
+    unshift: function(model, options) {
+      model = this._prepareModel(model, options);
+      this.add(model, _.extend({at: 0}, options));
+      return model;
+    },
+
+    // Remove a model from the beginning of the collection.
+    shift: function(options) {
+      var model = this.at(0);
+      this.remove(model, options);
+      return model;
+    },
+
+    // Get a model from the set by id.
+    get: function(id) {
+      if (id == null) return void 0;
+      return this._byId[id.id != null ? id.id : id];
+    },
+
+    // Get a model from the set by client id.
+    getByCid: function(cid) {
+      return cid && this._byCid[cid.cid || cid];
+    },
+
+    // Get the model at the given index.
+    at: function(index) {
+      return this.models[index];
+    },
+
+    // Return models with matching attributes. Useful for simple cases of `filter`.
+    where: function(attrs) {
+      if (_.isEmpty(attrs)) return [];
+      return this.filter(function(model) {
+        for (var key in attrs) {
+          if (attrs[key] !== model.get(key)) return false;
+        }
+        return true;
+      });
+    },
+
+    // Force the collection to re-sort itself. You don't need to call this under
+    // normal circumstances, as the set will maintain sort order as each item
+    // is added.
+    sort: function(options) {
+      options || (options = {});
+      if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
+      var boundComparator = _.bind(this.comparator, this);
+      if (this.comparator.length == 1) {
+        this.models = this.sortBy(boundComparator);
+      } else {
+        this.models.sort(boundComparator);
+      }
+      if (!options.silent) this.trigger('reset', this, options);
+      return this;
+    },
+
+    // Pluck an attribute from each model in the collection.
+    pluck: function(attr) {
+      return _.map(this.models, function(model){ return model.get(attr); });
+    },
+
+    // When you have more items than you want to add or remove individually,
+    // you can reset the entire set with a new list of models, without firing
+    // any `add` or `remove` events. Fires `reset` when finished.
+    reset: function(models, options) {
+      models  || (models = []);
+      options || (options = {});
+      for (var i = 0, l = this.models.length; i < l; i++) {
+        this._removeReference(this.models[i]);
+      }
+      this._reset();
+      this.add(models, _.extend({silent: true}, options));
+      if (!options.silent) this.trigger('reset', this, options);
+      return this;
+    },
+
+    // Fetch the default set of models for this collection, resetting the
+    // collection when they arrive. If `add: true` is passed, appends the
+    // models to the collection instead of resetting.
+    fetch: function(options) {
+      options = options ? _.clone(options) : {};
+      if (options.parse === undefined) options.parse = true;
+      var collection = this;
+      var success = options.success;
+      options.success = function(resp, status, xhr) {
+        collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
+        if (success) success(collection, resp);
+      };
+      options.error = Backbone.wrapError(options.error, collection, options);
+      return (this.sync || Backbone.sync).call(this, 'read', this, options);
+    },
+
+    // Create a new instance of a model in this collection. Add the model to the
+    // collection immediately, unless `wait: true` is passed, in which case we
+    // wait for the server to agree.
+    create: function(model, options) {
+      var coll = this;
+      options = options ? _.clone(options) : {};
+      model = this._prepareModel(model, options);
+      if (!model) return false;
+      if (!options.wait) coll.add(model, options);
+      var success = options.success;
+      options.success = function(nextModel, resp, xhr) {
+        if (options.wait) coll.add(nextModel, options);
+        if (success) {
+          success(nextModel, resp);
+        } else {
+          nextModel.trigger('sync', model, resp, options);
+        }
+      };
+      model.save(null, options);
+      return model;
+    },
+
+    // **parse** converts a response into a list of models to be added to the
+    // collection. The default implementation is just to pass it through.
+    parse: function(resp, xhr) {
+      return resp;
+    },
+
+    // Proxy to _'s chain. Can't be proxied the same way the rest of the
+    // underscore methods are proxied because it relies on the underscore
+    // constructor.
+    chain: function () {
+      return _(this.models).chain();
+    },
+
+    // Reset all internal state. Called when the collection is reset.
+    _reset: function(options) {
+      this.length = 0;
+      this.models = [];
+      this._byId  = {};
+      this._byCid = {};
+    },
+
+    // Prepare a model or hash of attributes to be added to this collection.
+    _prepareModel: function(model, options) {
+      options || (options = {});
+      if (!(model instanceof Model)) {
+        var attrs = model;
+        options.collection = this;
+        model = new this.model(attrs, options);
+        if (!model._validate(model.attributes, options)) model = false;
+      } else if (!model.collection) {
+        model.collection = this;
+      }
+      return model;
+    },
+
+    // Internal method to remove a model's ties to a collection.
+    _removeReference: function(model) {
+      if (this == model.collection) {
+        delete model.collection;
+      }
+      model.off('all', this._onModelEvent, this);
+    },
+
+    // Internal method called every time a model in the set fires an event.
+    // Sets need to update their indexes when models change ids. All other
+    // events simply proxy through. "add" and "remove" events that originate
+    // in other collections are ignored.
+    _onModelEvent: function(event, model, collection, options) {
+      if ((event == 'add' || event == 'remove') && collection != this) return;
+      if (event == 'destroy') {
+        this.remove(model, options);
+      }
+      if (model && event === 'change:' + model.idAttribute) {
+        delete this._byId[model.previous(model.idAttribute)];
+        this._byId[model.id] = model;
+      }
+      this.trigger.apply(this, arguments);
+    }
+
+  });
+
+  // Underscore methods that we want to implement on the Collection.
+  var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find',
+    'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any',
+    'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex',
+    'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf',
+    'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy'];
+
+  // Mix in each Underscore method as a proxy to `Collection#models`.
+  _.each(methods, function(method) {
+    Collection.prototype[method] = function() {
+      return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
+    };
+  });
+
+  // Backbone.Router
+  // -------------------
+
+  // Routers map faux-URLs to actions, and fire events when routes are
+  // matched. Creating a new one sets its `routes` hash, if not set statically.
+  var Router = Backbone.Router = function(options) {
+    options || (options = {});
+    if (options.routes) this.routes = options.routes;
+    this._bindRoutes();
+    this.initialize.apply(this, arguments);
+  };
+
+  // Cached regular expressions for matching named param parts and splatted
+  // parts of route strings.
+  var namedParam    = /:\w+/g;
+  var splatParam    = /\*\w+/g;
+  var escapeRegExp  = /[-[\]{}()+?.,\\^$|#\s]/g;
+
+  // Set up all inheritable **Backbone.Router** properties and methods.
+  _.extend(Router.prototype, Events, {
+
+    // Initialize is an empty function by default. Override it with your own
+    // initialization logic.
+    initialize: function(){},
+
+    // Manually bind a single named route to a callback. For example:
+    //
+    //     this.route('search/:query/p:num', 'search', function(query, num) {
+    //       ...
+    //     });
+    //
+    route: function(route, name, callback) {
+      Backbone.history || (Backbone.history = new History);
+      if (!_.isRegExp(route)) route = this._routeToRegExp(route);
+      if (!callback) callback = this[name];
+      Backbone.history.route(route, _.bind(function(fragment) {
+        var args = this._extractParameters(route, fragment);
+        callback && callback.apply(this, args);
+        this.trigger.apply(this, ['route:' + name].concat(args));
+        Backbone.history.trigger('route', this, name, args);
+      }, this));
+      return this;
+    },
+
+    // Simple proxy to `Backbone.history` to save a fragment into the history.
+    navigate: function(fragment, options) {
+      Backbone.history.navigate(fragment, options);
+    },
+
+    // Bind all defined routes to `Backbone.history`. We have to reverse the
+    // order of the routes here to support behavior where the most general
+    // routes can be defined at the bottom of the route map.
+    _bindRoutes: function() {
+      if (!this.routes) return;
+      var routes = [];
+      for (var route in this.routes) {
+        routes.unshift([route, this.routes[route]]);
+      }
+      for (var i = 0, l = routes.length; i < l; i++) {
+        this.route(routes[i][0], routes[i][1], this[routes[i][1]]);
+      }
+    },
+
+    // Convert a route string into a regular expression, suitable for matching
+    // against the current location hash.
+    _routeToRegExp: function(route) {
+      route = route.replace(escapeRegExp, '\\$&')
+                   .replace(namedParam, '([^\/]+)')
+                   .replace(splatParam, '(.*?)');
+      return new RegExp('^' + route + '$');
+    },
+
+    // Given a route, and a URL fragment that it matches, return the array of
+    // extracted parameters.
+    _extractParameters: function(route, fragment) {
+      return route.exec(fragment).slice(1);
+    }
+
+  });
+
+  // Backbone.History
+  // ----------------
+
+  // Handles cross-browser history management, based on URL fragments. If the
+  // browser does not support `onhashchange`, falls back to polling.
+  var History = Backbone.History = function() {
+    this.handlers = [];
+    _.bindAll(this, 'checkUrl');
+  };
+
+  // Cached regex for cleaning leading hashes and slashes .
+  var routeStripper = /^[#\/]/;
+
+  // Cached regex for detecting MSIE.
+  var isExplorer = /msie [\w.]+/;
+
+  // Has the history handling already been started?
+  History.started = false;
+
+  // Set up all inheritable **Backbone.History** properties and methods.
+  _.extend(History.prototype, Events, {
+
+    // The default interval to poll for hash changes, if necessary, is
+    // twenty times a second.
+    interval: 50,
+
+    // Gets the true hash value. Cannot use location.hash directly due to bug
+    // in Firefox where location.hash will always be decoded.
+    getHash: function(windowOverride) {
+      var loc = windowOverride ? windowOverride.location : window.location;
+      var match = loc.href.match(/#(.*)$/);
+      return match ? match[1] : '';
+    },
+
+    // Get the cross-browser normalized URL fragment, either from the URL,
+    // the hash, or the override.
+    getFragment: function(fragment, forcePushState) {
+      if (fragment == null) {
+        if (this._hasPushState || forcePushState) {
+          fragment = window.location.pathname;
+          var search = window.location.search;
+          if (search) fragment += search;
+        } else {
+          fragment = this.getHash();
+        }
+      }
+      if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length);
+      return fragment.replace(routeStripper, '');
+    },
+
+    // Start the hash change handling, returning `true` if the current URL matches
+    // an existing route, and `false` otherwise.
+    start: function(options) {
+      if (History.started) throw new Error("Backbone.history has already been started");
+      History.started = true;
+
+      // Figure out the initial configuration. Do we need an iframe?
+      // Is pushState desired ... is it available?
+      this.options          = _.extend({}, {root: '/'}, this.options, options);
+      this._wantsHashChange = this.options.hashChange !== false;
+      this._wantsPushState  = !!this.options.pushState;
+      this._hasPushState    = !!(this.options.pushState && window.history && window.history.pushState);
+      var fragment          = this.getFragment();
+      var docMode           = document.documentMode;
+      var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
+
+      if (oldIE) {
+        this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
+        this.navigate(fragment);
+      }
+
+      // Depending on whether we're using pushState or hashes, and whether
+      // 'onhashchange' is supported, determine how we check the URL state.
+      if (this._hasPushState) {
+        $(window).bind('popstate', this.checkUrl);
+      } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
+        $(window).bind('hashchange', this.checkUrl);
+      } else if (this._wantsHashChange) {
+        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
+      }
+
+      // Determine if we need to change the base url, for a pushState link
+      // opened by a non-pushState browser.
+      this.fragment = fragment;
+      var loc = window.location;
+      var atRoot  = loc.pathname == this.options.root;
+
+      // If we've started off with a route from a `pushState`-enabled browser,
+      // but we're currently in a browser that doesn't support it...
+      if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {
+        this.fragment = this.getFragment(null, true);
+        window.location.replace(this.options.root + '#' + this.fragment);
+        // Return immediately as browser will do redirect to new url
+        return true;
+
+      // Or if we've started out with a hash-based route, but we're currently
+      // in a browser where it could be `pushState`-based instead...
+      } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
+        this.fragment = this.getHash().replace(routeStripper, '');
+        window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
+      }
+
+      if (!this.options.silent) {
+        return this.loadUrl();
+      }
+    },
+
+    // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
+    // but possibly useful for unit testing Routers.
+    stop: function() {
+      $(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl);
+      clearInterval(this._checkUrlInterval);
+      History.started = false;
+    },
+
+    // Add a route to be tested when the fragment changes. Routes added later
+    // may override previous routes.
+    route: function(route, callback) {
+      this.handlers.unshift({route: route, callback: callback});
+    },
+
+    // Checks the current URL to see if it has changed, and if it has,
+    // calls `loadUrl`, normalizing across the hidden iframe.
+    checkUrl: function(e) {
+      var current = this.getFragment();
+      if (current == this.fragment && this.iframe) current = this.getFragment(this.getHash(this.iframe));
+      if (current == this.fragment) return false;
+      if (this.iframe) this.navigate(current);
+      this.loadUrl() || this.loadUrl(this.getHash());
+    },
+
+    // Attempt to load the current URL fragment. If a route succeeds with a
+    // match, returns `true`. If no defined routes matches the fragment,
+    // returns `false`.
+    loadUrl: function(fragmentOverride) {
+      var fragment = this.fragment = this.getFragment(fragmentOverride);
+      var matched = _.any(this.handlers, function(handler) {
+        if (handler.route.test(fragment)) {
+          handler.callback(fragment);
+          return true;
+        }
+      });
+      return matched;
+    },
+
+    // Save a fragment into the hash history, or replace the URL state if the
+    // 'replace' option is passed. You are responsible for properly URL-encoding
+    // the fragment in advance.
+    //
+    // The options object can contain `trigger: true` if you wish to have the
+    // route callback be fired (not usually desirable), or `replace: true`, if
+    // you wish to modify the current URL without adding an entry to the history.
+    navigate: function(fragment, options) {
+      if (!History.started) return false;
+      if (!options || options === true) options = {trigger: options};
+      var frag = (fragment || '').replace(routeStripper, '');
+      if (this.fragment == frag) return;
+
+      // If pushState is available, we use it to set the fragment as a real URL.
+      if (this._hasPushState) {
+        if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag;
+        this.fragment = frag;
+        window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag);
+
+      // If hash changes haven't been explicitly disabled, update the hash
+      // fragment to store history.
+      } else if (this._wantsHashChange) {
+        this.fragment = frag;
+        this._updateHash(window.location, frag, options.replace);
+        if (this.iframe && (frag != this.getFragment(this.getHash(this.iframe)))) {
+          // Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change.
+          // When replace is true, we don't want this.
+          if(!options.replace) this.iframe.document.open().close();
+          this._updateHash(this.iframe.location, frag, options.replace);
+        }
+
+      // If you've told us that you explicitly don't want fallback hashchange-
+      // based history, then `navigate` becomes a page refresh.
+      } else {
+        window.location.assign(this.options.root + fragment);
+      }
+      if (options.trigger) this.loadUrl(fragment);
+    },
+
+    // Update the hash location, either replacing the current entry, or adding
+    // a new one to the browser history.
+    _updateHash: function(location, fragment, replace) {
+      if (replace) {
+        location.replace(location.toString().replace(/(javascript:|#).*$/, '') + '#' + fragment);
+      } else {
+        location.hash = fragment;
+      }
+    }
+  });
+
+  // Backbone.View
+  // -------------
+
+  // Creating a Backbone.View creates its initial element outside of the DOM,
+  // if an existing element is not provided...
+  var View = Backbone.View = function(options) {
+    this.cid = _.uniqueId('view');
+    this._configure(options || {});
+    this._ensureElement();
+    this.initialize.apply(this, arguments);
+    this.delegateEvents();
+  };
+
+  // Cached regex to split keys for `delegate`.
+  var delegateEventSplitter = /^(\S+)\s*(.*)$/;
+
+  // List of view options to be merged as properties.
+  var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
+
+  // Set up all inheritable **Backbone.View** properties and methods.
+  _.extend(View.prototype, Events, {
+
+    // The default `tagName` of a View's element is `"div"`.
+    tagName: 'div',
+
+    // jQuery delegate for element lookup, scoped to DOM elements within the
+    // current view. This should be prefered to global lookups where possible.
+    $: function(selector) {
+      return this.$el.find(selector);
+    },
+
+    // Initialize is an empty function by default. Override it with your own
+    // initialization logic.
+    initialize: function(){},
+
+    // **render** is the core function that your view should override, in order
+    // to populate its element (`this.el`), with the appropriate HTML. The
+    // convention is for **render** to always return `this`.
+    render: function() {
+      return this;
+    },
+
+    // Remove this view from the DOM. Note that the view isn't present in the
+    // DOM by default, so calling this method may be a no-op.
+    remove: function() {
+      this.$el.remove();
+      return this;
+    },
+
+    // For small amounts of DOM Elements, where a full-blown template isn't
+    // needed, use **make** to manufacture elements, one at a time.
+    //
+    //     var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
+    //
+    make: function(tagName, attributes, content) {
+      var el = document.createElement(tagName);
+      if (attributes) $(el).attr(attributes);
+      if (content) $(el).html(content);
+      return el;
+    },
+
+    // Change the view's element (`this.el` property), including event
+    // re-delegation.
+    setElement: function(element, delegate) {
+      if (this.$el) this.undelegateEvents();
+      this.$el = (element instanceof $) ? element : $(element);
+      this.el = this.$el[0];
+      if (delegate !== false) this.delegateEvents();
+      return this;
+    },
+
+    // Set callbacks, where `this.events` is a hash of
+    //
+    // *{"event selector": "callback"}*
+    //
+    //     {
+    //       'mousedown .title':  'edit',
+    //       'click .button':     'save'
+    //       'click .open':       function(e) { ... }
+    //     }
+    //
+    // pairs. Callbacks will be bound to the view, with `this` set properly.
+    // Uses event delegation for efficiency.
+    // Omitting the selector binds the event to `this.el`.
+    // This only works for delegate-able events: not `focus`, `blur`, and
+    // not `change`, `submit`, and `reset` in Internet Explorer.
+    delegateEvents: function(events) {
+      if (!(events || (events = getValue(this, 'events')))) return;
+      this.undelegateEvents();
+      for (var key in events) {
+        var method = events[key];
+        if (!_.isFunction(method)) method = this[events[key]];
+        if (!method) throw new Error('Method "' + events[key] + '" does not exist');
+        var match = key.match(delegateEventSplitter);
+        var eventName = match[1], selector = match[2];
+        method = _.bind(method, this);
+        eventName += '.delegateEvents' + this.cid;
+        if (selector === '') {
+          this.$el.bind(eventName, method);
+        } else {
+          this.$el.delegate(selector, eventName, method);
+        }
+      }
+    },
+
+    // Clears all callbacks previously bound to the view with `delegateEvents`.
+    // You usually don't need to use this, but may wish to if you have multiple
+    // Backbone views attached to the same DOM element.
+    undelegateEvents: function() {
+      this.$el.unbind('.delegateEvents' + this.cid);
+    },
+
+    // Performs the initial configuration of a View with a set of options.
+    // Keys with special meaning *(model, collection, id, className)*, are
+    // attached directly to the view.
+    _configure: function(options) {
+      if (this.options) options = _.extend({}, this.options, options);
+      for (var i = 0, l = viewOptions.length; i < l; i++) {
+        var attr = viewOptions[i];
+        if (options[attr]) this[attr] = options[attr];
+      }
+      this.options = options;
+    },
+
+    // Ensure that the View has a DOM element to render into.
+    // If `this.el` is a string, pass it through `$()`, take the first
+    // matching element, and re-assign it to `el`. Otherwise, create
+    // an element from the `id`, `className` and `tagName` properties.
+    _ensureElement: function() {
+      if (!this.el) {
+        var attrs = getValue(this, 'attributes') || {};
+        if (this.id) attrs.id = this.id;
+        if (this.className) attrs['class'] = this.className;
+        this.setElement(this.make(this.tagName, attrs), false);
+      } else {
+        this.setElement(this.el, false);
+      }
+    }
+
+  });
+
+  // The self-propagating extend function that Backbone classes use.
+  var extend = function (protoProps, classProps) {
+    var child = inherits(this, protoProps, classProps);
+    child.extend = this.extend;
+    return child;
+  };
+
+  // Set up inheritance for the model, collection, and view.
+  Model.extend = Collection.extend = Router.extend = View.extend = extend;
+
+  // Backbone.sync
+  // -------------
+
+  // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
+  var methodMap = {
+    'create': 'POST',
+    'update': 'PUT',
+    'delete': 'DELETE',
+    'read':   'GET'
+  };
+
+  // Override this function to change the manner in which Backbone persists
+  // models to the server. You will be passed the type of request, and the
+  // model in question. By default, makes a RESTful Ajax request
+  // to the model's `url()`. Some possible customizations could be:
+  //
+  // * Use `setTimeout` to batch rapid-fire updates into a single request.
+  // * Send up the models as XML instead of JSON.
+  // * Persist models via WebSockets instead of Ajax.
+  //
+  // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
+  // as `POST`, with a `_method` parameter containing the true HTTP method,
+  // as well as all requests with the body as `application/x-www-form-urlencoded`
+  // instead of `application/json` with the model in a param named `model`.
+  // Useful when interfacing with server-side languages like **PHP** that make
+  // it difficult to read the body of `PUT` requests.
+  Backbone.sync = function(method, model, options) {
+    var type = methodMap[method];
+
+    // Default options, unless specified.
+    options || (options = {});
+
+    // Default JSON-request options.
+    var params = {type: type, dataType: 'json'};
+
+    // Ensure that we have a URL.
+    if (!options.url) {
+      params.url = getValue(model, 'url') || urlError();
+    }
+
+    // Ensure that we have the appropriate request data.
+    if (!options.data && model && (method == 'create' || method == 'update')) {
+      params.contentType = 'application/json';
+      params.data = JSON.stringify(model.toJSON());
+    }
+
+    // For older servers, emulate JSON by encoding the request into an HTML-form.
+    if (Backbone.emulateJSON) {
+      params.contentType = 'application/x-www-form-urlencoded';
+      params.data = params.data ? {model: params.data} : {};
+    }
+
+    // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
+    // And an `X-HTTP-Method-Override` header.
+    if (Backbone.emulateHTTP) {
+      if (type === 'PUT' || type === 'DELETE') {
+        if (Backbone.emulateJSON) params.data._method = type;
+        params.type = 'POST';
+        params.beforeSend = function(xhr) {
+          xhr.setRequestHeader('X-HTTP-Method-Override', type);
+        };
+      }
+    }
+
+    // Don't process data on a non-GET request.
+    if (params.type !== 'GET' && !Backbone.emulateJSON) {
+      params.processData = false;
+    }
+
+    // Make the request, allowing the user to override any Ajax options.
+    return $.ajax(_.extend(params, options));
+  };
+
+  // Wrap an optional error callback with a fallback error event.
+  Backbone.wrapError = function(onError, originalModel, options) {
+    return function(model, resp) {
+      resp = model === originalModel ? resp : model;
+      if (onError) {
+        onError(originalModel, resp, options);
+      } else {
+        originalModel.trigger('error', originalModel, resp, options);
+      }
+    };
+  };
+
+  // Helpers
+  // -------
+
+  // Shared empty constructor function to aid in prototype-chain creation.
+  var ctor = function(){};
+
+  // Helper function to correctly set up the prototype chain, for subclasses.
+  // Similar to `goog.inherits`, but uses a hash of prototype properties and
+  // class properties to be extended.
+  var inherits = function(parent, protoProps, staticProps) {
+    var child;
+
+    // The constructor function for the new subclass is either defined by you
+    // (the "constructor" property in your `extend` definition), or defaulted
+    // by us to simply call the parent's constructor.
+    if (protoProps && protoProps.hasOwnProperty('constructor')) {
+      child = protoProps.constructor;
+    } else {
+      child = function(){ parent.apply(this, arguments); };
+    }
+
+    // Inherit class (static) properties from parent.
+    _.extend(child, parent);
+
+    // Set the prototype chain to inherit from `parent`, without calling
+    // `parent`'s constructor function.
+    ctor.prototype = parent.prototype;
+    child.prototype = new ctor();
+
+    // Add prototype properties (instance properties) to the subclass,
+    // if supplied.
+    if (protoProps) _.extend(child.prototype, protoProps);
+
+    // Add static properties to the constructor function, if supplied.
+    if (staticProps) _.extend(child, staticProps);
+
+    // Correctly set child's `prototype.constructor`.
+    child.prototype.constructor = child;
+
+    // Set a convenience property in case the parent's prototype is needed later.
+    child.__super__ = parent.prototype;
+
+    return child;
+  };
+
+  // Helper function to get a value from a Backbone object as a property
+  // or as a function.
+  var getValue = function(object, prop) {
+    if (!(object && object[prop])) return null;
+    return _.isFunction(object[prop]) ? object[prop]() : object[prop];
+  };
+
+  // Throw an error when a URL is needed, and none is supplied.
+  var urlError = function() {
+    throw new Error('A "url" property or function must be specified');
+  };
+
+}).call(this);
+
+
+});
diff --git a/static/vendor/backbone-0.9.2.mod.min.js b/static/vendor/backbone-0.9.2.mod.min.js
new file mode 100644 (file)
index 0000000..b2a3e7b
--- /dev/null
@@ -0,0 +1 @@
+require.define("/node_modules/backbone.js",function(e,t,n,r,i,s){(function(){var t=this,r=t.Backbone,i=Array.prototype.slice,o=Array.prototype.splice,u;typeof n!="undefined"?u=n:u=t.Backbone={},u.VERSION="0.9.2";var a=t._;!a&&typeof e!="undefined"&&(a=e("underscore"));var f=t.jQuery||t.Zepto||t.ender;u.setDomLibrary=function(e){f=e},u.noConflict=function(){return t.Backbone=r,this},u.emulateHTTP=!1,u.emulateJSON=!1;var l=/\s+/,c=u.Events={on:function(e,t,n){var r,i,s,o,u;if(!t)return this;e=e.split(l),r=this._callbacks||(this._callbacks={});while(i=e.shift())u=r[i],s=u?u.tail:{},s.next=o={},s.context=n,s.callback=t,r[i]={tail:o,next:u?u.next:s};return this},off:function(e,t,n){var r,i,s,o,u,f;if(!(i=this._callbacks))return;if(!(e||t||n))return delete this._callbacks,this;e=e?e.split(l):a.keys(i);while(r=e.shift()){s=i[r],delete i[r];if(!s||!t&&!n)continue;o=s.tail;while((s=s.next)!==o)u=s.callback,f=s.context,(t&&u!==t||n&&f!==n)&&this.on(r,u,f)}return this},trigger:function(e){var t,n,r,s,o,u,a;if(!(r=this._callbacks))return this;u=r.all,e=e.split(l),a=i.call(arguments,1);while(t=e.shift()){if(n=r[t]){s=n.tail;while((n=n.next)!==s)n.callback.apply(n.context||this,a)}if(n=u){s=n.tail,o=[t].concat(a);while((n=n.next)!==s)n.callback.apply(n.context||this,o)}}return this}};c.bind=c.on,c.unbind=c.off;var h=u.Model=function(e,t){var n;e||(e={}),t&&t.parse&&(e=this.parse(e));if(n=A(this,"defaults"))e=a.extend({},n,e);t&&t.collection&&(this.collection=t.collection),this.attributes={},this._escapedAttributes={},this.cid=a.uniqueId("c"),this.changed={},this._silent={},this._pending={},this.set(e,{silent:!0}),this.changed={},this._silent={},this._pending={},this._previousAttributes=a.clone(this.attributes),this.initialize.apply(this,arguments)};a.extend(h.prototype,c,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(e){return a.clone(this.attributes)},get:function(e){return this.attributes[e]},escape:function(e){var t;if(t=this._escapedAttributes[e])return t;var n=this.get(e);return this._escapedAttributes[e]=a.escape(n==null?"":""+n)},has:function(e){return this.get(e)!=null},set:function(e,t,n){var r,i,s;a.isObject(e)||e==null?(r=e,n=t):(r={},r[e]=t),n||(n={});if(!r)return this;r instanceof h&&(r=r.attributes);if(n.unset)for(i in r)r[i]=void 0;if(!this._validate(r,n))return!1;this.idAttribute in r&&(this.id=r[this.idAttribute]);var o=n.changes={},u=this.attributes,f=this._escapedAttributes,l=this._previousAttributes||{};for(i in r){s=r[i];if(!a.isEqual(u[i],s)||n.unset&&a.has(u,i))delete f[i],(n.silent?this._silent:o)[i]=!0;n.unset?delete u[i]:u[i]=s,!a.isEqual(l[i],s)||a.has(u,i)!=a.has(l,i)?(this.changed[i]=s,n.silent||(this._pending[i]=!0)):(delete this.changed[i],delete this._pending[i])}return n.silent||this.change(n),this},unset:function(e,t){return(t||(t={})).unset=!0,this.set(e,null,t)},clear:function(e){return(e||(e={})).unset=!0,this.set(a.clone(this.attributes),e)},fetch:function(e){e=e?a.clone(e):{};var t=this,n=e.success;return e.success=function(r,i,s){if(!t.set(t.parse(r,s),e))return!1;n&&n(t,r)},e.error=u.wrapError(e.error,t,e),(this.sync||u.sync).call(this,"read",this,e)},save:function(e,t,n){var r,i;a.isObject(e)||e==null?(r=e,n=t):(r={},r[e]=t),n=n?a.clone(n):{};if(n.wait){if(!this._validate(r,n))return!1;i=a.clone(this.attributes)}var s=a.extend({},n,{silent:!0});if(r&&!this.set(r,n.wait?s:n))return!1;var o=this,f=n.success;n.success=function(e,t,i){var s=o.parse(e,i);n.wait&&(delete n.wait,s=a.extend(r||{},s));if(!o.set(s,n))return!1;f?f(o,e):o.trigger("sync",o,e,n)},n.error=u.wrapError(n.error,o,n);var l=this.isNew()?"create":"update",c=(this.sync||u.sync).call(this,l,this,n);return n.wait&&this.set(i,s),c},destroy:function(e){e=e?a.clone(e):{};var t=this,n=e.success,r=function(){t.trigger("destroy",t,t.collection,e)};if(this.isNew())return r(),!1;e.success=function(i){e.wait&&r(),n?n(t,i):t.trigger("sync",t,i,e)},e.error=u.wrapError(e.error,t,e);var i=(this.sync||u.sync).call(this,"delete",this,e);return e.wait||r(),i},url:function(){var e=A(this,"urlRoot")||A(this.collection,"url")||O();return this.isNew()?e:e+(e.charAt(e.length-1)=="/"?"":"/")+encodeURIComponent(this.id)},parse:function(e,t){return e},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return this.id==null},change:function(e){e||(e={});var t=this._changing;this._changing=!0;for(var n in this._silent)this._pending[n]=!0;var r=a.extend({},e.changes,this._silent);this._silent={};for(var n in r)this.trigger("change:"+n,this,this.get(n),e);if(t)return this;while(!a.isEmpty(this._pending)){this._pending={},this.trigger("change",this,e);for(var n in this.changed){if(this._pending[n]||this._silent[n])continue;delete this.changed[n]}this._previousAttributes=a.clone(this.attributes)}return this._changing=!1,this},hasChanged:function(e){return arguments.length?a.has(this.changed,e):!a.isEmpty(this.changed)},changedAttributes:function(e){if(!e)return this.hasChanged()?a.clone(this.changed):!1;var t,n=!1,r=this._previousAttributes;for(var i in e){if(a.isEqual(r[i],t=e[i]))continue;(n||(n={}))[i]=t}return n},previous:function(e){return!arguments.length||!this._previousAttributes?null:this._previousAttributes[e]},previousAttributes:function(){return a.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(e,t){if(t.silent||!this.validate)return!0;e=a.extend({},this.attributes,e);var n=this.validate(e,t);return n?(t&&t.error?t.error(this,n,t):this.trigger("error",this,n,t),!1):!0}});var p=u.Collection=function(e,t){t||(t={}),t.model&&(this.model=t.model),t.comparator&&(this.comparator=t.comparator),this._reset(),this.initialize.apply(this,arguments),e&&this.reset(e,{silent:!0,parse:t.parse})};a.extend(p.prototype,c,{model:h,initialize:function(){},toJSON:function(e){return this.map(function(t){return t.toJSON(e)})},add:function(e,t){var n,r,i,s,u,f,l={},c={},h=[];t||(t={}),e=a.isArray(e)?e.slice():[e];for(n=0,i=e.length;n<i;n++){if(!(s=e[n]=this._prepareModel(e[n],t)))throw new Error("Can't add an invalid model to a collection");u=s.cid,f=s.id;if(l[u]||this._byCid[u]||f!=null&&(c[f]||this._byId[f])){h.push(n);continue}l[u]=c[f]=s}n=h.length;while(n--)e.splice(h[n],1);for(n=0,i=e.length;n<i;n++)(s=e[n]).on("all",this._onModelEvent,this),this._byCid[s.cid]=s,s.id!=null&&(this._byId[s.id]=s);this.length+=i,r=t.at!=null?t.at:this.models.length,o.apply(this.models,[r,0].concat(e)),this.comparator&&this.sort({silent:!0});if(t.silent)return this;for(n=0,i=this.models.length;n<i;n++){if(!l[(s=this.models[n]).cid])continue;t.index=n,s.trigger("add",s,this,t)}return this},remove:function(e,t){var n,r,i,s;t||(t={}),e=a.isArray(e)?e.slice():[e];for(n=0,r=e.length;n<r;n++){s=this.getByCid(e[n])||this.get(e[n]);if(!s)continue;delete this._byId[s.id],delete this._byCid[s.cid],i=this.indexOf(s),this.models.splice(i,1),this.length--,t.silent||(t.index=i,s.trigger("remove",s,this,t)),this._removeReference(s)}return this},push:function(e,t){return e=this._prepareModel(e,t),this.add(e,t),e},pop:function(e){var t=this.at(this.length-1);return this.remove(t,e),t},unshift:function(e,t){return e=this._prepareModel(e,t),this.add(e,a.extend({at:0},t)),e},shift:function(e){var t=this.at(0);return this.remove(t,e),t},get:function(e){return e==null?void 0:this._byId[e.id!=null?e.id:e]},getByCid:function(e){return e&&this._byCid[e.cid||e]},at:function(e){return this.models[e]},where:function(e){return a.isEmpty(e)?[]:this.filter(function(t){for(var n in e)if(e[n]!==t.get(n))return!1;return!0})},sort:function(e){e||(e={});if(!this.comparator)throw new Error("Cannot sort a set without a comparator");var t=a.bind(this.comparator,this);return this.comparator.length==1?this.models=this.sortBy(t):this.models.sort(t),e.silent||this.trigger("reset",this,e),this},pluck:function(e){return a.map(this.models,function(t){return t.get(e)})},reset:function(e,t){e||(e=[]),t||(t={});for(var n=0,r=this.models.length;n<r;n++)this._removeReference(this.models[n]);return this._reset(),this.add(e,a.extend({silent:!0},t)),t.silent||this.trigger("reset",this,t),this},fetch:function(e){e=e?a.clone(e):{},e.parse===s&&(e.parse=!0);var t=this,n=e.success;return e.success=function(r,i,s){t[e.add?"add":"reset"](t.parse(r,s),e),n&&n(t,r)},e.error=u.wrapError(e.error,t,e),(this.sync||u.sync).call(this,"read",this,e)},create:function(e,t){var n=this;t=t?a.clone(t):{},e=this._prepareModel(e,t);if(!e)return!1;t.wait||n.add(e,t);var r=t.success;return t.success=function(i,s,o){t.wait&&n.add(i,t),r?r(i,s):i.trigger("sync",e,s,t)},e.save(null,t),e},parse:function(e,t){return e},chain:function(){return a(this.models).chain()},_reset:function(e){this.length=0,this.models=[],this._byId={},this._byCid={}},_prepareModel:function(e,t){t||(t={});if(e instanceof h)e.collection||(e.collection=this);else{var n=e;t.collection=this,e=new this.model(n,t),e._validate(e.attributes,t)||(e=!1)}return e},_removeReference:function(e){this==e.collection&&delete e.collection,e.off("all",this._onModelEvent,this)},_onModelEvent:function(e,t,n,r){if((e=="add"||e=="remove")&&n!=this)return;e=="destroy"&&this.remove(t,r),t&&e==="change:"+t.idAttribute&&(delete this._byId[t.previous(t.idAttribute)],this._byId[t.id]=t),this.trigger.apply(this,arguments)}});var d=["forEach","each","map","reduce","reduceRight","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","sortBy","sortedIndex","toArray","size","first","initial","rest","last","without","indexOf","shuffle","lastIndexOf","isEmpty","groupBy"];a.each(d,function(e){p.prototype[e]=function(){return a[e].apply(a,[this.models].concat(a.toArray(arguments)))}});var v=u.Router=function(e){e||(e={}),e.routes&&(this.routes=e.routes),this._bindRoutes(),this.initialize.apply(this,arguments)},m=/:\w+/g,g=/\*\w+/g,y=/[-[\]{}()+?.,\\^$|#\s]/g;a.extend(v.prototype,c,{initialize:function(){},route:function(e,t,n){return u.history||(u.history=new b),a.isRegExp(e)||(e=this._routeToRegExp(e)),n||(n=this[t]),u.history.route(e,a.bind(function(r){var i=this._extractParameters(e,r);n&&n.apply(this,i),this.trigger.apply(this,["route:"+t].concat(i)),u.history.trigger("route",this,t,i)},this)),this},navigate:function(e,t){u.history.navigate(e,t)},_bindRoutes:function(){if(!this.routes)return;var e=[];for(var t in this.routes)e.unshift([t,this.routes[t]]);for(var n=0,r=e.length;n<r;n++)this.route(e[n][0],e[n][1],this[e[n][1]])},_routeToRegExp:function(e){return e=e.replace(y,"\\$&").replace(m,"([^/]+)").replace(g,"(.*?)"),new RegExp("^"+e+"$")},_extractParameters:function(e,t){return e.exec(t).slice(1)}});var b=u.History=function(){this.handlers=[],a.bindAll(this,"checkUrl")},w=/^[#\/]/,E=/msie [\w.]+/;b.started=!1,a.extend(b.prototype,c,{interval:50,getHash:function(e){var t=e?e.location:window.location,n=t.href.match(/#(.*)$/);return n?n[1]:""},getFragment:function(e,t){if(e==null)if(this._hasPushState||t){e=window.location.pathname;var n=window.location.search;n&&(e+=n)}else e=this.getHash();return e.indexOf(this.options.root)||(e=e.substr(this.options.root.length)),e.replace(w,"")},start:function(e){if(b.started)throw new Error("Backbone.history has already been started");b.started=!0,this.options=a.extend({},{root:"/"},this.options,e),this._wantsHashChange=this.options.hashChange!==!1,this._wantsPushState=!!this.options.pushState,this._hasPushState=!!(this.options.pushState&&window.history&&window.history.pushState);var t=this.getFragment(),n=document.documentMode,r=E.exec(navigator.userAgent.toLowerCase())&&(!n||n<=7);r&&(this.iframe=f('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(t)),this._hasPushState?f(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!r?f(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,this.interval)),this.fragment=t;var i=window.location,s=i.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!s)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&s&&i.hash&&(this.fragment=this.getHash().replace(w,""),window.history.replaceState({},document.title,i.protocol+"//"+i.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},stop:function(){f(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl),clearInterval(this._checkUrlInterval),b.started=!1},route:function(e,t){this.handlers.unshift({route:e,callback:t})},checkUrl:function(e){var t=this.getFragment();t==this.fragment&&this.iframe&&(t=this.getFragment(this.getHash(this.iframe)));if(t==this.fragment)return!1;this.iframe&&this.navigate(t),this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(e){var t=this.fragment=this.getFragment(e),n=a.any(this.handlers,function(e){if(e.route.test(t))return e.callback(t),!0});return n},navigate:function(e,t){if(!b.started)return!1;if(!t||t===!0)t={trigger:t};var n=(e||"").replace(w,"");if(this.fragment==n)return;this._hasPushState?(n.indexOf(this.options.root)!=0&&(n=this.options.root+n),this.fragment=n,window.history[t.replace?"replaceState":"pushState"]({},document.title,n)):this._wantsHashChange?(this.fragment=n,this._updateHash(window.location,n,t.replace),this.iframe&&n!=this.getFragment(this.getHash(this.iframe))&&(t.replace||this.iframe.document.open().close(),this._updateHash(this.iframe.location,n,t.replace))):window.location.assign(this.options.root+e),t.trigger&&this.loadUrl(e)},_updateHash:function(e,t,n){n?e.replace(e.toString().replace(/(javascript:|#).*$/,"")+"#"+t):e.hash=t}});var S=u.View=function(e){this.cid=a.uniqueId("view"),this._configure(e||{}),this._ensureElement(),this.initialize.apply(this,arguments),this.delegateEvents()},x=/^(\S+)\s*(.*)$/,T=["model","collection","el","id","attributes","className","tagName"];a.extend(S.prototype,c,{tagName:"div",$:function(e){return this.$el.find(e)},initialize:function(){},render:function(){return this},remove:function(){return this.$el.remove(),this},make:function(e,t,n){var r=document.createElement(e);return t&&f(r).attr(t),n&&f(r).html(n),r},setElement:function(e,t){return this.$el&&this.undelegateEvents(),this.$el=e instanceof f?e:f(e),this.el=this.$el[0],t!==!1&&this.delegateEvents(),this},delegateEvents:function(e){if(!e&&!(e=A(this,"events")))return;this.undelegateEvents();for(var t in e){var n=e[t];a.isFunction(n)||(n=this[e[t]]);if(!n)throw new Error('Method "'+e[t]+'" does not exist');var r=t.match(x),i=r[1],s=r[2];n=a.bind(n,this),i+=".delegateEvents"+this.cid,s===""?this.$el.bind(i,n):this.$el.delegate(s,i,n)}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(e){this.options&&(e=a.extend({},this.options,e));for(var t=0,n=T.length;t<n;t++){var r=T[t];e[r]&&(this[r]=e[r])}this.options=e},_ensureElement:function(){if(!this.el){var e=A(this,"attributes")||{};this.id&&(e.id=this.id),this.className&&(e["class"]=this.className),this.setElement(this.make(this.tagName,e),!1)}else this.setElement(this.el,!1)}});var N=function(e,t){var n=L(this,e,t);return n.extend=this.extend,n};h.extend=p.extend=v.extend=S.extend=N;var C={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};u.sync=function(e,t,n){var r=C[e];n||(n={});var i={type:r,dataType:"json"};return n.url||(i.url=A(t,"url")||O()),!n.data&&t&&(e=="create"||e=="update")&&(i.contentType="application/json",i.data=JSON.stringify(t.toJSON())),u.emulateJSON&&(i.contentType="application/x-www-form-urlencoded",i.data=i.data?{model:i.data}:{}),u.emulateHTTP&&(r==="PUT"||r==="DELETE")&&(u.emulateJSON&&(i.data._method=r),i.type="POST",i.beforeSend=function(e){e.setRequestHeader("X-HTTP-Method-Override",r)}),i.type!=="GET"&&!u.emulateJSON&&(i.processData=!1),f.ajax(a.extend(i,n))},u.wrapError=function(e,t,n){return function(r,i){i=r===t?i:r,e?e(t,i,n):t.trigger("error",t,i,n)}};var k=function(){},L=function(e,t,n){var r;return t&&t.hasOwnProperty("constructor")?r=t.constructor:r=function(){e.apply(this,arguments)},a.extend(r,e),k.prototype=e.prototype,r.prototype=new k,t&&a.extend(r.prototype,t),n&&a.extend(r,n),r.prototype.constructor=r,r.__super__=e.prototype,r},A=function(e,t){return!e||!e[t]?null:a.isFunction(e[t])?e[t]():e[t]},O=function(){throw new Error('A "url" property or function must be specified')}}).call(this)});
\ No newline at end of file
diff --git a/static/vendor/backbone.mod.js b/static/vendor/backbone.mod.js
new file mode 120000 (symlink)
index 0000000..7859b35
--- /dev/null
@@ -0,0 +1 @@
+backbone-0.9.2.mod.js
\ No newline at end of file
diff --git a/static/vendor/backbone.mod.min.js b/static/vendor/backbone.mod.min.js
new file mode 120000 (symlink)
index 0000000..d86dd8a
--- /dev/null
@@ -0,0 +1 @@
+backbone-0.9.2.mod.min.js
\ No newline at end of file
diff --git a/static/vendor/colorbrewer.mod.js b/static/vendor/colorbrewer.mod.js
new file mode 100644 (file)
index 0000000..552c73e
--- /dev/null
@@ -0,0 +1,41 @@
+require.define('/node_modules/colorbrewer.js', function(require, module, exports, __dirname, __filename, undefined){
+
+/*
+ * This product includes color specifications and designs developed by Cynthia
+ * Brewer (http://colorbrewer.org/).
+ */
+var colorbrewer = {
+YlGn:{3:["rgb(247,252,185)","rgb(173,221,142)","rgb(49,163,84)"],4:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(35,132,67)"],5:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],6:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],7:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],8:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],9:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,104,55)","rgb(0,69,41)"]},
+YlGnBu:{3:["rgb(237,248,177)","rgb(127,205,187)","rgb(44,127,184)"],4:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(34,94,168)"],5:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],6:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],7:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],8:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],9:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(37,52,148)","rgb(8,29,88)"]},
+GnBu:{3:["rgb(224,243,219)","rgb(168,221,181)","rgb(67,162,202)"],4:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(43,140,190)"],5:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],6:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],7:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],8:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],9:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,104,172)","rgb(8,64,129)"]},
+BuGn:{3:["rgb(229,245,249)","rgb(153,216,201)","rgb(44,162,95)"],4:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(35,139,69)"],5:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],6:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],7:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],8:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],9:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]},
+PuBuGn:{3:["rgb(236,226,240)","rgb(166,189,219)","rgb(28,144,153)"],4:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(2,129,138)"],5:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],6:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],7:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],8:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],9:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,108,89)","rgb(1,70,54)"]},
+PuBu:{3:["rgb(236,231,242)","rgb(166,189,219)","rgb(43,140,190)"],4:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(5,112,176)"],5:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],6:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],7:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],8:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],9:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(4,90,141)","rgb(2,56,88)"]},
+BuPu:{3:["rgb(224,236,244)","rgb(158,188,218)","rgb(136,86,167)"],4:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,65,157)"],5:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],6:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],7:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],8:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],9:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(129,15,124)","rgb(77,0,75)"]},
+RdPu:{3:["rgb(253,224,221)","rgb(250,159,181)","rgb(197,27,138)"],4:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(174,1,126)"],5:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],6:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],7:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],8:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],9:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)","rgb(73,0,106)"]},
+PuRd:{3:["rgb(231,225,239)","rgb(201,148,199)","rgb(221,28,119)"],4:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(206,18,86)"],5:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],6:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],7:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],8:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],9:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(152,0,67)","rgb(103,0,31)"]},
+OrRd:{3:["rgb(254,232,200)","rgb(253,187,132)","rgb(227,74,51)"],4:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(215,48,31)"],5:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],6:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],7:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],8:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],9:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(179,0,0)","rgb(127,0,0)"]},
+YlOrRd:{3:["rgb(255,237,160)","rgb(254,178,76)","rgb(240,59,32)"],4:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(227,26,28)"],5:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],6:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],7:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],8:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],9:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(189,0,38)","rgb(128,0,38)"]},
+YlOrBr:{3:["rgb(255,247,188)","rgb(254,196,79)","rgb(217,95,14)"],4:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(204,76,2)"],5:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],6:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],7:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],8:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],9:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(153,52,4)","rgb(102,37,6)"]},
+Purples:{3:["rgb(239,237,245)","rgb(188,189,220)","rgb(117,107,177)"],4:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(106,81,163)"],5:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],6:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],7:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],8:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],9:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(84,39,143)","rgb(63,0,125)"]},
+Blues:{3:["rgb(222,235,247)","rgb(158,202,225)","rgb(49,130,189)"],4:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(33,113,181)"],5:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],6:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],7:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],8:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],9:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,81,156)","rgb(8,48,107)"]},
+Greens:{3:["rgb(229,245,224)","rgb(161,217,155)","rgb(49,163,84)"],4:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(35,139,69)"],5:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],6:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],7:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],8:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],9:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]},
+Oranges:{3:["rgb(254,230,206)","rgb(253,174,107)","rgb(230,85,13)"],4:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(217,71,1)"],5:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],6:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],7:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],8:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],9:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(166,54,3)","rgb(127,39,4)"]},
+Reds:{3:["rgb(254,224,210)","rgb(252,146,114)","rgb(222,45,38)"],4:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(203,24,29)"],5:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],6:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],7:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],8:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],9:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(165,15,21)","rgb(103,0,13)"]},
+Greys:{3:["rgb(240,240,240)","rgb(189,189,189)","rgb(99,99,99)"],4:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(82,82,82)"],5:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],6:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],7:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],8:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],9:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)","rgb(0,0,0)"]},
+PuOr:{3:["rgb(241,163,64)","rgb(247,247,247)","rgb(153,142,195)"],4:["rgb(230,97,1)","rgb(253,184,99)","rgb(178,171,210)","rgb(94,60,153)"],5:["rgb(230,97,1)","rgb(253,184,99)","rgb(247,247,247)","rgb(178,171,210)","rgb(94,60,153)"],6:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],7:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],8:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],9:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],10:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"],11:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"]},
+BrBG:{3:["rgb(216,179,101)","rgb(245,245,245)","rgb(90,180,172)"],4:["rgb(166,97,26)","rgb(223,194,125)","rgb(128,205,193)","rgb(1,133,113)"],5:["rgb(166,97,26)","rgb(223,194,125)","rgb(245,245,245)","rgb(128,205,193)","rgb(1,133,113)"],6:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],7:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],8:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],9:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],10:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"],11:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"]},
+PRGn:{3:["rgb(175,141,195)","rgb(247,247,247)","rgb(127,191,123)"],4:["rgb(123,50,148)","rgb(194,165,207)","rgb(166,219,160)","rgb(0,136,55)"],5:["rgb(123,50,148)","rgb(194,165,207)","rgb(247,247,247)","rgb(166,219,160)","rgb(0,136,55)"],6:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],7:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],8:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],9:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],10:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"],11:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"]},
+PiYG:{3:["rgb(233,163,201)","rgb(247,247,247)","rgb(161,215,106)"],4:["rgb(208,28,139)","rgb(241,182,218)","rgb(184,225,134)","rgb(77,172,38)"],5:["rgb(208,28,139)","rgb(241,182,218)","rgb(247,247,247)","rgb(184,225,134)","rgb(77,172,38)"],6:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],7:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],8:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],9:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],10:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"],11:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"]},
+RdBu:{3:["rgb(239,138,98)","rgb(247,247,247)","rgb(103,169,207)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(146,197,222)","rgb(5,113,176)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(247,247,247)","rgb(146,197,222)","rgb(5,113,176)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"]},
+RdGy:{3:["rgb(239,138,98)","rgb(255,255,255)","rgb(153,153,153)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(186,186,186)","rgb(64,64,64)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(255,255,255)","rgb(186,186,186)","rgb(64,64,64)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"]},
+RdYlBu:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,191,219)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,217,233)","rgb(44,123,182)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,217,233)","rgb(44,123,182)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"]},
+Spectral:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(153,213,148)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,221,164)","rgb(43,131,186)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,221,164)","rgb(43,131,186)"],6:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],7:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],8:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],9:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],10:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"],11:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"]},
+RdYlGn:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,207,96)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(166,217,106)","rgb(26,150,65)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(166,217,106)","rgb(26,150,65)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"]}};
+
+if ((typeof module != 'undefined' && module) || (typeof exports != 'undefined' && exports)) {
+  exports = module.exports = colorbrewer;
+}
+
+
+});
diff --git a/static/vendor/moment-1.4.0.mod.js b/static/vendor/moment-1.4.0.mod.js
new file mode 100644 (file)
index 0000000..91954a2
--- /dev/null
@@ -0,0 +1,735 @@
+require.define('/node_modules/moment-1.4.0.js', function(require, module, exports, __dirname, __filename, undefined){
+
+// moment.js
+// version : 1.4.0
+// author : Tim Wood
+// license : MIT
+// momentjs.com
+
+(function (Date, undefined) {
+
+    var moment,
+        round = Math.round,
+        languages = {},
+        hasModule = (typeof module !== 'undefined'),
+        paramsToParse = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
+        i,
+        jsonRegex = /^\/?Date\((\-?\d+)/i,
+        charactersToReplace = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?|ZZ?|LT|LL?L?L?)/g,
+        nonuppercaseLetters = /[^A-Z]/g,
+        timezoneRegex = /\([A-Za-z ]+\)|:[0-9]{2} [A-Z]{3} /g,
+        tokenCharacters = /(\\)?(MM?M?M?|dd?d?d|DD?D?D?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|ZZ?|T)/g,
+        inputCharacters = /(\\)?([0-9]+|([a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|([\+\-]\d\d:?\d\d))/gi,
+        isoRegex = /\d{4}.\d\d.\d\d(T(\d\d(.\d\d(.\d\d)?)?)?([\+\-]\d\d:?\d\d)?)?/,
+        isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
+        isoTimes = [
+            ['HH:mm:ss', /T\d\d:\d\d:\d\d/],
+            ['HH:mm', /T\d\d:\d\d/],
+            ['HH', /T\d\d/]
+        ],
+        timezoneParseRegex = /([\+\-]|\d\d)/gi,
+        VERSION = "1.4.0",
+        shortcuts = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|');
+
+    // Moment prototype object
+    function Moment(date, isUTC) {
+        this._d = date;
+        this._isUTC = !!isUTC;
+    }
+
+    // left zero fill a number
+    // see http://jsperf.com/left-zero-filling for performance comparison
+    function leftZeroFill(number, targetLength) {
+        var output = number + '';
+        while (output.length < targetLength) {
+            output = '0' + output;
+        }
+        return output;
+    }
+
+    // helper function for _.addTime and _.subtractTime
+    function dateAddRemove(date, _input, adding, val) {
+        var isString = (typeof _input === 'string'),
+            input = isString ? {} : _input,
+            ms, d, M, currentDate;
+        if (isString && val) {
+            input[_input] = +val;
+        }
+        ms = (input.ms || input.milliseconds || 0) +
+            (input.s || input.seconds || 0) * 1e3 + // 1000
+            (input.m || input.minutes || 0) * 6e4 + // 1000 * 60
+            (input.h || input.hours || 0) * 36e5; // 1000 * 60 * 60
+        d = (input.d || input.days || 0) +
+            (input.w || input.weeks || 0) * 7;
+        M = (input.M || input.months || 0) +
+            (input.y || input.years || 0) * 12;
+        if (ms) {
+            date.setTime(+date + ms * adding);
+        }
+        if (d) {
+            date.setDate(date.getDate() + d * adding);
+        }
+        if (M) {
+            currentDate = date.getDate();
+            date.setDate(1);
+            date.setMonth(date.getMonth() + M * adding);
+            date.setDate(Math.min(new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(), currentDate));
+        }
+        return date;
+    }
+
+    // check if is an array
+    function isArray(input) {
+        return Object.prototype.toString.call(input) === '[object Array]';
+    }
+
+    // convert an array to a date.
+    // the array should mirror the parameters below
+    // note: all values past the year are optional and will default to the lowest possible value.
+    // [year, month, day , hour, minute, second, millisecond]
+    function dateFromArray(input) {
+        return new Date(input[0], input[1] || 0, input[2] || 1, input[3] || 0, input[4] || 0, input[5] || 0, input[6] || 0);
+    }
+
+    // format date using native date object
+    function formatMoment(m, inputString) {
+        var currentMonth = m.month(),
+            currentDate = m.date(),
+            currentYear = m.year(),
+            currentDay = m.day(),
+            currentHours = m.hours(),
+            currentMinutes = m.minutes(),
+            currentSeconds = m.seconds(),
+            currentZone = -m.zone(),
+            ordinal = moment.ordinal,
+            meridiem = moment.meridiem;
+        // check if the character is a format
+        // return formatted string or non string.
+        //
+        // uses switch/case instead of an object of named functions (like http://phpjs.org/functions/date:380)
+        // for minification and performance
+        // see http://jsperf.com/object-of-functions-vs-switch for performance comparison
+        function replaceFunction(input) {
+            // create a couple variables to be used later inside one of the cases.
+            var a, b;
+            switch (input) {
+                // MONTH
+            case 'M' :
+                return currentMonth + 1;
+            case 'Mo' :
+                return (currentMonth + 1) + ordinal(currentMonth + 1);
+            case 'MM' :
+                return leftZeroFill(currentMonth + 1, 2);
+            case 'MMM' :
+                return moment.monthsShort[currentMonth];
+            case 'MMMM' :
+                return moment.months[currentMonth];
+            // DAY OF MONTH
+            case 'D' :
+                return currentDate;
+            case 'Do' :
+                return currentDate + ordinal(currentDate);
+            case 'DD' :
+                return leftZeroFill(currentDate, 2);
+            // DAY OF YEAR
+            case 'DDD' :
+                a = new Date(currentYear, currentMonth, currentDate);
+                b = new Date(currentYear, 0, 1);
+                return ~~ (((a - b) / 864e5) + 1.5);
+            case 'DDDo' :
+                a = replaceFunction('DDD');
+                return a + ordinal(a);
+            case 'DDDD' :
+                return leftZeroFill(replaceFunction('DDD'), 3);
+            // WEEKDAY
+            case 'd' :
+                return currentDay;
+            case 'do' :
+                return currentDay + ordinal(currentDay);
+            case 'ddd' :
+                return moment.weekdaysShort[currentDay];
+            case 'dddd' :
+                return moment.weekdays[currentDay];
+            // WEEK OF YEAR
+            case 'w' :
+                a = new Date(currentYear, currentMonth, currentDate - currentDay + 5);
+                b = new Date(a.getFullYear(), 0, 4);
+                return ~~ ((a - b) / 864e5 / 7 + 1.5);
+            case 'wo' :
+                a = replaceFunction('w');
+                return a + ordinal(a);
+            case 'ww' :
+                return leftZeroFill(replaceFunction('w'), 2);
+            // YEAR
+            case 'YY' :
+                return leftZeroFill(currentYear % 100, 2);
+            case 'YYYY' :
+                return currentYear;
+            // AM / PM
+            case 'a' :
+                return currentHours > 11 ? meridiem.pm : meridiem.am;
+            case 'A' :
+                return currentHours > 11 ? meridiem.PM : meridiem.AM;
+            // 24 HOUR
+            case 'H' :
+                return currentHours;
+            case 'HH' :
+                return leftZeroFill(currentHours, 2);
+            // 12 HOUR
+            case 'h' :
+                return currentHours % 12 || 12;
+            case 'hh' :
+                return leftZeroFill(currentHours % 12 || 12, 2);
+            // MINUTE
+            case 'm' :
+                return currentMinutes;
+            case 'mm' :
+                return leftZeroFill(currentMinutes, 2);
+            // SECOND
+            case 's' :
+                return currentSeconds;
+            case 'ss' :
+                return leftZeroFill(currentSeconds, 2);
+            // TIMEZONE
+            case 'zz' :
+                // depreciating 'zz' fall through to 'z'
+            case 'z' :
+                return (m._d.toString().match(timezoneRegex) || [''])[0].replace(nonuppercaseLetters, '');
+            case 'Z' :
+                return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(Math.abs(currentZone) / 60), 2) + ':' + leftZeroFill(~~(Math.abs(currentZone) % 60), 2);
+            case 'ZZ' :
+                return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(10 * Math.abs(currentZone) / 6), 4);
+            // LONG DATES
+            case 'L' :
+            case 'LL' :
+            case 'LLL' :
+            case 'LLLL' :
+            case 'LT' :
+                return formatMoment(m, moment.longDateFormat[input]);
+            // DEFAULT
+            default :
+                return input.replace(/(^\[)|(\\)|\]$/g, "");
+            }
+        }
+        return inputString.replace(charactersToReplace, replaceFunction);
+    }
+
+    // date from string and format string
+    function makeDateFromStringAndFormat(string, format) {
+        var inArray = [0, 0, 1, 0, 0, 0, 0],
+            timezoneHours = 0,
+            timezoneMinutes = 0,
+            isUsingUTC = false,
+            inputParts = string.match(inputCharacters),
+            formatParts = format.match(tokenCharacters),
+            len = Math.min(inputParts.length, formatParts.length),
+            i,
+            isPm;
+
+        // function to convert string input to date
+        function addTime(format, input) {
+            var a;
+            switch (format) {
+            // MONTH
+            case 'M' :
+                // fall through to MM
+            case 'MM' :
+                inArray[1] = ~~input - 1;
+                break;
+            case 'MMM' :
+                // fall through to MMMM
+            case 'MMMM' :
+                for (a = 0; a < 12; a++) {
+                    if (moment.monthsParse[a].test(input)) {
+                        inArray[1] = a;
+                        break;
+                    }
+                }
+                break;
+            // DAY OF MONTH
+            case 'D' :
+                // fall through to DDDD
+            case 'DD' :
+                // fall through to DDDD
+            case 'DDD' :
+                // fall through to DDDD
+            case 'DDDD' :
+                inArray[2] = ~~input;
+                break;
+            // YEAR
+            case 'YY' :
+                input = ~~input;
+                inArray[0] = input + (input > 70 ? 1900 : 2000);
+                break;
+            case 'YYYY' :
+                inArray[0] = ~~Math.abs(input);
+                break;
+            // AM / PM
+            case 'a' :
+                // fall through to A
+            case 'A' :
+                isPm = (input.toLowerCase() === 'pm');
+                break;
+            // 24 HOUR
+            case 'H' :
+                // fall through to hh
+            case 'HH' :
+                // fall through to hh
+            case 'h' :
+                // fall through to hh
+            case 'hh' :
+                inArray[3] = ~~input;
+                break;
+            // MINUTE
+            case 'm' :
+                // fall through to mm
+            case 'mm' :
+                inArray[4] = ~~input;
+                break;
+            // SECOND
+            case 's' :
+                // fall through to ss
+            case 'ss' :
+                inArray[5] = ~~input;
+                break;
+            // TIMEZONE
+            case 'Z' :
+                // fall through to ZZ
+            case 'ZZ' :
+                isUsingUTC = true;
+                a = (input || '').match(timezoneParseRegex);
+                if (a && a[1]) {
+                    timezoneHours = ~~a[1];
+                }
+                if (a && a[2]) {
+                    timezoneMinutes = ~~a[2];
+                }
+                // reverse offsets
+                if (a && a[0] === '+') {
+                    timezoneHours = -timezoneHours;
+                    timezoneMinutes = -timezoneMinutes;
+                }
+                break;
+            }
+        }
+        for (i = 0; i < len; i++) {
+            addTime(formatParts[i], inputParts[i]);
+        }
+        // handle am pm
+        if (isPm && inArray[3] < 12) {
+            inArray[3] += 12;
+        }
+        // if is 12 am, change hours to 0
+        if (isPm === false && inArray[3] === 12) {
+            inArray[3] = 0;
+        }
+        // handle timezone
+        inArray[3] += timezoneHours;
+        inArray[4] += timezoneMinutes;
+        // return
+        return isUsingUTC ? new Date(Date.UTC.apply({}, inArray)) : dateFromArray(inArray);
+    }
+
+    // compare two arrays, return the number of differences
+    function compareArrays(array1, array2) {
+        var len = Math.min(array1.length, array2.length),
+            lengthDiff = Math.abs(array1.length - array2.length),
+            diffs = 0,
+            i;
+        for (i = 0; i < len; i++) {
+            if (~~array1[i] !== ~~array2[i]) {
+                diffs++;
+            }
+        }
+        return diffs + lengthDiff;
+    }
+
+    // date from string and array of format strings
+    function makeDateFromStringAndArray(string, formats) {
+        var output,
+            inputParts = string.match(inputCharacters),
+            scores = [],
+            scoreToBeat = 99,
+            i,
+            curDate,
+            curScore;
+        for (i = 0; i < formats.length; i++) {
+            curDate = makeDateFromStringAndFormat(string, formats[i]);
+            curScore = compareArrays(inputParts, formatMoment(new Moment(curDate), formats[i]).match(inputCharacters));
+            if (curScore < scoreToBeat) {
+                scoreToBeat = curScore;
+                output = curDate;
+            }
+        }
+        return output;
+    }
+
+    // date from iso format
+    function makeDateFromString(string) {
+        var format = 'YYYY-MM-DDT',
+            i;
+        if (isoRegex.exec(string)) {
+            for (i = 0; i < 3; i++) {
+                if (isoTimes[i][1].exec(string)) {
+                    format += isoTimes[i][0];
+                    break;
+                }
+            }
+            return makeDateFromStringAndFormat(string, format + 'Z');
+        }
+        return new Date(string);
+    }
+
+    // helper function for _date.from() and _date.fromNow()
+    function substituteTimeAgo(string, number, withoutSuffix) {
+        var rt = moment.relativeTime[string];
+        return (typeof rt === 'function') ?
+            rt(number || 1, !!withoutSuffix, string) :
+            rt.replace(/%d/i, number || 1);
+    }
+
+    function relativeTime(milliseconds, withoutSuffix) {
+        var seconds = round(Math.abs(milliseconds) / 1000),
+            minutes = round(seconds / 60),
+            hours = round(minutes / 60),
+            days = round(hours / 24),
+            years = round(days / 365),
+            args = seconds < 45 && ['s', seconds] ||
+                minutes === 1 && ['m'] ||
+                minutes < 45 && ['mm', minutes] ||
+                hours === 1 && ['h'] ||
+                hours < 22 && ['hh', hours] ||
+                days === 1 && ['d'] ||
+                days <= 25 && ['dd', days] ||
+                days <= 45 && ['M'] ||
+                days < 345 && ['MM', round(days / 30)] ||
+                years === 1 && ['y'] || ['yy', years];
+        args[2] = withoutSuffix;
+        return substituteTimeAgo.apply({}, args);
+    }
+
+    moment = function (input, format) {
+        if (input === null || input === '') {
+            return null;
+        }
+        var date,
+            matched;
+        // parse Moment object
+        if (input && input._d instanceof Date) {
+            date = new Date(+input._d);
+        // parse string and format
+        } else if (format) {
+            if (isArray(format)) {
+                date = makeDateFromStringAndArray(input, format);
+            } else {
+                date = makeDateFromStringAndFormat(input, format);
+            }
+        // evaluate it as a JSON-encoded date
+        } else {
+            matched = jsonRegex.exec(input);
+            date = input === undefined ? new Date() :
+                matched ? new Date(+matched[1]) :
+                input instanceof Date ? input :
+                isArray(input) ? dateFromArray(input) :
+                typeof input === 'string' ? makeDateFromString(input) :
+                new Date(input);
+        }
+        return new Moment(date);
+    };
+
+    // creating with utc
+    moment.utc = function (input, format) {
+        if (isArray(input)) {
+            return new Moment(new Date(Date.UTC.apply({}, input)), true);
+        }
+        return (format && input) ? moment(input + ' 0', format + ' Z').utc() : moment(input).utc();
+    };
+
+    // humanizeDuration
+    moment.humanizeDuration = function (num, type, withSuffix) {
+        var difference = +num,
+            rel = moment.relativeTime,
+            output;
+        switch (type) {
+        case "seconds" :
+            difference *= 1000; // 1000
+            break;
+        case "minutes" :
+            difference *= 60000; // 60 * 1000
+            break;
+        case "hours" :
+            difference *= 3600000; // 60 * 60 * 1000
+            break;
+        case "days" :
+            difference *= 86400000; // 24 * 60 * 60 * 1000
+            break;
+        case "weeks" :
+            difference *= 604800000; // 7 * 24 * 60 * 60 * 1000
+            break;
+        case "months" :
+            difference *= 2592000000; // 30 * 24 * 60 * 60 * 1000
+            break;
+        case "years" :
+            difference *= 31536000000; // 365 * 24 * 60 * 60 * 1000
+            break;
+        default :
+            withSuffix = !!type;
+            break;
+        }
+        output = relativeTime(difference, !withSuffix);
+        return withSuffix ? (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output) : output;
+    };
+
+    // version number
+    moment.version = VERSION;
+
+    // default format
+    moment.defaultFormat = isoFormat;
+
+    // language switching and caching
+    moment.lang = function (key, values) {
+        var i,
+            param,
+            req,
+            parse = [];
+        if (values) {
+            for (i = 0; i < 12; i++) {
+                parse[i] = new RegExp('^' + values.months[i] + '|^' + values.monthsShort[i].replace('.', ''), 'i');
+            }
+            values.monthsParse = values.monthsParse || parse;
+            languages[key] = values;
+        }
+        if (languages[key]) {
+            for (i = 0; i < paramsToParse.length; i++) {
+                param = paramsToParse[i];
+                moment[param] = languages[key][param] || moment[param];
+            }
+        } else {
+            if (hasModule) {
+                req = require('./lang/' + key);
+                moment.lang(key, req);
+            }
+        }
+    };
+
+    // set default language
+    moment.lang('en', {
+        months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+        monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+        weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+        weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+        longDateFormat : {
+            LT : "h:mm A",
+            L : "MM/DD/YYYY",
+            LL : "MMMM D YYYY",
+            LLL : "MMMM D YYYY LT",
+            LLLL : "dddd, MMMM D YYYY LT"
+        },
+        meridiem : {
+            AM : 'AM',
+            am : 'am',
+            PM : 'PM',
+            pm : 'pm'
+        },
+        calendar : {
+            sameDay : '[Today at] LT',
+            nextDay : '[Tomorrow at] LT',
+            nextWeek : 'dddd [at] LT',
+            lastDay : '[Yesterday at] LT',
+            lastWeek : '[last] dddd [at] LT',
+            sameElse : 'L'
+        },
+        relativeTime : {
+            future : "in %s",
+            past : "%s ago",
+            s : "a few seconds",
+            m : "a minute",
+            mm : "%d minutes",
+            h : "an hour",
+            hh : "%d hours",
+            d : "a day",
+            dd : "%d days",
+            M : "a month",
+            MM : "%d months",
+            y : "a year",
+            yy : "%d years"
+        },
+        ordinal : function (number) {
+            var b = number % 10;
+            return (~~ (number % 100 / 10) === 1) ? 'th' :
+                (b === 1) ? 'st' :
+                (b === 2) ? 'nd' :
+                (b === 3) ? 'rd' : 'th';
+        }
+    });
+
+    // shortcut for prototype
+    moment.fn = Moment.prototype = {
+
+        clone : function () {
+            return moment(this);
+        },
+
+        valueOf : function () {
+            return +this._d;
+        },
+
+        'native' : function () {
+            return this._d;
+        },
+
+        toString : function () {
+            return this._d.toString();
+        },
+
+        toDate : function () {
+            return this._d;
+        },
+
+        utc : function () {
+            this._isUTC = true;
+            return this;
+        },
+
+        local : function () {
+            this._isUTC = false;
+            return this;
+        },
+
+        format : function (inputString) {
+            return formatMoment(this, inputString ? inputString : moment.defaultFormat);
+        },
+
+        add : function (input, val) {
+            this._d = dateAddRemove(this._d, input, 1, val);
+            return this;
+        },
+
+        subtract : function (input, val) {
+            this._d = dateAddRemove(this._d, input, -1, val);
+            return this;
+        },
+
+        diff : function (input, val, asFloat) {
+            var inputMoment = moment(input),
+                zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
+                diff = this._d - inputMoment._d - zoneDiff,
+                year = this.year() - inputMoment.year(),
+                month = this.month() - inputMoment.month(),
+                date = this.date() - inputMoment.date(),
+                output;
+            if (val === 'months') {
+                output = year * 12 + month + date / 30;
+            } else if (val === 'years') {
+                output = year + month / 12;
+            } else {
+                output = val === 'seconds' ? diff / 1e3 : // 1000
+                    val === 'minutes' ? diff / 6e4 : // 1000 * 60
+                    val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
+                    val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
+                    val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
+                    diff;
+            }
+            return asFloat ? output : round(output);
+        },
+
+        from : function (time, withoutSuffix) {
+            return moment.humanizeDuration(this.diff(time), !withoutSuffix);
+        },
+
+        fromNow : function (withoutSuffix) {
+            return this.from(moment(), withoutSuffix);
+        },
+
+        calendar : function () {
+            var diff = this.diff(moment().sod(), 'days', true),
+                calendar = moment.calendar,
+                allElse = calendar.sameElse,
+                format = diff < -6 ? allElse :
+                diff < -1 ? calendar.lastWeek :
+                diff < 0 ? calendar.lastDay :
+                diff < 1 ? calendar.sameDay :
+                diff < 2 ? calendar.nextDay :
+                diff < 7 ? calendar.nextWeek : allElse;
+            return this.format(typeof format === 'function' ? format.apply(this) : format);
+        },
+
+        isLeapYear : function () {
+            var year = this.year();
+            return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+        },
+
+        isDST : function () {
+            return (this.zone() < moment([this.year()]).zone() || 
+                this.zone() < moment([this.year(), 5]).zone());
+        },
+
+        day : function (input) {
+            var day = this._d.getDay();
+            return input == null ? day :
+                this.add({ d : input - day });
+        },
+
+        sod: function () {
+            return this.clone()
+                .hours(0)
+                .minutes(0)
+                .seconds(0)
+                .milliseconds(0);
+        },
+
+        eod: function () {
+            // end of day = start of day plus 1 day, minus 1 millisecond
+            return this.sod().add({
+                d : 1,
+                ms : -1
+            });
+        },
+
+        zone : function () {
+            return this._isUTC ? 0 : this._d.getTimezoneOffset();
+        },
+
+        daysInMonth : function () {
+            return this.clone().month(this.month() + 1).date(0).date();
+        }
+    };
+
+    // helper for adding shortcuts
+    function makeShortcut(name, key) {
+        moment.fn[name] = function (input) {
+            var utc = this._isUTC ? 'UTC' : '';
+            if (input != null) {
+                this._d['set' + utc + key](input);
+                return this;
+            } else {
+                return this._d['get' + utc + key]();
+            }
+        };
+    }
+
+    // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
+    for (i = 0; i < shortcuts.length; i ++) {
+        makeShortcut(shortcuts[i].toLowerCase(), shortcuts[i]);
+    }
+
+    // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
+    makeShortcut('year', 'FullYear');
+
+    // CommonJS module is defined
+    if (hasModule) {
+        module.exports = moment;
+    }
+    if (typeof window !== 'undefined') {
+        window.moment = moment;
+    }
+    /*global define:false */
+    if (typeof define === "function" && define.amd) {
+        define("moment", [], function () {
+            return moment;
+        });
+    }
+})(Date);
+
+
+});
diff --git a/static/vendor/moment-1.4.0.mod.min.js b/static/vendor/moment-1.4.0.mod.min.js
new file mode 100644 (file)
index 0000000..ff3d327
--- /dev/null
@@ -0,0 +1,10 @@
+require.define('/node_modules/moment.js', function(require, module, exports, __dirname, __filename, undefined){
+
+// moment.js
+// version : 1.4.0
+// author : Tim Wood
+// license : MIT
+// momentjs.com
+(function(a,b){function u(a,b){this._d=a,this._isUTC=!!b}function v(a,b){var c=a+"";while(c.length<b)c="0"+c;return c}function w(b,c,d,e){var f=typeof c=="string",g=f?{}:c,h,i,j,k;return f&&e&&(g[c]=+e),h=(g.ms||g.milliseconds||0)+(g.s||g.seconds||0)*1e3+(g.m||g.minutes||0)*6e4+(g.h||g.hours||0)*36e5,i=(g.d||g.days||0)+(g.w||g.weeks||0)*7,j=(g.M||g.months||0)+(g.y||g.years||0)*12,h&&b.setTime(+b+h*d),i&&b.setDate(b.getDate()+i*d),j&&(k=b.getDate(),b.setDate(1),b.setMonth(b.getMonth()+j*d),b.setDate(Math.min((new a(b.getFullYear(),b.getMonth()+1,0)).getDate(),k))),b}function x(a){return Object.prototype.toString.call(a)==="[object Array]"}function y(b){return new a(b[0],b[1]||0,b[2]||1,b[3]||0,b[4]||0,b[5]||0,b[6]||0)}function z(b,d){function r(d){var j,s;switch(d){case"M":return e+1;case"Mo":return e+1+p(e+1);case"MM":return v(e+1,2);case"MMM":return c.monthsShort[e];case"MMMM":return c.months[e];case"D":return f;case"Do":return f+p(f);case"DD":return v(f,2);case"DDD":return j=new a(g,e,f),s=new a(g,0,1),~~((j-s)/864e5+1.5);case"DDDo":return j=r("DDD"),j+p(j);case"DDDD":return v(r("DDD"),3);case"d":return h;case"do":return h+p(h);case"ddd":return c.weekdaysShort[h];case"dddd":return c.weekdays[h];case"w":return j=new a(g,e,f-h+5),s=new a(j.getFullYear(),0,4),~~((j-s)/864e5/7+1.5);case"wo":return j=r("w"),j+p(j);case"ww":return v(r("w"),2);case"YY":return v(g%100,2);case"YYYY":return g;case"a":return i>11?q.pm:q.am;case"A":return i>11?q.PM:q.AM;case"H":return i;case"HH":return v(i,2);case"h":return i%12||12;case"hh":return v(i%12||12,2);case"m":return m;case"mm":return v(m,2);case"s":return n;case"ss":return v(n,2);case"zz":case"z":return(b._d.toString().match(l)||[""])[0].replace(k,"");case"Z":return(o<0?"-":"+")+v(~~(Math.abs(o)/60),2)+":"+v(~~(Math.abs(o)%60),2);case"ZZ":return(o<0?"-":"+")+v(~~(10*Math.abs(o)/6),4);case"L":case"LL":case"LLL":case"LLLL":case"LT":return z(b,c.longDateFormat[d]);default:return d.replace(/(^\[)|(\\)|\]$/g,"")}}var e=b.month(),f=b.date(),g=b.year(),h=b.day(),i=b.hours(),m=b.minutes(),n=b.seconds(),o=-b.zone(),p=c.ordinal,q=c.meridiem;return d.replace(j,r)}function A(b,d){function p(a,b){var d;switch(a){case"M":case"MM":e[1]=~~b-1;break;case"MMM":case"MMMM":for(d=0;d<12;d++)if(c.monthsParse[d].test(b)){e[1]=d;break}break;case"D":case"DD":case"DDD":case"DDDD":e[2]=~~b;break;case"YY":b=~~b,e[0]=b+(b>70?1900:2e3);break;case"YYYY":e[0]=~~Math.abs(b);break;case"a":case"A":o=b.toLowerCase()==="pm";break;case"H":case"HH":case"h":case"hh":e[3]=~~b;break;case"m":case"mm":e[4]=~~b;break;case"s":case"ss":e[5]=~~b;break;case"Z":case"ZZ":h=!0,d=(b||"").match(r),d&&d[1]&&(f=~~d[1]),d&&d[2]&&(g=~~d[2]),d&&d[0]==="+"&&(f=-f,g=-g)}}var e=[0,0,1,0,0,0,0],f=0,g=0,h=!1,i=b.match(n),j=d.match(m),k=Math.min(i.length,j.length),l,o;for(l=0;l<k;l++)p(j[l],i[l]);return o&&e[3]<12&&(e[3]+=12),o===!1&&e[3]===12&&(e[3]=0),e[3]+=f,e[4]+=g,h?new a(a.UTC.apply({},e)):y(e)}function B(a,b){var c=Math.min(a.length,b.length),d=Math.abs(a.length-b.length),e=0,f;for(f=0;f<c;f++)~~a[f]!==~~b[f]&&e++;return e+d}function C(a,b){var c,d=a.match(n),e=[],f=99,g,h,i;for(g=0;g<b.length;g++)h=A(a,b[g]),i=B(d,z(new u(h),b[g]).match(n)),i<f&&(f=i,c=h);return c}function D(b){var c="YYYY-MM-DDT",d;if(o.exec(b)){for(d=0;d<3;d++)if(q[d][1].exec(b)){c+=q[d][0];break}return A(b,c+"Z")}return new a(b)}function E(a,b,d){var e=c.relativeTime[a];return typeof e=="function"?e(b||1,!!d,a):e.replace(/%d/i,b||1)}function F(a,b){var c=d(Math.abs(a)/1e3),e=d(c/60),f=d(e/60),g=d(f/24),h=d(g/365),i=c<45&&["s",c]||e===1&&["m"]||e<45&&["mm",e]||f===1&&["h"]||f<22&&["hh",f]||g===1&&["d"]||g<=25&&["dd",g]||g<=45&&["M"]||g<345&&["MM",d(g/30)]||h===1&&["y"]||["yy",h];return i[2]=b,E.apply({},i)}function G(a,b){c.fn[a]=function(a){var c=this._isUTC?"UTC":"";return a!=null?(this._d["set"+c+b](a),this):this._d["get"+c+b]()}}var c,d=Math.round,e={},f=typeof module!="undefined",g="months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem".split("|"),h,i=/^\/?Date\((\-?\d+)/i,j=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?|ZZ?|LT|LL?L?L?)/g,k=/[^A-Z]/g,l=/\([A-Za-z ]+\)|:[0-9]{2} [A-Z]{3} /g,m=/(\\)?(MM?M?M?|dd?d?d|DD?D?D?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|ZZ?|T)/g,n=/(\\)?([0-9]+|([a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|([\+\-]\d\d:?\d\d))/gi,o=/\d{4}.\d\d.\d\d(T(\d\d(.\d\d(.\d\d)?)?)?([\+\-]\d\d:?\d\d)?)?/,p="YYYY-MM-DDTHH:mm:ssZ",q=[["HH:mm:ss",/T\d\d:\d\d:\d\d/],["HH:mm",/T\d\d:\d\d/],["HH",/T\d\d/]],r=/([\+\-]|\d\d)/gi,s="1.4.0",t="Month|Date|Hours|Minutes|Seconds|Milliseconds".split("|");c=function(c,d){if(c===null||c==="")return null;var e,f;return c&&c._d instanceof a?e=new a(+c._d):d?x(d)?e=C(c,d):e=A(c,d):(f=i.exec(c),e=c===b?new a:f?new a(+f[1]):c instanceof a?c:x(c)?y(c):typeof c=="string"?D(c):new a(c)),new u(e)},c.utc=function(b,d){return x(b)?new u(new a(a.UTC.apply({},b)),!0):d&&b?c(b+" 0",d+" Z").utc():c(b).utc()},c.humanizeDuration=function(a,b,d){var e=+a,f=c.relativeTime,g;switch(b){case"seconds":e*=1e3;break;case"minutes":e*=6e4;break;case"hours":e*=36e5;break;case"days":e*=864e5;break;case"weeks":e*=6048e5;break;case"months":e*=2592e6;break;case"years":e*=31536e6;break;default:d=!!b}return g=F(e,!d),d?(e<=0?f.past:f.future).replace(/%s/i,g):g},c.version=s,c.defaultFormat=p,c.lang=function(a,b){var d,h,i,j=[];if(b){for(d=0;d<12;d++)j[d]=new RegExp("^"+b.months[d]+"|^"+b.monthsShort[d].replace(".",""),"i");b.monthsParse=b.monthsParse||j,e[a]=b}if(e[a])for(d=0;d<g.length;d++)h=g[d],c[h]=e[a][h]||c[h];else f&&(i=require("./lang/"+a),c.lang(a,i))},c.lang("en",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},meridiem:{AM:"AM",am:"am",PM:"PM",pm:"pm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(a){var b=a%10;return~~(a%100/10)===1?"th":b===1?"st":b===2?"nd":b===3?"rd":"th"}}),c.fn=u.prototype={clone:function(){return c(this)},valueOf:function(){return+this._d},"native":function(){return this._d},toString:function(){return this._d.toString()},toDate:function(){return this._d},utc:function(){return this._isUTC=!0,this},local:function(){return this._isUTC=!1,this},format:function(a){return z(this,a?a:c.defaultFormat)},add:function(a,b){return this._d=w(this._d,a,1,b),this},subtract:function(a,b){return this._d=w(this._d,a,-1,b),this},diff:function(a,b,e){var f=c(a),g=(this.zone()-f.zone())*6e4,h=this._d-f._d-g,i=this.year()-f.year(),j=this.month()-f.month(),k=this.date()-f.date(),l;return b==="months"?l=i*12+j+k/30:b==="years"?l=i+j/12:l=b==="seconds"?h/1e3:b==="minutes"?h/6e4:b==="hours"?h/36e5:b==="days"?h/864e5:b==="weeks"?h/6048e5:h,e?l:d(l)},from:function(a,b){return c.humanizeDuration(this.diff(a),!b)},fromNow:function(a){return this.from(c(),a)},calendar:function(){var a=this.diff(c().sod(),"days",!0),b=c.calendar,d=b.sameElse,e=a<-6?d:a<-1?b.lastWeek:a<0?b.lastDay:a<1?b.sameDay:a<2?b.nextDay:a<7?b.nextWeek:d;return this.format(typeof e=="function"?e.apply(this):e)},isLeapYear:function(){var a=this.year();return a%4===0&&a%100!==0||a%400===0},isDST:function(){return this.zone()<c([this.year()]).zone()||this.zone()<c([this.year(),5]).zone()},day:function(a){var b=this._d.getDay();return a==null?b:this.add({d:a-b})},sod:function(){return this.clone().hours(0).minutes(0).seconds(0).milliseconds(0)},eod:function(){return this.sod().add({d:1,ms:-1})},zone:function(){return this._isUTC?0:this._d.getTimezoneOffset()},daysInMonth:function(){return this.clone().month(this.month()+1).date(0).date()}};for(h=0;h<t.length;h++)G(t[h].toLowerCase(),t[h]);G("year","FullYear"),f&&(module.exports=c),typeof window!="undefined"&&(window.moment=c),typeof define=="function"&&define.amd&&define("moment",[],function(){return c})})(Date);
+
+});
diff --git a/static/vendor/moment.mod.js b/static/vendor/moment.mod.js
new file mode 120000 (symlink)
index 0000000..ac8f30b
--- /dev/null
@@ -0,0 +1 @@
+moment-1.4.0.mod.js
\ No newline at end of file
diff --git a/static/vendor/moment.mod.min.js b/static/vendor/moment.mod.min.js
new file mode 120000 (symlink)
index 0000000..871a418
--- /dev/null
@@ -0,0 +1 @@
+moment-1.4.0.mod.min.js
\ No newline at end of file
diff --git a/static/vendor/showdown.mod.js b/static/vendor/showdown.mod.js
new file mode 100644 (file)
index 0000000..4c477f7
--- /dev/null
@@ -0,0 +1,1306 @@
+require.define('/node_modules/showdown.js', function(require, module, exports, __dirname, __filename, undefined){
+
+//\r
+// showdown.js -- A javascript port of Markdown.\r
+//\r
+// Copyright (c) 2007 John Fraser.\r
+//\r
+// Original Markdown Copyright (c) 2004-2005 John Gruber\r
+//   <http://daringfireball.net/projects/markdown/>\r
+//\r
+// Redistributable under a BSD-style open source license.\r
+// See license.txt for more information.\r
+//\r
+// The full source distribution is at:\r
+//\r
+//                             A A L\r
+//                             T C A\r
+//                             T K B\r
+//\r
+//   <http://www.attacklab.net/>\r
+//\r
+\r
+//\r
+// Wherever possible, Showdown is a straight, line-by-line port\r
+// of the Perl version of Markdown.\r
+//\r
+// This is not a normal parser design; it's basically just a\r
+// series of string substitutions.  It's hard to read and\r
+// maintain this way,  but keeping Showdown close to the original\r
+// design makes it easier to port new features.\r
+//\r
+// More importantly, Showdown behaves like markdown.pl in most\r
+// edge cases.  So web applications can do client-side preview\r
+// in Javascript, and then build identical HTML on the server.\r
+//\r
+// This port needs the new RegExp functionality of ECMA 262,\r
+// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers\r
+// should do fine.  Even with the new regular expression features,\r
+// We do a lot of work to emulate Perl's regex functionality.\r
+// The tricky changes in this file mostly have the "attacklab:"\r
+// label.  Major or self-explanatory changes don't.\r
+//\r
+// Smart diff tools like Araxis Merge will be able to match up\r
+// this file with markdown.pl in a useful way.  A little tweaking\r
+// helps: in a copy of markdown.pl, replace "#" with "//" and\r
+// replace "$text" with "text".  Be sure to ignore whitespace\r
+// and line endings.\r
+//\r
+\r
+\r
+//\r
+// Showdown usage:\r
+//\r
+//   var text = "Markdown *rocks*.";\r
+//\r
+//   var converter = new Showdown.converter();\r
+//   var html = converter.makeHtml(text);\r
+//\r
+//   alert(html);\r
+//\r
+// Note: move the sample code to the bottom of this\r
+// file before uncommenting it.\r
+//\r
+\r
+\r
+//\r
+// Showdown namespace\r
+//\r
+var Showdown = {};\r
+\r
+//\r
+// converter\r
+//\r
+// Wraps all "globals" so that the only thing\r
+// exposed is makeHtml().\r
+//\r
+Showdown.converter = function() {\r
+\r
+//\r
+// Globals:\r
+//\r
+\r
+// Global hashes, used by various utility routines\r
+var g_urls;\r
+var g_titles;\r
+var g_html_blocks;\r
+\r
+// Used to track when we're inside an ordered or unordered list\r
+// (see _ProcessListItems() for details):\r
+var g_list_level = 0;\r
+\r
+\r
+this.makeHtml = function(text) {\r
+//\r
+// Main function. The order in which other subs are called here is\r
+// essential. Link and image substitutions need to happen before\r
+// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>\r
+// and <img> tags get encoded.\r
+//\r
+\r
+       // Clear the global hashes. If we don't clear these, you get conflicts\r
+       // from other articles when generating a page which contains more than\r
+       // one article (e.g. an index page that shows the N most recent\r
+       // articles):\r
+       g_urls = new Array();\r
+       g_titles = new Array();\r
+       g_html_blocks = new Array();\r
+\r
+       // attacklab: Replace ~ with ~T\r
+       // This lets us use tilde as an escape char to avoid md5 hashes\r
+       // The choice of character is arbitray; anything that isn't\r
+    // magic in Markdown will work.\r
+       text = text.replace(/~/g,"~T");\r
+\r
+       // attacklab: Replace $ with ~D\r
+       // RegExp interprets $ as a special character\r
+       // when it's in a replacement string\r
+       text = text.replace(/\$/g,"~D");\r
+\r
+       // Standardize line endings\r
+       text = text.replace(/\r\n/g,"\n"); // DOS to Unix\r
+       text = text.replace(/\r/g,"\n"); // Mac to Unix\r
+\r
+       // Make sure text begins and ends with a couple of newlines:\r
+       text = "\n\n" + text + "\n\n";\r
+\r
+       // Convert all tabs to spaces.\r
+       text = _Detab(text);\r
+\r
+       // Strip any lines consisting only of spaces and tabs.\r
+       // This makes subsequent regexen easier to write, because we can\r
+       // match consecutive blank lines with /\n+/ instead of something\r
+       // contorted like /[ \t]*\n+/ .\r
+       text = text.replace(/^[ \t]+$/mg,"");\r
+\r
+       // Turn block-level HTML blocks into hash entries\r
+       text = _HashHTMLBlocks(text);\r
+\r
+       // Strip link definitions, store in hashes.\r
+       text = _StripLinkDefinitions(text);\r
+\r
+       text = _RunBlockGamut(text);\r
+\r
+       text = _UnescapeSpecialChars(text);\r
+\r
+       // attacklab: Restore dollar signs\r
+       text = text.replace(/~D/g,"$$");\r
+\r
+       // attacklab: Restore tildes\r
+       text = text.replace(/~T/g,"~");\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _StripLinkDefinitions = function(text) {\r
+//\r
+// Strips link definitions from text, stores the URLs and titles in\r
+// hash references.\r
+//\r
+\r
+       // Link defs are in the form: ^[id]: url "optional title"\r
+\r
+       /*\r
+               var text = text.replace(/\r
+                               ^[ ]{0,3}\[(.+)\]:  // id = $1  attacklab: g_tab_width - 1\r
+                                 [ \t]*\r
+                                 \n?                           // maybe *one* newline\r
+                                 [ \t]*\r
+                               <?(\S+?)>?                      // url = $2\r
+                                 [ \t]*\r
+                                 \n?                           // maybe one newline\r
+                                 [ \t]*\r
+                               (?:\r
+                                 (\n*)                         // any lines skipped = $3 attacklab: lookbehind removed\r
+                                 ["(]\r
+                                 (.+?)                         // title = $4\r
+                                 [")]\r
+                                 [ \t]*\r
+                               )?                                      // title is optional\r
+                               (?:\n+|$)\r
+                         /gm,\r
+                         function(){...});\r
+       */\r
+       var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,\r
+               function (wholeMatch,m1,m2,m3,m4) {\r
+                       m1 = m1.toLowerCase();\r
+                       g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive\r
+                       if (m3) {\r
+                               // Oops, found blank lines, so it's not a title.\r
+                               // Put back the parenthetical statement we stole.\r
+                               return m3+m4;\r
+                       } else if (m4) {\r
+                               g_titles[m1] = m4.replace(/"/g,"&quot;");\r
+                       }\r
+                       \r
+                       // Completely remove the definition from the text\r
+                       return "";\r
+               }\r
+       );\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _HashHTMLBlocks = function(text) {\r
+       // attacklab: Double up blank lines to reduce lookaround\r
+       text = text.replace(/\n/g,"\n\n");\r
+\r
+       // Hashify HTML blocks:\r
+       // We only want to do this for block-level HTML tags, such as headers,\r
+       // lists, and tables. That's because we still want to wrap <p>s around\r
+       // "paragraphs" that are wrapped in non-block-level tags, such as anchors,\r
+       // phrase emphasis, and spans. The list of tags we're looking for is\r
+       // hard-coded:\r
+       var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"\r
+       var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"\r
+\r
+       // First, look for nested blocks, e.g.:\r
+       //   <div>\r
+       //     <div>\r
+       //     tags for inner block must be indented.\r
+       //     </div>\r
+       //   </div>\r
+       //\r
+       // The outermost tags must start at the left margin for this to match, and\r
+       // the inner nested divs must be indented.\r
+       // We need to do this before the next, more liberal match, because the next\r
+       // match will start at the first `<div>` and stop at the first `</div>`.\r
+\r
+       // attacklab: This regex can be expensive when it fails.\r
+       /*\r
+               var text = text.replace(/\r
+               (                                               // save in $1\r
+                       ^                                       // start of line  (with /m)\r
+                       <($block_tags_a)        // start tag = $2\r
+                       \b                                      // word break\r
+                                                               // attacklab: hack around khtml/pcre bug...\r
+                       [^\r]*?\n                       // any number of lines, minimally matching\r
+                       </\2>                           // the matching end tag\r
+                       [ \t]*                          // trailing spaces/tabs\r
+                       (?=\n+)                         // followed by a newline\r
+               )                                               // attacklab: there are sentinel newlines at end of document\r
+               /gm,function(){...}};\r
+       */\r
+       text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement);\r
+\r
+       //\r
+       // Now match more liberally, simply from `\n<tag>` to `</tag>\n`\r
+       //\r
+\r
+       /*\r
+               var text = text.replace(/\r
+               (                                               // save in $1\r
+                       ^                                       // start of line  (with /m)\r
+                       <($block_tags_b)        // start tag = $2\r
+                       \b                                      // word break\r
+                                                               // attacklab: hack around khtml/pcre bug...\r
+                       [^\r]*?                         // any number of lines, minimally matching\r
+                       .*</\2>                         // the matching end tag\r
+                       [ \t]*                          // trailing spaces/tabs\r
+                       (?=\n+)                         // followed by a newline\r
+               )                                               // attacklab: there are sentinel newlines at end of document\r
+               /gm,function(){...}};\r
+       */\r
+       text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);\r
+\r
+       // Special case just for <hr />. It was easier to make a special case than\r
+       // to make the other regex more complicated.  \r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // save in $1\r
+                       \n\n                            // Starting after a blank line\r
+                       [ ]{0,3}\r
+                       (<(hr)                          // start tag = $2\r
+                       \b                                      // word break\r
+                       ([^<>])*?                       // \r
+                       \/?>)                           // the matching end tag\r
+                       [ \t]*\r
+                       (?=\n{2,})                      // followed by a blank line\r
+               )\r
+               /g,hashElement);\r
+       */\r
+       text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+       // Special case for standalone HTML comments:\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // save in $1\r
+                       \n\n                            // Starting after a blank line\r
+                       [ ]{0,3}                        // attacklab: g_tab_width - 1\r
+                       <!\r
+                       (--[^\r]*?--\s*)+\r
+                       >\r
+                       [ \t]*\r
+                       (?=\n{2,})                      // followed by a blank line\r
+               )\r
+               /g,hashElement);\r
+       */\r
+       text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+       // PHP and ASP-style processor instructions (<?...?> and <%...%>)\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (?:\r
+                       \n\n                            // Starting after a blank line\r
+               )\r
+               (                                               // save in $1\r
+                       [ ]{0,3}                        // attacklab: g_tab_width - 1\r
+                       (?:\r
+                               <([?%])                 // $2\r
+                               [^\r]*?\r
+                               \2>\r
+                       )\r
+                       [ \t]*\r
+                       (?=\n{2,})                      // followed by a blank line\r
+               )\r
+               /g,hashElement);\r
+       */\r
+       text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+       // attacklab: Undo double lines (see comment at top of this function)\r
+       text = text.replace(/\n\n/g,"\n");\r
+       return text;\r
+}\r
+\r
+var hashElement = function(wholeMatch,m1) {\r
+       var blockText = m1;\r
+\r
+       // Undo double lines\r
+       blockText = blockText.replace(/\n\n/g,"\n");\r
+       blockText = blockText.replace(/^\n/,"");\r
+       \r
+       // strip trailing blank lines\r
+       blockText = blockText.replace(/\n+$/g,"");\r
+       \r
+       // Replace the element text with a marker ("~KxK" where x is its key)\r
+       blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";\r
+       \r
+       return blockText;\r
+};\r
+\r
+var _RunBlockGamut = function(text) {\r
+//\r
+// These are all the transformations that form block-level\r
+// tags like paragraphs, headers, and list items.\r
+//\r
+       text = _DoHeaders(text);\r
+\r
+       // Do Horizontal Rules:\r
+       var key = hashBlock("<hr />");\r
+       text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);\r
+       text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);\r
+       text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);\r
+\r
+       text = _DoLists(text);\r
+       text = _DoCodeBlocks(text);\r
+       text = _DoBlockQuotes(text);\r
+\r
+       // We already ran _HashHTMLBlocks() before, in Markdown(), but that\r
+       // was to escape raw HTML in the original Markdown source. This time,\r
+       // we're escaping the markup we've just created, so that we don't wrap\r
+       // <p> tags around block-level tags.\r
+       text = _HashHTMLBlocks(text);\r
+       text = _FormParagraphs(text);\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _RunSpanGamut = function(text) {\r
+//\r
+// These are all the transformations that occur *within* block-level\r
+// tags like paragraphs, headers, and list items.\r
+//\r
+\r
+       text = _DoCodeSpans(text);\r
+       text = _EscapeSpecialCharsWithinTagAttributes(text);\r
+       text = _EncodeBackslashEscapes(text);\r
+\r
+       // Process anchor and image tags. Images must come first,\r
+       // because ![foo][f] looks like an anchor.\r
+       text = _DoImages(text);\r
+       text = _DoAnchors(text);\r
+\r
+       // Make links out of things like `<http://example.com/>`\r
+       // Must come after _DoAnchors(), because you can use < and >\r
+       // delimiters in inline links like [this](<url>).\r
+       text = _DoAutoLinks(text);\r
+       text = _EncodeAmpsAndAngles(text);\r
+       text = _DoItalicsAndBold(text);\r
+\r
+       // Do hard breaks:\r
+       text = text.replace(/  +\n/g," <br />\n");\r
+\r
+       return text;\r
+}\r
+\r
+var _EscapeSpecialCharsWithinTagAttributes = function(text) {\r
+//\r
+// Within tags -- meaning between < and > -- encode [\ ` * _] so they\r
+// don't conflict with their use in Markdown for code, italics and strong.\r
+//\r
+\r
+       // Build a regex to find HTML tags and comments.  See Friedl's \r
+       // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.\r
+       var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;\r
+\r
+       text = text.replace(regex, function(wholeMatch) {\r
+               var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");\r
+               tag = escapeCharacters(tag,"\\`*_");\r
+               return tag;\r
+       });\r
+\r
+       return text;\r
+}\r
+\r
+var _DoAnchors = function(text) {\r
+//\r
+// Turn Markdown link shortcuts into XHTML <a> tags.\r
+//\r
+       //\r
+       // First, handle reference-style links: [link text] [id]\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                                       // wrap whole match in $1\r
+                       \[\r
+                       (\r
+                               (?:\r
+                                       \[[^\]]*\]              // allow brackets nested one level\r
+                                       |\r
+                                       [^\[]                   // or anything else\r
+                               )*\r
+                       )\r
+                       \]\r
+\r
+                       [ ]?                                    // one optional space\r
+                       (?:\n[ ]*)?                             // one optional newline followed by spaces\r
+\r
+                       \[\r
+                       (.*?)                                   // id = $3\r
+                       \]\r
+               )()()()()                                       // pad remaining backreferences\r
+               /g,_DoAnchors_callback);\r
+       */\r
+       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);\r
+\r
+       //\r
+       // Next, inline-style links: [link text](url "optional title")\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       (                                               // wrap whole match in $1\r
+                               \[\r
+                               (\r
+                                       (?:\r
+                                               \[[^\]]*\]      // allow brackets nested one level\r
+                                       |\r
+                                       [^\[\]]                 // or anything else\r
+                               )\r
+                       )\r
+                       \]\r
+                       \(                                              // literal paren\r
+                       [ \t]*\r
+                       ()                                              // no id, so leave $3 empty\r
+                       <?(.*?)>?                               // href = $4\r
+                       [ \t]*\r
+                       (                                               // $5\r
+                               (['"])                          // quote char = $6\r
+                               (.*?)                           // Title = $7\r
+                               \6                                      // matching quote\r
+                               [ \t]*                          // ignore any spaces/tabs between closing quote and )\r
+                       )?                                              // title is optional\r
+                       \)\r
+               )\r
+               /g,writeAnchorTag);\r
+       */\r
+       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);\r
+\r
+       //\r
+       // Last, handle reference-style shortcuts: [link text]\r
+       // These must come last in case you've also got [link test][1]\r
+       // or [link test](/foo)\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                                       // wrap whole match in $1\r
+                       \[\r
+                       ([^\[\]]+)                              // link text = $2; can't contain '[' or ']'\r
+                       \]\r
+               )()()()()()                                     // pad rest of backreferences\r
+               /g, writeAnchorTag);\r
+       */\r
+       text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);\r
+\r
+       return text;\r
+}\r
+\r
+var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {\r
+       if (m7 == undefined) m7 = "";\r
+       var whole_match = m1;\r
+       var link_text   = m2;\r
+       var link_id      = m3.toLowerCase();\r
+       var url         = m4;\r
+       var title       = m7;\r
+       \r
+       if (url == "") {\r
+               if (link_id == "") {\r
+                       // lower-case and turn embedded newlines into spaces\r
+                       link_id = link_text.toLowerCase().replace(/ ?\n/g," ");\r
+               }\r
+               url = "#"+link_id;\r
+               \r
+               if (g_urls[link_id] != undefined) {\r
+                       url = g_urls[link_id];\r
+                       if (g_titles[link_id] != undefined) {\r
+                               title = g_titles[link_id];\r
+                       }\r
+               }\r
+               else {\r
+                       if (whole_match.search(/\(\s*\)$/m)>-1) {\r
+                               // Special case for explicit empty url\r
+                               url = "";\r
+                       } else {\r
+                               return whole_match;\r
+                       }\r
+               }\r
+       }       \r
+       \r
+       url = escapeCharacters(url,"*_");\r
+       var result = "<a href=\"" + url + "\"";\r
+       \r
+       if (title != "") {\r
+               title = title.replace(/"/g,"&quot;");\r
+               title = escapeCharacters(title,"*_");\r
+               result +=  " title=\"" + title + "\"";\r
+       }\r
+       \r
+       result += ">" + link_text + "</a>";\r
+       \r
+       return result;\r
+}\r
+\r
+\r
+var _DoImages = function(text) {\r
+//\r
+// Turn Markdown image shortcuts into <img> tags.\r
+//\r
+\r
+       //\r
+       // First, handle reference-style labeled images: ![alt text][id]\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // wrap whole match in $1\r
+                       !\[\r
+                       (.*?)                           // alt text = $2\r
+                       \]\r
+\r
+                       [ ]?                            // one optional space\r
+                       (?:\n[ ]*)?                     // one optional newline followed by spaces\r
+\r
+                       \[\r
+                       (.*?)                           // id = $3\r
+                       \]\r
+               )()()()()                               // pad rest of backreferences\r
+               /g,writeImageTag);\r
+       */\r
+       text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);\r
+\r
+       //\r
+       // Next, handle inline images:  ![alt text](url "optional title")\r
+       // Don't forget: encode * and _\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // wrap whole match in $1\r
+                       !\[\r
+                       (.*?)                           // alt text = $2\r
+                       \]\r
+                       \s?                                     // One optional whitespace character\r
+                       \(                                      // literal paren\r
+                       [ \t]*\r
+                       ()                                      // no id, so leave $3 empty\r
+                       <?(\S+?)>?                      // src url = $4\r
+                       [ \t]*\r
+                       (                                       // $5\r
+                               (['"])                  // quote char = $6\r
+                               (.*?)                   // title = $7\r
+                               \6                              // matching quote\r
+                               [ \t]*\r
+                       )?                                      // title is optional\r
+               \)\r
+               )\r
+               /g,writeImageTag);\r
+       */\r
+       text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);\r
+\r
+       return text;\r
+}\r
+\r
+var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {\r
+       var whole_match = m1;\r
+       var alt_text   = m2;\r
+       var link_id      = m3.toLowerCase();\r
+       var url         = m4;\r
+       var title       = m7;\r
+\r
+       if (!title) title = "";\r
+       \r
+       if (url == "") {\r
+               if (link_id == "") {\r
+                       // lower-case and turn embedded newlines into spaces\r
+                       link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");\r
+               }\r
+               url = "#"+link_id;\r
+               \r
+               if (g_urls[link_id] != undefined) {\r
+                       url = g_urls[link_id];\r
+                       if (g_titles[link_id] != undefined) {\r
+                               title = g_titles[link_id];\r
+                       }\r
+               }\r
+               else {\r
+                       return whole_match;\r
+               }\r
+       }       \r
+       \r
+       alt_text = alt_text.replace(/"/g,"&quot;");\r
+       url = escapeCharacters(url,"*_");\r
+       var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";\r
+\r
+       // attacklab: Markdown.pl adds empty title attributes to images.\r
+       // Replicate this bug.\r
+\r
+       //if (title != "") {\r
+               title = title.replace(/"/g,"&quot;");\r
+               title = escapeCharacters(title,"*_");\r
+               result +=  " title=\"" + title + "\"";\r
+       //}\r
+       \r
+       result += " />";\r
+       \r
+       return result;\r
+}\r
+\r
+\r
+var _DoHeaders = function(text) {\r
+\r
+       // Setext-style headers:\r
+       //      Header 1\r
+       //      ========\r
+       //  \r
+       //      Header 2\r
+       //      --------\r
+       //\r
+       text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,\r
+               function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});\r
+\r
+       text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,\r
+               function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});\r
+\r
+       // atx-style headers:\r
+       //  # Header 1\r
+       //  ## Header 2\r
+       //  ## Header 2 with closing hashes ##\r
+       //  ...\r
+       //  ###### Header 6\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       ^(\#{1,6})                              // $1 = string of #'s\r
+                       [ \t]*\r
+                       (.+?)                                   // $2 = Header text\r
+                       [ \t]*\r
+                       \#*                                             // optional closing #'s (not counted)\r
+                       \n+\r
+               /gm, function() {...});\r
+       */\r
+\r
+       text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,\r
+               function(wholeMatch,m1,m2) {\r
+                       var h_level = m1.length;\r
+                       return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");\r
+               });\r
+\r
+       function headerId(m) {\r
+               return m.replace(/[^\w]/g, '').toLowerCase();\r
+       }\r
+       return text;\r
+}\r
+\r
+// This declaration keeps Dojo compressor from outputting garbage:\r
+var _ProcessListItems;\r
+\r
+var _DoLists = function(text) {\r
+//\r
+// Form HTML ordered (numbered) and unordered (bulleted) lists.\r
+//\r
+\r
+       // attacklab: add sentinel to hack around khtml/safari bug:\r
+       // http://bugs.webkit.org/show_bug.cgi?id=11231\r
+       text += "~0";\r
+\r
+       // Re-usable pattern to match any entirel ul or ol list:\r
+\r
+       /*\r
+               var whole_list = /\r
+               (                                                                       // $1 = whole list\r
+                       (                                                               // $2\r
+                               [ ]{0,3}                                        // attacklab: g_tab_width - 1\r
+                               ([*+-]|\d+[.])                          // $3 = first list item marker\r
+                               [ \t]+\r
+                       )\r
+                       [^\r]+?\r
+                       (                                                               // $4\r
+                               ~0                                                      // sentinel for workaround; should be $\r
+                       |\r
+                               \n{2,}\r
+                               (?=\S)\r
+                               (?!                                                     // Negative lookahead for another list item marker\r
+                                       [ \t]*\r
+                                       (?:[*+-]|\d+[.])[ \t]+\r
+                               )\r
+                       )\r
+               )/g\r
+       */\r
+       var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;\r
+\r
+       if (g_list_level) {\r
+               text = text.replace(whole_list,function(wholeMatch,m1,m2) {\r
+                       var list = m1;\r
+                       var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";\r
+\r
+                       // Turn double returns into triple returns, so that we can make a\r
+                       // paragraph for the last item in a list, if necessary:\r
+                       list = list.replace(/\n{2,}/g,"\n\n\n");;\r
+                       var result = _ProcessListItems(list);\r
+       \r
+                       // Trim any trailing whitespace, to put the closing `</$list_type>`\r
+                       // up on the preceding line, to get it past the current stupid\r
+                       // HTML block parser. This is a hack to work around the terrible\r
+                       // hack that is the HTML block parser.\r
+                       result = result.replace(/\s+$/,"");\r
+                       result = "<"+list_type+">" + result + "</"+list_type+">\n";\r
+                       return result;\r
+               });\r
+       } else {\r
+               whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;\r
+               text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {\r
+                       var runup = m1;\r
+                       var list = m2;\r
+\r
+                       var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";\r
+                       // Turn double returns into triple returns, so that we can make a\r
+                       // paragraph for the last item in a list, if necessary:\r
+                       var list = list.replace(/\n{2,}/g,"\n\n\n");;\r
+                       var result = _ProcessListItems(list);\r
+                       result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";   \r
+                       return result;\r
+               });\r
+       }\r
+\r
+       // attacklab: strip sentinel\r
+       text = text.replace(/~0/,"");\r
+\r
+       return text;\r
+}\r
+\r
+_ProcessListItems = function(list_str) {\r
+//\r
+//  Process the contents of a single ordered or unordered list, splitting it\r
+//  into individual list items.\r
+//\r
+       // The $g_list_level global keeps track of when we're inside a list.\r
+       // Each time we enter a list, we increment it; when we leave a list,\r
+       // we decrement. If it's zero, we're not in a list anymore.\r
+       //\r
+       // We do this because when we're not inside a list, we want to treat\r
+       // something like this:\r
+       //\r
+       //    I recommend upgrading to version\r
+       //    8. Oops, now this line is treated\r
+       //    as a sub-list.\r
+       //\r
+       // As a single paragraph, despite the fact that the second line starts\r
+       // with a digit-period-space sequence.\r
+       //\r
+       // Whereas when we're inside a list (or sub-list), that line will be\r
+       // treated as the start of a sub-list. What a kludge, huh? This is\r
+       // an aspect of Markdown's syntax that's hard to parse perfectly\r
+       // without resorting to mind-reading. Perhaps the solution is to\r
+       // change the syntax rules such that sub-lists must start with a\r
+       // starting cardinal number; e.g. "1." or "a.".\r
+\r
+       g_list_level++;\r
+\r
+       // trim trailing blank lines:\r
+       list_str = list_str.replace(/\n{2,}$/,"\n");\r
+\r
+       // attacklab: add sentinel to emulate \z\r
+       list_str += "~0";\r
+\r
+       /*\r
+               list_str = list_str.replace(/\r
+                       (\n)?                                                   // leading line = $1\r
+                       (^[ \t]*)                                               // leading whitespace = $2\r
+                       ([*+-]|\d+[.]) [ \t]+                   // list marker = $3\r
+                       ([^\r]+?                                                // list item text   = $4\r
+                       (\n{1,2}))\r
+                       (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))\r
+               /gm, function(){...});\r
+       */\r
+       list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,\r
+               function(wholeMatch,m1,m2,m3,m4){\r
+                       var item = m4;\r
+                       var leading_line = m1;\r
+                       var leading_space = m2;\r
+\r
+                       if (leading_line || (item.search(/\n{2,}/)>-1)) {\r
+                               item = _RunBlockGamut(_Outdent(item));\r
+                       }\r
+                       else {\r
+                               // Recursion for sub-lists:\r
+                               item = _DoLists(_Outdent(item));\r
+                               item = item.replace(/\n$/,""); // chomp(item)\r
+                               item = _RunSpanGamut(item);\r
+                       }\r
+\r
+                       return  "<li>" + item + "</li>\n";\r
+               }\r
+       );\r
+\r
+       // attacklab: strip sentinel\r
+       list_str = list_str.replace(/~0/g,"");\r
+\r
+       g_list_level--;\r
+       return list_str;\r
+}\r
+\r
+\r
+var _DoCodeBlocks = function(text) {\r
+//\r
+//  Process Markdown `<pre><code>` blocks.\r
+//  \r
+\r
+       /*\r
+               text = text.replace(text,\r
+                       /(?:\n\n|^)\r
+                       (                                                               // $1 = the code block -- one or more lines, starting with a space/tab\r
+                               (?:\r
+                                       (?:[ ]{4}|\t)                   // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width\r
+                                       .*\n+\r
+                               )+\r
+                       )\r
+                       (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width\r
+               /g,function(){...});\r
+       */\r
+\r
+       // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug\r
+       text += "~0";\r
+       \r
+       text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,\r
+               function(wholeMatch,m1,m2) {\r
+                       var codeblock = m1;\r
+                       var nextChar = m2;\r
+               \r
+                       codeblock = _EncodeCode( _Outdent(codeblock));\r
+                       codeblock = _Detab(codeblock);\r
+                       codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines\r
+                       codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace\r
+\r
+                       codeblock = "<pre><code>" + codeblock + "\n</code></pre>";\r
+\r
+                       return hashBlock(codeblock) + nextChar;\r
+               }\r
+       );\r
+\r
+       // attacklab: strip sentinel\r
+       text = text.replace(/~0/,"");\r
+\r
+       return text;\r
+}\r
+\r
+var hashBlock = function(text) {\r
+       text = text.replace(/(^\n+|\n+$)/g,"");\r
+       return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";\r
+}\r
+\r
+\r
+var _DoCodeSpans = function(text) {\r
+//\r
+//   *  Backtick quotes are used for <code></code> spans.\r
+// \r
+//   *  You can use multiple backticks as the delimiters if you want to\r
+//      include literal backticks in the code span. So, this input:\r
+//      \r
+//              Just type ``foo `bar` baz`` at the prompt.\r
+//      \r
+//        Will translate to:\r
+//      \r
+//              <p>Just type <code>foo `bar` baz</code> at the prompt.</p>\r
+//      \r
+//     There's no arbitrary limit to the number of backticks you\r
+//     can use as delimters. If you need three consecutive backticks\r
+//     in your code, use four for delimiters, etc.\r
+//\r
+//  *  You can use spaces to get literal backticks at the edges:\r
+//      \r
+//              ... type `` `bar` `` ...\r
+//      \r
+//        Turns to:\r
+//      \r
+//              ... type <code>`bar`</code> ...\r
+//\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       (^|[^\\])                                       // Character before opening ` can't be a backslash\r
+                       (`+)                                            // $2 = Opening run of `\r
+                       (                                                       // $3 = The code block\r
+                               [^\r]*?\r
+                               [^`]                                    // attacklab: work around lack of lookbehind\r
+                       )\r
+                       \2                                                      // Matching closer\r
+                       (?!`)\r
+               /gm, function(){...});\r
+       */\r
+\r
+       text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,\r
+               function(wholeMatch,m1,m2,m3,m4) {\r
+                       var c = m3;\r
+                       c = c.replace(/^([ \t]*)/g,""); // leading whitespace\r
+                       c = c.replace(/[ \t]*$/g,"");   // trailing whitespace\r
+                       c = _EncodeCode(c);\r
+                       return m1+"<code>"+c+"</code>";\r
+               });\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _EncodeCode = function(text) {\r
+//\r
+// Encode/escape certain characters inside Markdown code runs.\r
+// The point is that in code, these characters are literals,\r
+// and lose their special Markdown meanings.\r
+//\r
+       // Encode all ampersands; HTML entities are not\r
+       // entities within a Markdown code span.\r
+       text = text.replace(/&/g,"&amp;");\r
+\r
+       // Do the angle bracket song and dance:\r
+       text = text.replace(/</g,"&lt;");\r
+       text = text.replace(/>/g,"&gt;");\r
+\r
+       // Now, escape characters that are magic in Markdown:\r
+       text = escapeCharacters(text,"\*_{}[]\\",false);\r
+\r
+// jj the line above breaks this:\r
+//---\r
+\r
+//* Item\r
+\r
+//   1. Subitem\r
+\r
+//            special char: *\r
+//---\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _DoItalicsAndBold = function(text) {\r
+\r
+       // <strong> must go first:\r
+       text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,\r
+               "<strong>$2</strong>");\r
+\r
+       text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,\r
+               "<em>$2</em>");\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _DoBlockQuotes = function(text) {\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                                               // Wrap whole match in $1\r
+                       (\r
+                               ^[ \t]*>[ \t]?                  // '>' at the start of a line\r
+                               .+\n                                    // rest of the first line\r
+                               (.+\n)*                                 // subsequent consecutive lines\r
+                               \n*                                             // blanks\r
+                       )+\r
+               )\r
+               /gm, function(){...});\r
+       */\r
+\r
+       text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,\r
+               function(wholeMatch,m1) {\r
+                       var bq = m1;\r
+\r
+                       // attacklab: hack around Konqueror 3.5.4 bug:\r
+                       // "----------bug".replace(/^-/g,"") == "bug"\r
+\r
+                       bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0");       // trim one level of quoting\r
+\r
+                       // attacklab: clean up hack\r
+                       bq = bq.replace(/~0/g,"");\r
+\r
+                       bq = bq.replace(/^[ \t]+$/gm,"");               // trim whitespace-only lines\r
+                       bq = _RunBlockGamut(bq);                                // recurse\r
+                       \r
+                       bq = bq.replace(/(^|\n)/g,"$1  ");\r
+                       // These leading spaces screw with <pre> content, so we need to fix that:\r
+                       bq = bq.replace(\r
+                                       /(\s*<pre>[^\r]+?<\/pre>)/gm,\r
+                               function(wholeMatch,m1) {\r
+                                       var pre = m1;\r
+                                       // attacklab: hack around Konqueror 3.5.4 bug:\r
+                                       pre = pre.replace(/^  /mg,"~0");\r
+                                       pre = pre.replace(/~0/g,"");\r
+                                       return pre;\r
+                               });\r
+                       \r
+                       return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");\r
+               });\r
+       return text;\r
+}\r
+\r
+\r
+var _FormParagraphs = function(text) {\r
+//\r
+//  Params:\r
+//    $text - string to process with html <p> tags\r
+//\r
+\r
+       // Strip leading and trailing lines:\r
+       text = text.replace(/^\n+/g,"");\r
+       text = text.replace(/\n+$/g,"");\r
+\r
+       var grafs = text.split(/\n{2,}/g);\r
+       var grafsOut = new Array();\r
+\r
+       //\r
+       // Wrap <p> tags.\r
+       //\r
+       var end = grafs.length;\r
+       for (var i=0; i<end; i++) {\r
+               var str = grafs[i];\r
+\r
+               // if this is an HTML marker, copy it\r
+               if (str.search(/~K(\d+)K/g) >= 0) {\r
+                       grafsOut.push(str);\r
+               }\r
+               else if (str.search(/\S/) >= 0) {\r
+                       str = _RunSpanGamut(str);\r
+                       str = str.replace(/^([ \t]*)/g,"<p>");\r
+                       str += "</p>"\r
+                       grafsOut.push(str);\r
+               }\r
+\r
+       }\r
+\r
+       //\r
+       // Unhashify HTML blocks\r
+       //\r
+       end = grafsOut.length;\r
+       for (var i=0; i<end; i++) {\r
+               // if this is a marker for an html block...\r
+               while (grafsOut[i].search(/~K(\d+)K/) >= 0) {\r
+                       var blockText = g_html_blocks[RegExp.$1];\r
+                       blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs\r
+                       grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);\r
+               }\r
+       }\r
+\r
+       return grafsOut.join("\n\n");\r
+}\r
+\r
+\r
+var _EncodeAmpsAndAngles = function(text) {\r
+// Smart processing for ampersands and angle brackets that need to be encoded.\r
+       \r
+       // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:\r
+       //   http://bumppo.net/projects/amputator/\r
+       text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");\r
+       \r
+       // Encode naked <'s\r
+       text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");\r
+       \r
+       return text;\r
+}\r
+\r
+\r
+var _EncodeBackslashEscapes = function(text) {\r
+//\r
+//   Parameter:  String.\r
+//   Returns:  The string, with after processing the following backslash\r
+//                        escape sequences.\r
+//\r
+\r
+       // attacklab: The polite way to do this is with the new\r
+       // escapeCharacters() function:\r
+       //\r
+       //      text = escapeCharacters(text,"\\",true);\r
+       //      text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);\r
+       //\r
+       // ...but we're sidestepping its use of the (slow) RegExp constructor\r
+       // as an optimization for Firefox.  This function gets called a LOT.\r
+\r
+       text = text.replace(/\\(\\)/g,escapeCharacters_callback);\r
+       text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);\r
+       return text;\r
+}\r
+\r
+\r
+var _DoAutoLinks = function(text) {\r
+\r
+       text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");\r
+\r
+       // Email addresses: <address@domain.foo>\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       <\r
+                       (?:mailto:)?\r
+                       (\r
+                               [-.\w]+\r
+                               \@\r
+                               [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+\r
+                       )\r
+                       >\r
+               /gi, _DoAutoLinks_callback());\r
+       */\r
+       text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,\r
+               function(wholeMatch,m1) {\r
+                       return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );\r
+               }\r
+       );\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _EncodeEmailAddress = function(addr) {\r
+//\r
+//  Input: an email address, e.g. "foo@example.com"\r
+//\r
+//  Output: the email address as a mailto link, with each character\r
+//     of the address encoded as either a decimal or hex entity, in\r
+//     the hopes of foiling most address harvesting spam bots. E.g.:\r
+//\r
+//     <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;\r
+//        x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;\r
+//        &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>\r
+//\r
+//  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk\r
+//  mailing list: <http://tinyurl.com/yu7ue>\r
+//\r
+\r
+       // attacklab: why can't javascript speak hex?\r
+       function char2hex(ch) {\r
+               var hexDigits = '0123456789ABCDEF';\r
+               var dec = ch.charCodeAt(0);\r
+               return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));\r
+       }\r
+\r
+       var encode = [\r
+               function(ch){return "&#"+ch.charCodeAt(0)+";";},\r
+               function(ch){return "&#x"+char2hex(ch)+";";},\r
+               function(ch){return ch;}\r
+       ];\r
+\r
+       addr = "mailto:" + addr;\r
+\r
+       addr = addr.replace(/./g, function(ch) {\r
+               if (ch == "@") {\r
+                       // this *must* be encoded. I insist.\r
+                       ch = encode[Math.floor(Math.random()*2)](ch);\r
+               } else if (ch !=":") {\r
+                       // leave ':' alone (to spot mailto: later)\r
+                       var r = Math.random();\r
+                       // roughly 10% raw, 45% hex, 45% dec\r
+                       ch =  (\r
+                                       r > .9  ?       encode[2](ch)   :\r
+                                       r > .45 ?       encode[1](ch)   :\r
+                                                               encode[0](ch)\r
+                               );\r
+               }\r
+               return ch;\r
+       });\r
+\r
+       addr = "<a href=\"" + addr + "\">" + addr + "</a>";\r
+       addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part\r
+\r
+       return addr;\r
+}\r
+\r
+\r
+var _UnescapeSpecialChars = function(text) {\r
+//\r
+// Swap back in all the special characters we've hidden.\r
+//\r
+       text = text.replace(/~E(\d+)E/g,\r
+               function(wholeMatch,m1) {\r
+                       var charCodeToReplace = parseInt(m1);\r
+                       return String.fromCharCode(charCodeToReplace);\r
+               }\r
+       );\r
+       return text;\r
+}\r
+\r
+\r
+var _Outdent = function(text) {\r
+//\r
+// Remove one level of line-leading tabs or spaces\r
+//\r
+\r
+       // attacklab: hack around Konqueror 3.5.4 bug:\r
+       // "----------bug".replace(/^-/g,"") == "bug"\r
+\r
+       text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width\r
+\r
+       // attacklab: clean up hack\r
+       text = text.replace(/~0/g,"")\r
+\r
+       return text;\r
+}\r
+\r
+var _Detab = function(text) {\r
+// attacklab: Detab's completely rewritten for speed.\r
+// In perl we could fix it by anchoring the regexp with \G.\r
+// In javascript we're less fortunate.\r
+\r
+       // expand first n-1 tabs\r
+       text = text.replace(/\t(?=\t)/g,"    "); // attacklab: g_tab_width\r
+\r
+       // replace the nth with two sentinels\r
+       text = text.replace(/\t/g,"~A~B");\r
+\r
+       // use the sentinel to anchor our regex so it doesn't explode\r
+       text = text.replace(/~B(.+?)~A/g,\r
+               function(wholeMatch,m1,m2) {\r
+                       var leadingText = m1;\r
+                       var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width\r
+\r
+                       // there *must* be a better way to do this:\r
+                       for (var i=0; i<numSpaces; i++) leadingText+=" ";\r
+\r
+                       return leadingText;\r
+               }\r
+       );\r
+\r
+       // clean up sentinels\r
+       text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width\r
+       text = text.replace(/~B/g,"");\r
+\r
+       return text;\r
+}\r
+\r
+\r
+//\r
+//  attacklab: Utility functions\r
+//\r
+\r
+\r
+var escapeCharacters = function(text, charsToEscape, afterBackslash) {\r
+       // First we have to escape the escape characters so that\r
+       // we can build a character class out of them\r
+       var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";\r
+\r
+       if (afterBackslash) {\r
+               regexString = "\\\\" + regexString;\r
+       }\r
+\r
+       var regex = new RegExp(regexString,"g");\r
+       text = text.replace(regex,escapeCharacters_callback);\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var escapeCharacters_callback = function(wholeMatch,m1) {\r
+       var charCodeToEscape = m1.charCodeAt(0);\r
+       return "~E"+charCodeToEscape+"E";\r
+}\r
+\r
+} // end of Showdown.converter\r
+\r
+// export\r
+if (typeof exports != 'undefined') exports.Showdown = Showdown;
+
+});
diff --git a/static/vendor/showdown.mod.min.js b/static/vendor/showdown.mod.min.js
new file mode 100644 (file)
index 0000000..ba084e6
--- /dev/null
@@ -0,0 +1,66 @@
+require.define('/node_modules/showdown.js', function(require, module, exports, __dirname, __filename, undefined){
+
+//
+// showdown.js -- A javascript port of Markdown.
+//
+// Copyright (c) 2007 John Fraser.
+//
+// Original Markdown Copyright (c) 2004-2005 John Gruber
+//   <http://daringfireball.net/projects/markdown/>
+//
+// Redistributable under a BSD-style open source license.
+// See license.txt for more information.
+//
+// The full source distribution is at:
+//
+//                             A A L
+//                             T C A
+//                             T K B
+//
+//   <http://www.attacklab.net/>
+//
+//
+// Wherever possible, Showdown is a straight, line-by-line port
+// of the Perl version of Markdown.
+//
+// This is not a normal parser design; it's basically just a
+// series of string substitutions.  It's hard to read and
+// maintain this way,  but keeping Showdown close to the original
+// design makes it easier to port new features.
+//
+// More importantly, Showdown behaves like markdown.pl in most
+// edge cases.  So web applications can do client-side preview
+// in Javascript, and then build identical HTML on the server.
+//
+// This port needs the new RegExp functionality of ECMA 262,
+// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers
+// should do fine.  Even with the new regular expression features,
+// We do a lot of work to emulate Perl's regex functionality.
+// The tricky changes in this file mostly have the "attacklab:"
+// label.  Major or self-explanatory changes don't.
+//
+// Smart diff tools like Araxis Merge will be able to match up
+// this file with markdown.pl in a useful way.  A little tweaking
+// helps: in a copy of markdown.pl, replace "#" with "//" and
+// replace "$text" with "text".  Be sure to ignore whitespace
+// and line endings.
+//
+//
+// Showdown usage:
+//
+//   var text = "Markdown *rocks*.";
+//
+//   var converter = new Showdown.converter();
+//   var html = converter.makeHtml(text);
+//
+//   alert(html);
+//
+// Note: move the sample code to the bottom of this
+// file before uncommenting it.
+//
+//
+// Showdown namespace
+//
+var Showdown={};Showdown.converter=function(){var a,b,c,d=0;this.makeHtml=function(d){a=[],b=[],c=[],d=d.replace(/~/g,"~T"),d=d.replace(/\$/g,"~D"),d=d.replace(/\r\n/g,"\n"),d=d.replace(/\r/g,"\n"),d="\n\n"+d+"\n\n",d=E(d),d=d.replace(/^[ \t]+$/mg,""),d=f(d),d=e(d),d=h(d),d=C(d),d=d.replace(/~D/g,"$$"),d=d.replace(/~T/g,"~");return d};var e=function(c){var c=c.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,function(c,d,e,f,g){d=d.toLowerCase(),a[d]=y(e);if(f)return f+g;g&&(b[d]=g.replace(/"/g,"&quot;"));return""});return c},f=function(a){a=a.replace(/\n/g,"\n\n");var b="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del",c="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";a=a.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,g),a=a.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,g),a=a.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,g),a=a.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,g),a=a.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,g),a=a.replace(/\n\n/g,"\n");return a},g=function(a,b){var d=b;d=d.replace(/\n\n/g,"\n"),d=d.replace(/^\n/,""),d=d.replace(/\n+$/g,""),d="\n\n~K"+(c.push(d)-1)+"K\n\n";return d},h=function(a){a=o(a);var b=s("<hr />");a=a.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,b),a=q(a),a=r(a),a=w(a),a=f(a),a=x(a);return a},i=function(a){a=t(a),a=j(a),a=z(a),a=m(a),a=k(a),a=A(a),a=y(a),a=v(a),a=a.replace(/  +\n/g," <br />\n");return a},j=function(a){var b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;a=a.replace(b,function(a){var b=a.replace(/(.)<\/?code>(?=.)/g,"$1`");b=F(b,"\\`*_");return b});return a},k=function(a){a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,l),a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,l),a=a.replace(/(\[([^\[\]]+)\])()()()()()/g,l);return a},l=function(c,d,e,f,g,h,i,j){j==undefined&&(j="");var k=d,l=e,m=f.toLowerCase(),n=g,o=j;if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(a[m]!=undefined)n=a[m],b[m]!=undefined&&(o=b[m]);else if(k.search(/\(\s*\)$/m)>-1)n="";else return k}n=F(n,"*_");var p='<a href="'+n+'"';o!=""&&(o=o.replace(/"/g,"&quot;"),o=F(o,"*_"),p+=' title="'+o+'"'),p+=">"+l+"</a>";return p},m=function(a){a=a.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,n),a=a.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,n);return a},n=function(c,d,e,f,g,h,i,j){var k=d,l=e,m=f.toLowerCase(),n=g,o=j;o||(o="");if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(a[m]!=undefined)n=a[m],b[m]!=undefined&&(o=b[m]);else return k}l=l.replace(/"/g,"&quot;"),n=F(n,"*_");var p='<img src="'+n+'" alt="'+l+'"';o=o.replace(/"/g,"&quot;"),o=F(o,"*_"),p+=' title="'+o+'"',p+=" />";return p},o=function(a){function b(a){return a.replace(/[^\w]/g,"").toLowerCase()}a=a.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,function(a,c){return s('<h1 id="'+b(c)+'">'+i(c)+"</h1>")}),a=a.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(a,c){return s('<h2 id="'+b(c)+'">'+i(c)+"</h2>")}),a=a.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(a,c,d){var e=c.length;return s("<h"+e+' id="'+b(d)+'">'+i(d)+"</h"+e+">")});return a},p,q=function(a){a+="~0";var b=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;d?a=a.replace(b,function(a,b,c){var d=b,e=c.search(/[*+-]/g)>-1?"ul":"ol";d=d.replace(/\n{2,}/g,"\n\n\n");var f=p(d);f=f.replace(/\s+$/,""),f="<"+e+">"+f+"</"+e+">\n";return f}):(b=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g,a=a.replace(b,function(a,b,c,d){var e=b,f=c,g=d.search(/[*+-]/g)>-1?"ul":"ol",f=f.replace(/\n{2,}/g,"\n\n\n"),h=p(f);h=e+"<"+g+">\n"+h+"</"+g+">\n";return h})),a=a.replace(/~0/,"");return a};p=function(a){d++,a=a.replace(/\n{2,}$/,"\n"),a+="~0",a=a.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,function(a,b,c,d,e){var f=e,g=b,j=c;g||f.search(/\n{2,}/)>-1?f=h(D(f)):(f=q(D(f)),f=f.replace(/\n$/,""),f=i(f));return"<li>"+f+"</li>\n"}),a=a.replace(/~0/g,""),d--;return a};var r=function(a){a+="~0",a=a.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(a,b,c){var d=b,e=c;d=u(D(d)),d=E(d),d=d.replace(/^\n+/g,""),d=d.replace(/\n+$/g,""),d="<pre><code>"+d+"\n</code></pre>";return s(d)+e}),a=a.replace(/~0/,"");return a},s=function(a){a=a.replace(/(^\n+|\n+$)/g,"");return"\n\n~K"+(c.push(a)-1)+"K\n\n"},t=function(a){a=a.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(a,b,c,d,e){var f=d;f=f.replace(/^([ \t]*)/g,""),f=f.replace(/[ \t]*$/g,""),f=u(f);return b+"<code>"+f+"</code>"});return a},u=function(a){a=a.replace(/&/g,"&amp;"),a=a.replace(/</g,"&lt;"),a=a.replace(/>/g,"&gt;"),a=F(a,"*_{}[]\\",!1);return a},v=function(a){a=a.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"<strong>$2</strong>"),a=a.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"<em>$2</em>");return a},w=function(a){a=a.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(a,b){var c=b;c=c.replace(/^[ \t]*>[ \t]?/gm,"~0"),c=c.replace(/~0/g,""),c=c.replace(/^[ \t]+$/gm,""),c=h(c),c=c.replace(/(^|\n)/g,"$1  "),c=c.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm,function(a,b){var c=b;c=c.replace(/^  /mg,"~0"),c=c.replace(/~0/g,"");return c});return s("<blockquote>\n"+c+"\n</blockquote>")});return a},x=function(a){a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,"");var b=a.split(/\n{2,}/g),d=[],e=b.length;for(var f=0;f<e;f++){var g=b[f];g.search(/~K(\d+)K/g)>=0?d.push(g):g.search(/\S/)>=0&&(g=i(g),g=g.replace(/^([ \t]*)/g,"<p>"),g+="</p>",d.push(g))}e=d.length;for(var f=0;f<e;f++)while(d[f].search(/~K(\d+)K/)>=0){var h=c[RegExp.$1];h=h.replace(/\$/g,"$$$$"),d[f]=d[f].replace(/~K\d+K/,h)}return d.join("\n\n")},y=function(a){a=a.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;"),a=a.replace(/<(?![a-z\/?\$!])/gi,"&lt;");return a},z=function(a){a=a.replace(/\\(\\)/g,G),a=a.replace(/\\([`*_{}\[\]()>#+-.!])/g,G);return a},A=function(a){a=a.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,'<a href="$1">$1</a>'),a=a.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,function(a,b){return B(C(b))});return a},B=function(a){function b(a){var b="0123456789ABCDEF",c=a.charCodeAt(0);return b.charAt(c>>4)+b.charAt(c&15)}var c=[function(a){return"&#"+a.charCodeAt(0)+";"},function(a){return"&#x"+b(a)+";"},function(a){return a}];a="mailto:"+a,a=a.replace(/./g,function(a){if(a=="@")a=c[Math.floor(Math.random()*2)](a);else if(a!=":"){var b=Math.random();a=b>.9?c[2](a):b>.45?c[1](a):c[0](a)}return a}),a='<a href="'+a+'">'+a+"</a>",a=a.replace(/">.+:/g,'">');return a},C=function(a){a=a.replace(/~E(\d+)E/g,function(a,b){var c=parseInt(b);return String.fromCharCode(c)});return a},D=function(a){a=a.replace(/^(\t|[ ]{1,4})/gm,"~0"),a=a.replace(/~0/g,"");return a},E=function(a){a=a.replace(/\t(?=\t)/g,"    "),a=a.replace(/\t/g,"~A~B"),a=a.replace(/~B(.+?)~A/g,function(a,b,c){var d=b,e=4-d.length%4;for(var f=0;f<e;f++)d+=" ";return d}),a=a.replace(/~A/g,"    "),a=a.replace(/~B/g,"");return a},F=function(a,b,c){var d="(["+b.replace(/([\[\]\\])/g,"\\$1")+"])";c&&(d="\\\\"+d);var e=new RegExp(d,"g");a=a.replace(e,G);return a},G=function(a,b){var c=b.charCodeAt(0);return"~E"+c+"E"}},typeof exports!="undefined"&&(exports.Showdown=Showdown)
+
+});
index 5b55f32..a9e6f66 100644 (file)
@@ -1,31 +1,8 @@
-// Underscore.js 1.3.1
-// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Underscore is freely distributable under the MIT license.
-// Portions of Underscore are inspired or borrowed from Prototype,
-// Oliver Steele's Functional, and John Resig's Micro-Templating.
-// For all details and documentation:
-// http://documentcloud.github.com/underscore
-(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
-c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
-h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
-b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
-null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
-function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
-e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
-function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
-return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
-c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
-b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
-return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
-d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
-var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
-c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
-a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
-b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
-1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
-b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
-b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),
-function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
-u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
-function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
-true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
+//     Underscore.js 1.3.1
+//     (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+//     Underscore is freely distributable under the MIT license.
+//     Portions of Underscore are inspired or borrowed from Prototype,
+//     Oliver Steele's Functional, and John Resig's Micro-Templating.
+//     For all details and documentation:
+//     http://documentcloud.github.com/underscore
+(function(){function C(e,t,n){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e._chain&&(e=e._wrapped),t._chain&&(t=t._wrapped);if(e.isEqual&&S.isFunction(e.isEqual))return e.isEqual(t);if(t.isEqual&&S.isFunction(t.isEqual))return t.isEqual(e);var r=a.call(e);if(r!=a.call(t))return!1;switch(r){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var i=n.length;while(i--)if(n[i]==e)return!0;n.push(e);var s=0,o=!0;if(r=="[object Array]"){s=e.length,o=s==t.length;if(o)while(s--)if(!(o=s in e==s in t&&C(e[s],t[s],n)))break}else{if("constructor"in e!="constructor"in t||e.constructor!=t.constructor)return!1;for(var u in e)if(S.has(e,u)){s++;if(!(o=S.has(t,u)&&C(e[u],t[u],n)))break}if(o){for(u in t)if(S.has(t,u)&&!(s--))break;o=!s}}return n.pop(),o}var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.slice,u=r.unshift,a=i.toString,f=i.hasOwnProperty,l=r.forEach,c=r.map,h=r.reduce,p=r.reduceRight,d=r.filter,v=r.every,m=r.some,g=r.indexOf,y=r.lastIndexOf,b=Array.isArray,w=Object.keys,E=s.bind,S=function(e){return new O(e)};typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(exports=module.exports=S),exports._=S):e._=S,S.VERSION="1.3.1";var x=S.each=S.forEach=function(e,t,r){if(e==null)return;if(l&&e.forEach===l)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;i<s;i++)if(i in e&&t.call(r,e[i],i,e)===n)return}else for(var o in e)if(S.has(e,o)&&t.call(r,e[o],o,e)===n)return};S.map=S.collect=function(e,t,n){var r=[];return e==null?r:c&&e.map===c?e.map(t,n):(x(e,function(e,i,s){r[r.length]=t.call(n,e,i,s)}),e.length===+e.length&&(r.length=e.length),r)},S.reduce=S.foldl=S.inject=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(h&&e.reduce===h)return r&&(t=S.bind(t,r)),i?e.reduce(t,n):e.reduce(t);x(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError("Reduce of empty array with no initial value");return n},S.reduceRight=S.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(p&&e.reduceRight===p)return r&&(t=S.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=S.toArray(e).reverse();return r&&!i&&(t=S.bind(t,r)),i?S.reduce(s,t,n,r):S.reduce(s,t)},S.find=S.detect=function(e,t,n){var r;return T(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},S.filter=S.select=function(e,t,n){var r=[];return e==null?r:d&&e.filter===d?e.filter(t,n):(x(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},S.reject=function(e,t,n){var r=[];return e==null?r:(x(e,function(e,i,s){t.call(n,e,i,s)||(r[r.length]=e)}),r)},S.every=S.all=function(e,t,r){var i=!0;return e==null?i:v&&e.every===v?e.every(t,r):(x(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),i)};var T=S.some=S.any=function(e,t,r){t||(t=S.identity);var i=!1;return e==null?i:m&&e.some===m?e.some(t,r):(x(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};S.include=S.contains=function(e,t){var n=!1;return e==null?n:g&&e.indexOf===g?e.indexOf(t)!=-1:(n=T(e,function(e){return e===t}),n)},S.invoke=function(e,t){var n=o.call(arguments,2);return S.map(e,function(e){return(S.isFunction(t)?t||e:e[t]).apply(e,n)})},S.pluck=function(e,t){return S.map(e,function(e){return e[t]})},S.max=function(e,t,n){if(!t&&S.isArray(e))return Math.max.apply(Math,e);if(!t&&S.isEmpty(e))return-Infinity;var r={computed:-Infinity};return x(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},S.min=function(e,t,n){if(!t&&S.isArray(e))return Math.min.apply(Math,e);if(!t&&S.isEmpty(e))return Infinity;var r={computed:Infinity};return x(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o<r.computed&&(r={value:e,computed:o})}),r.value},S.shuffle=function(e){var t=[],n;return x(e,function(e,r,i){r==0?t[0]=e:(n=Math.floor(Math.random()*(r+1)),t[r]=t[n],t[n]=e)}),t},S.sortBy=function(e,t,n){return S.pluck(S.map(e,function(e,r,i){return{value:e,criteria:t.call(n,e,r,i)}}).sort(function(e,t){var n=e.criteria,r=t.criteria;return n<r?-1:n>r?1:0}),"value")},S.groupBy=function(e,t){var n={},r=S.isFunction(t)?t:function(e){return e[t]};return x(e,function(e,t){var i=r(e,t);(n[i]||(n[i]=[])).push(e)}),n},S.sortedIndex=function(e,t,n){n||(n=S.identity);var r=0,i=e.length;while(r<i){var s=r+i>>1;n(e[s])<n(t)?r=s+1:i=s}return r},S.toArray=function(e){return e?e.toArray?e.toArray():S.isArray(e)?o.call(e):S.isArguments(e)?o.call(e):S.values(e):[]},S.size=function(e){return S.toArray(e).length},S.first=S.head=function(e,t,n){return t!=null&&!n?o.call(e,0,t):e[0]},S.initial=function(e,t,n){return o.call(e,0,e.length-(t==null||n?1:t))},S.last=function(e,t,n){return t!=null&&!n?o.call(e,Math.max(e.length-t,0)):e[e.length-1]},S.rest=S.tail=function(e,t,n){return o.call(e,t==null||n?1:t)},S.compact=function(e){return S.filter(e,function(e){return!!e})},S.flatten=function(e,t){return S.reduce(e,function(e,n){return S.isArray(n)?e.concat(t?n:S.flatten(n)):(e[e.length]=n,e)},[])},S.without=function(e){return S.difference(e,o.call(arguments,1))},S.uniq=S.unique=function(e,t,n){var r=n?S.map(e,n):e,i=[];return S.reduce(r,function(n,r,s){if(0==s||(t===!0?S.last(n)!=r:!S.include(n,r)))n[n.length]=r,i[i.length]=e[s];return n},[]),i},S.union=function(){return S.uniq(S.flatten(arguments,!0))},S.intersection=S.intersect=function(e){var t=o.call(arguments,1);return S.filter(S.uniq(e),function(e){return S.every(t,function(t){return S.indexOf(t,e)>=0})})},S.difference=function(e){var t=S.flatten(o.call(arguments,1));return S.filter(e,function(e){return!S.include(t,e)})},S.zip=function(){var e=o.call(arguments),t=S.max(S.pluck(e,"length")),n=new Array(t);for(var r=0;r<t;r++)n[r]=S.pluck(e,""+r);return n},S.indexOf=function(e,t,n){if(e==null)return-1;var r,i;if(n)return r=S.sortedIndex(e,t),e[r]===t?r:-1;if(g&&e.indexOf===g)return e.indexOf(t);for(r=0,i=e.length;r<i;r++)if(r in e&&e[r]===t)return r;return-1},S.lastIndexOf=function(e,t){if(e==null)return-1;if(y&&e.lastIndexOf===y)return e.lastIndexOf(t);var n=e.length;while(n--)if(n in e&&e[n]===t)return n;return-1},S.range=function(e,t,n){arguments.length<=1&&(t=e||0,e=0),n=arguments[2]||1;var r=Math.max(Math.ceil((t-e)/n),0),i=0,s=new Array(r);while(i<r)s[i++]=e,e+=n;return s};var N=function(){};S.bind=function(t,n){var r,i;if(t.bind===E&&E)return E.apply(t,o.call(arguments,1));if(!S.isFunction(t))throw new TypeError;return i=o.call(arguments,2),r=function(){if(this instanceof r){N.prototype=t.prototype;var e=new N,s=t.apply(e,i.concat(o.call(arguments)));return Object(s)===s?s:e}return t.apply(n,i.concat(o.call(arguments)))}},S.bindAll=function(e){var t=o.call(arguments,1);return t.length==0&&(t=S.functions(e)),x(t,function(t){e[t]=S.bind(e[t],e)}),e},S.memoize=function(e,t){var n={};return t||(t=S.identity),function(){var r=t.apply(this,arguments);return S.has(n,r)?n[r]:n[r]=e.apply(this,arguments)}},S.delay=function(e,t){var n=o.call(arguments,2);return setTimeout(function(){return e.apply(e,n)},t)},S.defer=function(e){return S.delay.apply(S,[e,1].concat(o.call(arguments,1)))},S.throttle=function(e,t){var n,r,i,s,o,u=S.debounce(function(){o=s=!1},t);return function(){n=this,r=arguments;var a=function(){i=null,o&&e.apply(n,r),u()};i||(i=setTimeout(a,t)),s?o=!0:e.apply(n,r),u(),s=!0}},S.debounce=function(e,t){var n;return function(){var r=this,i=arguments,s=function(){n=null,e.apply(r,i)};clearTimeout(n),n=setTimeout(s,t)}},S.once=function(e){var t=!1,n;return function(){return t?n:(t=!0,n=e.apply(this,arguments))}},S.wrap=function(e,t){return function(){var n=[e].concat(o.call(arguments,0));return t.apply(this,n)}},S.compose=function(){var e=arguments;return function(){var t=arguments;for(var n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},S.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},S.keys=w||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)S.has(e,n)&&(t[t.length]=n);return t},S.values=function(e){return S.map(e,S.identity)},S.functions=S.methods=function(e){var t=[];for(var n in e)S.isFunction(e[n])&&t.push(n);return t.sort()},S.extend=function(e){return x(o.call(arguments,1),function(t){for(var n in t)e[n]=t[n]}),e},S.defaults=function(e){return x(o.call(arguments,1),function(t){for(var n in t)e[n]==null&&(e[n]=t[n])}),e},S.clone=function(e){return S.isObject(e)?S.isArray(e)?e.slice():S.extend({},e):e},S.tap=function(e,t){return t(e),e},S.isEqual=function(e,t){return C(e,t,[])},S.isEmpty=function(e){if(S.isArray(e)||S.isString(e))return e.length===0;for(var t in e)if(S.has(e,t))return!1;return!0},S.isElement=function(e){return!!e&&e.nodeType==1},S.isArray=b||function(e){return a.call(e)=="[object Array]"},S.isObject=function(e){return e===Object(e)},S.isArguments=function(e){return a.call(e)=="[object Arguments]"},S.isArguments(arguments)||(S.isArguments=function(e){return!!e&&!!S.has(e,"callee")}),S.isFunction=function(e){return a.call(e)=="[object Function]"},S.isString=function(e){return a.call(e)=="[object String]"},S.isNumber=function(e){return a.call(e)=="[object Number]"},S.isNaN=function(e){return e!==e},S.isBoolean=function(e){return e===!0||e===!1||a.call(e)=="[object Boolean]"},S.isDate=function(e){return a.call(e)=="[object Date]"},S.isRegExp=function(e){return a.call(e)=="[object RegExp]"},S.isNull=function(e){return e===null},S.isUndefined=function(e){return e===void 0},S.has=function(e,t){return f.call(e,t)},S.noConflict=function(){return e._=t,this},S.identity=function(e){return e},S.times=function(e,t,n){for(var r=0;r<e;r++)t.call(n,r)},S.escape=function(e){return(""+e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")},S.mixin=function(e){x(S.functions(e),function(t){_(t,S[t]=e[t])})};var k=0;S.uniqueId=function(e){var t=k++;return e?e+t:t},S.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var L=/.^/,A=function(e){return e.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};S.template=function(e,t){var n=S.templateSettings,r="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(n.escape||L,function(e,t){return"',_.escape("+A(t)+"),'"}).replace(n.interpolate||L,function(e,t){return"',"+A(t)+",'"}).replace(n.evaluate||L,function(e,t){return"');"+A(t).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",i=new Function("obj","_",r);return t?i(t,S):function(e){return i.call(this,e,S)}},S.chain=function(e){return S(e).chain()};var O=function(e){this._wrapped=e};S.prototype=O.prototype;var M=function(e,t){return t?S(e).chain():e},_=function(e,t){O.prototype[e]=function(){var e=o.call(arguments);return u.call(e,this._wrapped),M(t.apply(S,e),this._chain)}};S.mixin(S),x(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];O.prototype[e]=function(){var n=this._wrapped;t.apply(n,arguments);var r=n.length;return(e=="shift"||e=="splice")&&r===0&&delete n[0],M(n,this._chain)}}),x(["concat","join","slice"],function(e){var t=r[e];O.prototype[e]=function(){return M(t.apply(this._wrapped,arguments),this._chain)}}),O.prototype.chain=function(){return this._chain=!0,this},O.prototype.value=function(){return this._wrapped}}).call(this);
\ No newline at end of file
diff --git a/static/vendor/underscore-1.3.1.mod.js b/static/vendor/underscore-1.3.1.mod.js
new file mode 100644 (file)
index 0000000..2fb292b
--- /dev/null
@@ -0,0 +1,1004 @@
+require.define('/node_modules/underscore.js', function(require, module, exports, __dirname, __filename, undefined){
+
+//     Underscore.js 1.3.1
+//     (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+//     Underscore is freely distributable under the MIT license.
+//     Portions of Underscore are inspired or borrowed from Prototype,
+//     Oliver Steele's Functional, and John Resig's Micro-Templating.
+//     For all details and documentation:
+//     http://documentcloud.github.com/underscore
+
+(function() {
+
+  // Baseline setup
+  // --------------
+
+  // Establish the root object, `window` in the browser, or `global` on the server.
+  var root = this;
+
+  // Save the previous value of the `_` variable.
+  var previousUnderscore = root._;
+
+  // Establish the object that gets returned to break out of a loop iteration.
+  var breaker = {};
+
+  // Save bytes in the minified (but not gzipped) version:
+  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
+
+  // Create quick reference variables for speed access to core prototypes.
+  var slice            = ArrayProto.slice,
+      unshift          = ArrayProto.unshift,
+      toString         = ObjProto.toString,
+      hasOwnProperty   = ObjProto.hasOwnProperty;
+
+  // All **ECMAScript 5** native function implementations that we hope to use
+  // are declared here.
+  var
+    nativeForEach      = ArrayProto.forEach,
+    nativeMap          = ArrayProto.map,
+    nativeReduce       = ArrayProto.reduce,
+    nativeReduceRight  = ArrayProto.reduceRight,
+    nativeFilter       = ArrayProto.filter,
+    nativeEvery        = ArrayProto.every,
+    nativeSome         = ArrayProto.some,
+    nativeIndexOf      = ArrayProto.indexOf,
+    nativeLastIndexOf  = ArrayProto.lastIndexOf,
+    nativeIsArray      = Array.isArray,
+    nativeKeys         = Object.keys,
+    nativeBind         = FuncProto.bind;
+
+  // Create a safe reference to the Underscore object for use below.
+  var _ = function(obj) { return new wrapper(obj); };
+
+  // Export the Underscore object for **Node.js**, with
+  // backwards-compatibility for the old `require()` API. If we're in
+  // the browser, add `_` as a global object via a string identifier,
+  // for Closure Compiler "advanced" mode.
+  if (typeof exports !== 'undefined') {
+    if (typeof module !== 'undefined' && module.exports) {
+      exports = module.exports = _;
+    }
+    exports._ = _;
+  } else {
+    root['_'] = _;
+  }
+
+  // Current version.
+  _.VERSION = '1.3.1';
+
+  // Collection Functions
+  // --------------------
+
+  // The cornerstone, an `each` implementation, aka `forEach`.
+  // Handles objects with the built-in `forEach`, arrays, and raw objects.
+  // Delegates to **ECMAScript 5**'s native `forEach` if available.
+  var each = _.each = _.forEach = function(obj, iterator, context) {
+    if (obj == null) return;
+    if (nativeForEach && obj.forEach === nativeForEach) {
+      obj.forEach(iterator, context);
+    } else if (obj.length === +obj.length) {
+      for (var i = 0, l = obj.length; i < l; i++) {
+        if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
+      }
+    } else {
+      for (var key in obj) {
+        if (_.has(obj, key)) {
+          if (iterator.call(context, obj[key], key, obj) === breaker) return;
+        }
+      }
+    }
+  };
+
+  // Return the results of applying the iterator to each element.
+  // Delegates to **ECMAScript 5**'s native `map` if available.
+  _.map = _.collect = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+    each(obj, function(value, index, list) {
+      results[results.length] = iterator.call(context, value, index, list);
+    });
+    if (obj.length === +obj.length) results.length = obj.length;
+    return results;
+  };
+
+  // **Reduce** builds up a single result from a list of values, aka `inject`,
+  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
+  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+    var initial = arguments.length > 2;
+    if (obj == null) obj = [];
+    if (nativeReduce && obj.reduce === nativeReduce) {
+      if (context) iterator = _.bind(iterator, context);
+      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+    }
+    each(obj, function(value, index, list) {
+      if (!initial) {
+        memo = value;
+        initial = true;
+      } else {
+        memo = iterator.call(context, memo, value, index, list);
+      }
+    });
+    if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+    return memo;
+  };
+
+  // The right-associative version of reduce, also known as `foldr`.
+  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
+  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+    var initial = arguments.length > 2;
+    if (obj == null) obj = [];
+    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+      if (context) iterator = _.bind(iterator, context);
+      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+    }
+    var reversed = _.toArray(obj).reverse();
+    if (context && !initial) iterator = _.bind(iterator, context);
+    return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
+  };
+
+  // Return the first value which passes a truth test. Aliased as `detect`.
+  _.find = _.detect = function(obj, iterator, context) {
+    var result;
+    any(obj, function(value, index, list) {
+      if (iterator.call(context, value, index, list)) {
+        result = value;
+        return true;
+      }
+    });
+    return result;
+  };
+
+  // Return all the elements that pass a truth test.
+  // Delegates to **ECMAScript 5**'s native `filter` if available.
+  // Aliased as `select`.
+  _.filter = _.select = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+    each(obj, function(value, index, list) {
+      if (iterator.call(context, value, index, list)) results[results.length] = value;
+    });
+    return results;
+  };
+
+  // Return all the elements for which a truth test fails.
+  _.reject = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    each(obj, function(value, index, list) {
+      if (!iterator.call(context, value, index, list)) results[results.length] = value;
+    });
+    return results;
+  };
+
+  // Determine whether all of the elements match a truth test.
+  // Delegates to **ECMAScript 5**'s native `every` if available.
+  // Aliased as `all`.
+  _.every = _.all = function(obj, iterator, context) {
+    var result = true;
+    if (obj == null) return result;
+    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+    each(obj, function(value, index, list) {
+      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+    });
+    return result;
+  };
+
+  // Determine if at least one element in the object matches a truth test.
+  // Delegates to **ECMAScript 5**'s native `some` if available.
+  // Aliased as `any`.
+  var any = _.some = _.any = function(obj, iterator, context) {
+    iterator || (iterator = _.identity);
+    var result = false;
+    if (obj == null) return result;
+    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+    each(obj, function(value, index, list) {
+      if (result || (result = iterator.call(context, value, index, list))) return breaker;
+    });
+    return !!result;
+  };
+
+  // Determine if a given value is included in the array or object using `===`.
+  // Aliased as `contains`.
+  _.include = _.contains = function(obj, target) {
+    var found = false;
+    if (obj == null) return found;
+    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+    found = any(obj, function(value) {
+      return value === target;
+    });
+    return found;
+  };
+
+  // Invoke a method (with arguments) on every item in a collection.
+  _.invoke = function(obj, method) {
+    var args = slice.call(arguments, 2);
+    return _.map(obj, function(value) {
+      return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
+    });
+  };
+
+  // Convenience version of a common use case of `map`: fetching a property.
+  _.pluck = function(obj, key) {
+    return _.map(obj, function(value){ return value[key]; });
+  };
+
+  // Return the maximum element or (element-based computation).
+  _.max = function(obj, iterator, context) {
+    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
+    if (!iterator && _.isEmpty(obj)) return -Infinity;
+    var result = {computed : -Infinity};
+    each(obj, function(value, index, list) {
+      var computed = iterator ? iterator.call(context, value, index, list) : value;
+      computed >= result.computed && (result = {value : value, computed : computed});
+    });
+    return result.value;
+  };
+
+  // Return the minimum element (or element-based computation).
+  _.min = function(obj, iterator, context) {
+    if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
+    if (!iterator && _.isEmpty(obj)) return Infinity;
+    var result = {computed : Infinity};
+    each(obj, function(value, index, list) {
+      var computed = iterator ? iterator.call(context, value, index, list) : value;
+      computed < result.computed && (result = {value : value, computed : computed});
+    });
+    return result.value;
+  };
+
+  // Shuffle an array.
+  _.shuffle = function(obj) {
+    var shuffled = [], rand;
+    each(obj, function(value, index, list) {
+      if (index == 0) {
+        shuffled[0] = value;
+      } else {
+        rand = Math.floor(Math.random() * (index + 1));
+        shuffled[index] = shuffled[rand];
+        shuffled[rand] = value;
+      }
+    });
+    return shuffled;
+  };
+
+  // Sort the object's values by a criterion produced by an iterator.
+  _.sortBy = function(obj, iterator, context) {
+    return _.pluck(_.map(obj, function(value, index, list) {
+      return {
+        value : value,
+        criteria : iterator.call(context, value, index, list)
+      };
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }), 'value');
+  };
+
+  // Groups the object's values by a criterion. Pass either a string attribute
+  // to group by, or a function that returns the criterion.
+  _.groupBy = function(obj, val) {
+    var result = {};
+    var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
+    each(obj, function(value, index) {
+      var key = iterator(value, index);
+      (result[key] || (result[key] = [])).push(value);
+    });
+    return result;
+  };
+
+  // Use a comparator function to figure out at what index an object should
+  // be inserted so as to maintain order. Uses binary search.
+  _.sortedIndex = function(array, obj, iterator) {
+    iterator || (iterator = _.identity);
+    var low = 0, high = array.length;
+    while (low < high) {
+      var mid = (low + high) >> 1;
+      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
+    }
+    return low;
+  };
+
+  // Safely convert anything iterable into a real, live array.
+  _.toArray = function(iterable) {
+    if (!iterable)                return [];
+    if (iterable.toArray)         return iterable.toArray();
+    if (_.isArray(iterable))      return slice.call(iterable);
+    if (_.isArguments(iterable))  return slice.call(iterable);
+    return _.values(iterable);
+  };
+
+  // Return the number of elements in an object.
+  _.size = function(obj) {
+    return _.toArray(obj).length;
+  };
+
+  // Array Functions
+  // ---------------
+
+  // Get the first element of an array. Passing **n** will return the first N
+  // values in the array. Aliased as `head`. The **guard** check allows it to work
+  // with `_.map`.
+  _.first = _.head = function(array, n, guard) {
+    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+  };
+
+  // Returns everything but the last entry of the array. Especcialy useful on
+  // the arguments object. Passing **n** will return all the values in
+  // the array, excluding the last N. The **guard** check allows it to work with
+  // `_.map`.
+  _.initial = function(array, n, guard) {
+    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+  };
+
+  // Get the last element of an array. Passing **n** will return the last N
+  // values in the array. The **guard** check allows it to work with `_.map`.
+  _.last = function(array, n, guard) {
+    if ((n != null) && !guard) {
+      return slice.call(array, Math.max(array.length - n, 0));
+    } else {
+      return array[array.length - 1];
+    }
+  };
+
+  // Returns everything but the first entry of the array. Aliased as `tail`.
+  // Especially useful on the arguments object. Passing an **index** will return
+  // the rest of the values in the array from that index onward. The **guard**
+  // check allows it to work with `_.map`.
+  _.rest = _.tail = function(array, index, guard) {
+    return slice.call(array, (index == null) || guard ? 1 : index);
+  };
+
+  // Trim out all falsy values from an array.
+  _.compact = function(array) {
+    return _.filter(array, function(value){ return !!value; });
+  };
+
+  // Return a completely flattened version of an array.
+  _.flatten = function(array, shallow) {
+    return _.reduce(array, function(memo, value) {
+      if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
+      memo[memo.length] = value;
+      return memo;
+    }, []);
+  };
+
+  // Return a version of the array that does not contain the specified value(s).
+  _.without = function(array) {
+    return _.difference(array, slice.call(arguments, 1));
+  };
+
+  // Produce a duplicate-free version of the array. If the array has already
+  // been sorted, you have the option of using a faster algorithm.
+  // Aliased as `unique`.
+  _.uniq = _.unique = function(array, isSorted, iterator) {
+    var initial = iterator ? _.map(array, iterator) : array;
+    var result = [];
+    _.reduce(initial, function(memo, el, i) {
+      if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
+        memo[memo.length] = el;
+        result[result.length] = array[i];
+      }
+      return memo;
+    }, []);
+    return result;
+  };
+
+  // Produce an array that contains the union: each distinct element from all of
+  // the passed-in arrays.
+  _.union = function() {
+    return _.uniq(_.flatten(arguments, true));
+  };
+
+  // Produce an array that contains every item shared between all the
+  // passed-in arrays. (Aliased as "intersect" for back-compat.)
+  _.intersection = _.intersect = function(array) {
+    var rest = slice.call(arguments, 1);
+    return _.filter(_.uniq(array), function(item) {
+      return _.every(rest, function(other) {
+        return _.indexOf(other, item) >= 0;
+      });
+    });
+  };
+
+  // Take the difference between one array and a number of other arrays.
+  // Only the elements present in just the first array will remain.
+  _.difference = function(array) {
+    var rest = _.flatten(slice.call(arguments, 1));
+    return _.filter(array, function(value){ return !_.include(rest, value); });
+  };
+
+  // Zip together multiple lists into a single array -- elements that share
+  // an index go together.
+  _.zip = function() {
+    var args = slice.call(arguments);
+    var length = _.max(_.pluck(args, 'length'));
+    var results = new Array(length);
+    for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
+    return results;
+  };
+
+  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+  // we need this function. Return the position of the first occurrence of an
+  // item in an array, or -1 if the item is not included in the array.
+  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+  // If the array is large and already in sort order, pass `true`
+  // for **isSorted** to use binary search.
+  _.indexOf = function(array, item, isSorted) {
+    if (array == null) return -1;
+    var i, l;
+    if (isSorted) {
+      i = _.sortedIndex(array, item);
+      return array[i] === item ? i : -1;
+    }
+    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
+    for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
+    return -1;
+  };
+
+  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
+  _.lastIndexOf = function(array, item) {
+    if (array == null) return -1;
+    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
+    var i = array.length;
+    while (i--) if (i in array && array[i] === item) return i;
+    return -1;
+  };
+
+  // Generate an integer Array containing an arithmetic progression. A port of
+  // the native Python `range()` function. See
+  // [the Python documentation](http://docs.python.org/library/functions.html#range).
+  _.range = function(start, stop, step) {
+    if (arguments.length <= 1) {
+      stop = start || 0;
+      start = 0;
+    }
+    step = arguments[2] || 1;
+
+    var len = Math.max(Math.ceil((stop - start) / step), 0);
+    var idx = 0;
+    var range = new Array(len);
+
+    while(idx < len) {
+      range[idx++] = start;
+      start += step;
+    }
+
+    return range;
+  };
+
+  // Function (ahem) Functions
+  // ------------------
+
+  // Reusable constructor function for prototype setting.
+  var ctor = function(){};
+
+  // Create a function bound to a given object (assigning `this`, and arguments,
+  // optionally). Binding with arguments is also known as `curry`.
+  // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
+  // We check for `func.bind` first, to fail fast when `func` is undefined.
+  _.bind = function bind(func, context) {
+    var bound, args;
+    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+    if (!_.isFunction(func)) throw new TypeError;
+    args = slice.call(arguments, 2);
+    return bound = function() {
+      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
+      ctor.prototype = func.prototype;
+      var self = new ctor;
+      var result = func.apply(self, args.concat(slice.call(arguments)));
+      if (Object(result) === result) return result;
+      return self;
+    };
+  };
+
+  // Bind all of an object's methods to that object. Useful for ensuring that
+  // all callbacks defined on an object belong to it.
+  _.bindAll = function(obj) {
+    var funcs = slice.call(arguments, 1);
+    if (funcs.length == 0) funcs = _.functions(obj);
+    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+    return obj;
+  };
+
+  // Memoize an expensive function by storing its results.
+  _.memoize = function(func, hasher) {
+    var memo = {};
+    hasher || (hasher = _.identity);
+    return function() {
+      var key = hasher.apply(this, arguments);
+      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+    };
+  };
+
+  // Delays a function for the given number of milliseconds, and then calls
+  // it with the arguments supplied.
+  _.delay = function(func, wait) {
+    var args = slice.call(arguments, 2);
+    return setTimeout(function(){ return func.apply(func, args); }, wait);
+  };
+
+  // Defers a function, scheduling it to run after the current call stack has
+  // cleared.
+  _.defer = function(func) {
+    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+  };
+
+  // Returns a function, that, when invoked, will only be triggered at most once
+  // during a given window of time.
+  _.throttle = function(func, wait) {
+    var context, args, timeout, throttling, more;
+    var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
+    return function() {
+      context = this; args = arguments;
+      var later = function() {
+        timeout = null;
+        if (more) func.apply(context, args);
+        whenDone();
+      };
+      if (!timeout) timeout = setTimeout(later, wait);
+      if (throttling) {
+        more = true;
+      } else {
+        func.apply(context, args);
+      }
+      whenDone();
+      throttling = true;
+    };
+  };
+
+  // Returns a function, that, as long as it continues to be invoked, will not
+  // be triggered. The function will be called after it stops being called for
+  // N milliseconds.
+  _.debounce = function(func, wait) {
+    var timeout;
+    return function() {
+      var context = this, args = arguments;
+      var later = function() {
+        timeout = null;
+        func.apply(context, args);
+      };
+      clearTimeout(timeout);
+      timeout = setTimeout(later, wait);
+    };
+  };
+
+  // Returns a function that will be executed at most one time, no matter how
+  // often you call it. Useful for lazy initialization.
+  _.once = function(func) {
+    var ran = false, memo;
+    return function() {
+      if (ran) return memo;
+      ran = true;
+      return memo = func.apply(this, arguments);
+    };
+  };
+
+  // Returns the first function passed as an argument to the second,
+  // allowing you to adjust arguments, run code before and after, and
+  // conditionally execute the original function.
+  _.wrap = function(func, wrapper) {
+    return function() {
+      var args = [func].concat(slice.call(arguments, 0));
+      return wrapper.apply(this, args);
+    };
+  };
+
+  // Returns a function that is the composition of a list of functions, each
+  // consuming the return value of the function that follows.
+  _.compose = function() {
+    var funcs = arguments;
+    return function() {
+      var args = arguments;
+      for (var i = funcs.length - 1; i >= 0; i--) {
+        args = [funcs[i].apply(this, args)];
+      }
+      return args[0];
+    };
+  };
+
+  // Returns a function that will only be executed after being called N times.
+  _.after = function(times, func) {
+    if (times <= 0) return func();
+    return function() {
+      if (--times < 1) { return func.apply(this, arguments); }
+    };
+  };
+
+  // Object Functions
+  // ----------------
+
+  // Retrieve the names of an object's properties.
+  // Delegates to **ECMAScript 5**'s native `Object.keys`
+  _.keys = nativeKeys || function(obj) {
+    if (obj !== Object(obj)) throw new TypeError('Invalid object');
+    var keys = [];
+    for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+    return keys;
+  };
+
+  // Retrieve the values of an object's properties.
+  _.values = function(obj) {
+    return _.map(obj, _.identity);
+  };
+
+  // Return a sorted list of the function names available on the object.
+  // Aliased as `methods`
+  _.functions = _.methods = function(obj) {
+    var names = [];
+    for (var key in obj) {
+      if (_.isFunction(obj[key])) names.push(key);
+    }
+    return names.sort();
+  };
+
+  // Extend a given object with all the properties in passed-in object(s).
+  _.extend = function(obj) {
+    each(slice.call(arguments, 1), function(source) {
+      for (var prop in source) {
+        obj[prop] = source[prop];
+      }
+    });
+    return obj;
+  };
+
+  // Fill in a given object with default properties.
+  _.defaults = function(obj) {
+    each(slice.call(arguments, 1), function(source) {
+      for (var prop in source) {
+        if (obj[prop] == null) obj[prop] = source[prop];
+      }
+    });
+    return obj;
+  };
+
+  // Create a (shallow-cloned) duplicate of an object.
+  _.clone = function(obj) {
+    if (!_.isObject(obj)) return obj;
+    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+  };
+
+  // Invokes interceptor with the obj, and then returns obj.
+  // The primary purpose of this method is to "tap into" a method chain, in
+  // order to perform operations on intermediate results within the chain.
+  _.tap = function(obj, interceptor) {
+    interceptor(obj);
+    return obj;
+  };
+
+  // Internal recursive comparison function.
+  function eq(a, b, stack) {
+    // Identical objects are equal. `0 === -0`, but they aren't identical.
+    // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+    if (a === b) return a !== 0 || 1 / a == 1 / b;
+    // A strict comparison is necessary because `null == undefined`.
+    if (a == null || b == null) return a === b;
+    // Unwrap any wrapped objects.
+    if (a._chain) a = a._wrapped;
+    if (b._chain) b = b._wrapped;
+    // Invoke a custom `isEqual` method if one is provided.
+    if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
+    if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
+    // Compare `[[Class]]` names.
+    var className = toString.call(a);
+    if (className != toString.call(b)) return false;
+    switch (className) {
+      // Strings, numbers, dates, and booleans are compared by value.
+      case '[object String]':
+        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+        // equivalent to `new String("5")`.
+        return a == String(b);
+      case '[object Number]':
+        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+        // other numeric values.
+        return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+      case '[object Date]':
+      case '[object Boolean]':
+        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+        // millisecond representations. Note that invalid dates with millisecond representations
+        // of `NaN` are not equivalent.
+        return +a == +b;
+      // RegExps are compared by their source patterns and flags.
+      case '[object RegExp]':
+        return a.source == b.source &&
+               a.global == b.global &&
+               a.multiline == b.multiline &&
+               a.ignoreCase == b.ignoreCase;
+    }
+    if (typeof a != 'object' || typeof b != 'object') return false;
+    // Assume equality for cyclic structures. The algorithm for detecting cyclic
+    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+    var length = stack.length;
+    while (length--) {
+      // Linear search. Performance is inversely proportional to the number of
+      // unique nested structures.
+      if (stack[length] == a) return true;
+    }
+    // Add the first object to the stack of traversed objects.
+    stack.push(a);
+    var size = 0, result = true;
+    // Recursively compare objects and arrays.
+    if (className == '[object Array]') {
+      // Compare array lengths to determine if a deep comparison is necessary.
+      size = a.length;
+      result = size == b.length;
+      if (result) {
+        // Deep compare the contents, ignoring non-numeric properties.
+        while (size--) {
+          // Ensure commutative equality for sparse arrays.
+          if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
+        }
+      }
+    } else {
+      // Objects with different constructors are not equivalent.
+      if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
+      // Deep compare objects.
+      for (var key in a) {
+        if (_.has(a, key)) {
+          // Count the expected number of properties.
+          size++;
+          // Deep compare each member.
+          if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
+        }
+      }
+      // Ensure that both objects contain the same number of properties.
+      if (result) {
+        for (key in b) {
+          if (_.has(b, key) && !(size--)) break;
+        }
+        result = !size;
+      }
+    }
+    // Remove the first object from the stack of traversed objects.
+    stack.pop();
+    return result;
+  }
+
+  // Perform a deep comparison to check if two objects are equal.
+  _.isEqual = function(a, b) {
+    return eq(a, b, []);
+  };
+
+  // Is a given array, string, or object empty?
+  // An "empty" object has no enumerable own-properties.
+  _.isEmpty = function(obj) {
+    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+    for (var key in obj) if (_.has(obj, key)) return false;
+    return true;
+  };
+
+  // Is a given value a DOM element?
+  _.isElement = function(obj) {
+    return !!(obj && obj.nodeType == 1);
+  };
+
+  // Is a given value an array?
+  // Delegates to ECMA5's native Array.isArray
+  _.isArray = nativeIsArray || function(obj) {
+    return toString.call(obj) == '[object Array]';
+  };
+
+  // Is a given variable an object?
+  _.isObject = function(obj) {
+    return obj === Object(obj);
+  };
+
+  // Is a given variable an arguments object?
+  _.isArguments = function(obj) {
+    return toString.call(obj) == '[object Arguments]';
+  };
+  if (!_.isArguments(arguments)) {
+    _.isArguments = function(obj) {
+      return !!(obj && _.has(obj, 'callee'));
+    };
+  }
+
+  // Is a given value a function?
+  _.isFunction = function(obj) {
+    return toString.call(obj) == '[object Function]';
+  };
+
+  // Is a given value a string?
+  _.isString = function(obj) {
+    return toString.call(obj) == '[object String]';
+  };
+
+  // Is a given value a number?
+  _.isNumber = function(obj) {
+    return toString.call(obj) == '[object Number]';
+  };
+
+  // Is the given value `NaN`?
+  _.isNaN = function(obj) {
+    // `NaN` is the only value for which `===` is not reflexive.
+    return obj !== obj;
+  };
+
+  // Is a given value a boolean?
+  _.isBoolean = function(obj) {
+    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+  };
+
+  // Is a given value a date?
+  _.isDate = function(obj) {
+    return toString.call(obj) == '[object Date]';
+  };
+
+  // Is the given value a regular expression?
+  _.isRegExp = function(obj) {
+    return toString.call(obj) == '[object RegExp]';
+  };
+
+  // Is a given value equal to null?
+  _.isNull = function(obj) {
+    return obj === null;
+  };
+
+  // Is a given variable undefined?
+  _.isUndefined = function(obj) {
+    return obj === void 0;
+  };
+
+  // Has own property?
+  _.has = function(obj, key) {
+    return hasOwnProperty.call(obj, key);
+  };
+
+  // Utility Functions
+  // -----------------
+
+  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+  // previous owner. Returns a reference to the Underscore object.
+  _.noConflict = function() {
+    root._ = previousUnderscore;
+    return this;
+  };
+
+  // Keep the identity function around for default iterators.
+  _.identity = function(value) {
+    return value;
+  };
+
+  // Run a function **n** times.
+  _.times = function (n, iterator, context) {
+    for (var i = 0; i < n; i++) iterator.call(context, i);
+  };
+
+  // Escape a string for HTML interpolation.
+  _.escape = function(string) {
+    return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
+  };
+
+  // Add your own custom functions to the Underscore object, ensuring that
+  // they're correctly added to the OOP wrapper as well.
+  _.mixin = function(obj) {
+    each(_.functions(obj), function(name){
+      addToWrapper(name, _[name] = obj[name]);
+    });
+  };
+
+  // Generate a unique integer id (unique within the entire client session).
+  // Useful for temporary DOM ids.
+  var idCounter = 0;
+  _.uniqueId = function(prefix) {
+    var id = idCounter++;
+    return prefix ? prefix + id : id;
+  };
+
+  // By default, Underscore uses ERB-style template delimiters, change the
+  // following template settings to use alternative delimiters.
+  _.templateSettings = {
+    evaluate    : /<%([\s\S]+?)%>/g,
+    interpolate : /<%=([\s\S]+?)%>/g,
+    escape      : /<%-([\s\S]+?)%>/g
+  };
+
+  // When customizing `templateSettings`, if you don't want to define an
+  // interpolation, evaluation or escaping regex, we need one that is
+  // guaranteed not to match.
+  var noMatch = /.^/;
+
+  // Within an interpolation, evaluation, or escaping, remove HTML escaping
+  // that had been previously added.
+  var unescape = function(code) {
+    return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
+  };
+
+  // JavaScript micro-templating, similar to John Resig's implementation.
+  // Underscore templating handles arbitrary delimiters, preserves whitespace,
+  // and correctly escapes quotes within interpolated code.
+  _.template = function(str, data) {
+    var c  = _.templateSettings;
+    var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
+      'with(obj||{}){__p.push(\'' +
+      str.replace(/\\/g, '\\\\')
+         .replace(/'/g, "\\'")
+         .replace(c.escape || noMatch, function(match, code) {
+           return "',_.escape(" + unescape(code) + "),'";
+         })
+         .replace(c.interpolate || noMatch, function(match, code) {
+           return "'," + unescape(code) + ",'";
+         })
+         .replace(c.evaluate || noMatch, function(match, code) {
+           return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
+         })
+         .replace(/\r/g, '\\r')
+         .replace(/\n/g, '\\n')
+         .replace(/\t/g, '\\t')
+         + "');}return __p.join('');";
+    var func = new Function('obj', '_', tmpl);
+    if (data) return func(data, _);
+    return function(data) {
+      return func.call(this, data, _);
+    };
+  };
+
+  // Add a "chain" function, which will delegate to the wrapper.
+  _.chain = function(obj) {
+    return _(obj).chain();
+  };
+
+  // The OOP Wrapper
+  // ---------------
+
+  // If Underscore is called as a function, it returns a wrapped object that
+  // can be used OO-style. This wrapper holds altered versions of all the
+  // underscore functions. Wrapped objects may be chained.
+  var wrapper = function(obj) { this._wrapped = obj; };
+
+  // Expose `wrapper.prototype` as `_.prototype`
+  _.prototype = wrapper.prototype;
+
+  // Helper function to continue chaining intermediate results.
+  var result = function(obj, chain) {
+    return chain ? _(obj).chain() : obj;
+  };
+
+  // A method to easily add functions to the OOP wrapper.
+  var addToWrapper = function(name, func) {
+    wrapper.prototype[name] = function() {
+      var args = slice.call(arguments);
+      unshift.call(args, this._wrapped);
+      return result(func.apply(_, args), this._chain);
+    };
+  };
+
+  // Add all of the Underscore functions to the wrapper object.
+  _.mixin(_);
+
+  // Add all mutator Array functions to the wrapper.
+  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+    var method = ArrayProto[name];
+    wrapper.prototype[name] = function() {
+      var wrapped = this._wrapped;
+      method.apply(wrapped, arguments);
+      var length = wrapped.length;
+      if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
+      return result(wrapped, this._chain);
+    };
+  });
+
+  // Add all accessor Array functions to the wrapper.
+  each(['concat', 'join', 'slice'], function(name) {
+    var method = ArrayProto[name];
+    wrapper.prototype[name] = function() {
+      return result(method.apply(this._wrapped, arguments), this._chain);
+    };
+  });
+
+  // Start chaining a wrapped Underscore object.
+  wrapper.prototype.chain = function() {
+    this._chain = true;
+    return this;
+  };
+
+  // Extracts the result from a wrapped and chained object.
+  wrapper.prototype.value = function() {
+    return this._wrapped;
+  };
+
+}).call(this);
+
+
+});
diff --git a/static/vendor/underscore-1.3.1.mod.min.js b/static/vendor/underscore-1.3.1.mod.min.js
new file mode 100644 (file)
index 0000000..4b32e8d
--- /dev/null
@@ -0,0 +1 @@
+require.define("/node_modules/underscore.js",function(e,t,n,r,i,s){(function(){function L(e,t,n){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e._chain&&(e=e._wrapped),t._chain&&(t=t._wrapped);if(e.isEqual&&T.isFunction(e.isEqual))return e.isEqual(t);if(t.isEqual&&T.isFunction(t.isEqual))return t.isEqual(e);var r=l.call(e);if(r!=l.call(t))return!1;switch(r){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var i=n.length;while(i--)if(n[i]==e)return!0;n.push(e);var s=0,o=!0;if(r=="[object Array]"){s=e.length,o=s==t.length;if(o)while(s--)if(!(o=s in e==s in t&&L(e[s],t[s],n)))break}else{if("constructor"in e!="constructor"in t||e.constructor!=t.constructor)return!1;for(var u in e)if(T.has(e,u)){s++;if(!(o=T.has(t,u)&&L(e[u],t[u],n)))break}if(o){for(u in t)if(T.has(t,u)&&!(s--))break;o=!s}}return n.pop(),o}var e=this,r=e._,i={},s=Array.prototype,o=Object.prototype,u=Function.prototype,a=s.slice,f=s.unshift,l=o.toString,c=o.hasOwnProperty,h=s.forEach,p=s.map,d=s.reduce,v=s.reduceRight,m=s.filter,g=s.every,y=s.some,b=s.indexOf,w=s.lastIndexOf,E=Array.isArray,S=Object.keys,x=u.bind,T=function(e){return new _(e)};typeof n!="undefined"?(typeof t!="undefined"&&t.exports&&(n=t.exports=T),n._=T):e._=T,T.VERSION="1.3.1";var N=T.each=T.forEach=function(e,t,n){if(e==null)return;if(h&&e.forEach===h)e.forEach(t,n);else if(e.length===+e.length){for(var r=0,s=e.length;r<s;r++)if(r in e&&t.call(n,e[r],r,e)===i)return}else for(var o in e)if(T.has(e,o)&&t.call(n,e[o],o,e)===i)return};T.map=T.collect=function(e,t,n){var r=[];return e==null?r:p&&e.map===p?e.map(t,n):(N(e,function(e,i,s){r[r.length]=t.call(n,e,i,s)}),e.length===+e.length&&(r.length=e.length),r)},T.reduce=T.foldl=T.inject=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(d&&e.reduce===d)return r&&(t=T.bind(t,r)),i?e.reduce(t,n):e.reduce(t);N(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError("Reduce of empty array with no initial value");return n},T.reduceRight=T.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(v&&e.reduceRight===v)return r&&(t=T.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=T.toArray(e).reverse();return r&&!i&&(t=T.bind(t,r)),i?T.reduce(s,t,n,r):T.reduce(s,t)},T.find=T.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},T.filter=T.select=function(e,t,n){var r=[];return e==null?r:m&&e.filter===m?e.filter(t,n):(N(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},T.reject=function(e,t,n){var r=[];return e==null?r:(N(e,function(e,i,s){t.call(n,e,i,s)||(r[r.length]=e)}),r)},T.every=T.all=function(e,t,n){var r=!0;return e==null?r:g&&e.every===g?e.every(t,n):(N(e,function(e,s,o){if(!(r=r&&t.call(n,e,s,o)))return i}),r)};var C=T.some=T.any=function(e,t,n){t||(t=T.identity);var r=!1;return e==null?r:y&&e.some===y?e.some(t,n):(N(e,function(e,s,o){if(r||(r=t.call(n,e,s,o)))return i}),!!r)};T.include=T.contains=function(e,t){var n=!1;return e==null?n:b&&e.indexOf===b?e.indexOf(t)!=-1:(n=C(e,function(e){return e===t}),n)},T.invoke=function(e,t){var n=a.call(arguments,2);return T.map(e,function(e){return(T.isFunction(t)?t||e:e[t]).apply(e,n)})},T.pluck=function(e,t){return T.map(e,function(e){return e[t]})},T.max=function(e,t,n){if(!t&&T.isArray(e))return Math.max.apply(Math,e);if(!t&&T.isEmpty(e))return-Infinity;var r={computed:-Infinity};return N(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},T.min=function(e,t,n){if(!t&&T.isArray(e))return Math.min.apply(Math,e);if(!t&&T.isEmpty(e))return Infinity;var r={computed:Infinity};return N(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o<r.computed&&(r={value:e,computed:o})}),r.value},T.shuffle=function(e){var t=[],n;return N(e,function(e,r,i){r==0?t[0]=e:(n=Math.floor(Math.random()*(r+1)),t[r]=t[n],t[n]=e)}),t},T.sortBy=function(e,t,n){return T.pluck(T.map(e,function(e,r,i){return{value:e,criteria:t.call(n,e,r,i)}}).sort(function(e,t){var n=e.criteria,r=t.criteria;return n<r?-1:n>r?1:0}),"value")},T.groupBy=function(e,t){var n={},r=T.isFunction(t)?t:function(e){return e[t]};return N(e,function(e,t){var i=r(e,t);(n[i]||(n[i]=[])).push(e)}),n},T.sortedIndex=function(e,t,n){n||(n=T.identity);var r=0,i=e.length;while(r<i){var s=r+i>>1;n(e[s])<n(t)?r=s+1:i=s}return r},T.toArray=function(e){return e?e.toArray?e.toArray():T.isArray(e)?a.call(e):T.isArguments(e)?a.call(e):T.values(e):[]},T.size=function(e){return T.toArray(e).length},T.first=T.head=function(e,t,n){return t!=null&&!n?a.call(e,0,t):e[0]},T.initial=function(e,t,n){return a.call(e,0,e.length-(t==null||n?1:t))},T.last=function(e,t,n){return t!=null&&!n?a.call(e,Math.max(e.length-t,0)):e[e.length-1]},T.rest=T.tail=function(e,t,n){return a.call(e,t==null||n?1:t)},T.compact=function(e){return T.filter(e,function(e){return!!e})},T.flatten=function(e,t){return T.reduce(e,function(e,n){return T.isArray(n)?e.concat(t?n:T.flatten(n)):(e[e.length]=n,e)},[])},T.without=function(e){return T.difference(e,a.call(arguments,1))},T.uniq=T.unique=function(e,t,n){var r=n?T.map(e,n):e,i=[];return T.reduce(r,function(n,r,s){if(0==s||(t===!0?T.last(n)!=r:!T.include(n,r)))n[n.length]=r,i[i.length]=e[s];return n},[]),i},T.union=function(){return T.uniq(T.flatten(arguments,!0))},T.intersection=T.intersect=function(e){var t=a.call(arguments,1);return T.filter(T.uniq(e),function(e){return T.every(t,function(t){return T.indexOf(t,e)>=0})})},T.difference=function(e){var t=T.flatten(a.call(arguments,1));return T.filter(e,function(e){return!T.include(t,e)})},T.zip=function(){var e=a.call(arguments),t=T.max(T.pluck(e,"length")),n=new Array(t);for(var r=0;r<t;r++)n[r]=T.pluck(e,""+r);return n},T.indexOf=function(e,t,n){if(e==null)return-1;var r,i;if(n)return r=T.sortedIndex(e,t),e[r]===t?r:-1;if(b&&e.indexOf===b)return e.indexOf(t);for(r=0,i=e.length;r<i;r++)if(r in e&&e[r]===t)return r;return-1},T.lastIndexOf=function(e,t){if(e==null)return-1;if(w&&e.lastIndexOf===w)return e.lastIndexOf(t);var n=e.length;while(n--)if(n in e&&e[n]===t)return n;return-1},T.range=function(e,t,n){arguments.length<=1&&(t=e||0,e=0),n=arguments[2]||1;var r=Math.max(Math.ceil((t-e)/n),0),i=0,s=new Array(r);while(i<r)s[i++]=e,e+=n;return s};var k=function(){};T.bind=function(t,n){var r,i;if(t.bind===x&&x)return x.apply(t,a.call(arguments,1));if(!T.isFunction(t))throw new TypeError;return i=a.call(arguments,2),r=function(){if(this instanceof r){k.prototype=t.prototype;var e=new k,s=t.apply(e,i.concat(a.call(arguments)));return Object(s)===s?s:e}return t.apply(n,i.concat(a.call(arguments)))}},T.bindAll=function(e){var t=a.call(arguments,1);return t.length==0&&(t=T.functions(e)),N(t,function(t){e[t]=T.bind(e[t],e)}),e},T.memoize=function(e,t){var n={};return t||(t=T.identity),function(){var r=t.apply(this,arguments);return T.has(n,r)?n[r]:n[r]=e.apply(this,arguments)}},T.delay=function(e,t){var n=a.call(arguments,2);return setTimeout(function(){return e.apply(e,n)},t)},T.defer=function(e){return T.delay.apply(T,[e,1].concat(a.call(arguments,1)))},T.throttle=function(e,t){var n,r,i,s,o,u=T.debounce(function(){o=s=!1},t);return function(){n=this,r=arguments;var a=function(){i=null,o&&e.apply(n,r),u()};i||(i=setTimeout(a,t)),s?o=!0:e.apply(n,r),u(),s=!0}},T.debounce=function(e,t){var n;return function(){var r=this,i=arguments,s=function(){n=null,e.apply(r,i)};clearTimeout(n),n=setTimeout(s,t)}},T.once=function(e){var t=!1,n;return function(){return t?n:(t=!0,n=e.apply(this,arguments))}},T.wrap=function(e,t){return function(){var n=[e].concat(a.call(arguments,0));return t.apply(this,n)}},T.compose=function(){var e=arguments;return function(){var t=arguments;for(var n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},T.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},T.keys=S||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)T.has(e,n)&&(t[t.length]=n);return t},T.values=function(e){return T.map(e,T.identity)},T.functions=T.methods=function(e){var t=[];for(var n in e)T.isFunction(e[n])&&t.push(n);return t.sort()},T.extend=function(e){return N(a.call(arguments,1),function(t){for(var n in t)e[n]=t[n]}),e},T.defaults=function(e){return N(a.call(arguments,1),function(t){for(var n in t)e[n]==null&&(e[n]=t[n])}),e},T.clone=function(e){return T.isObject(e)?T.isArray(e)?e.slice():T.extend({},e):e},T.tap=function(e,t){return t(e),e},T.isEqual=function(e,t){return L(e,t,[])},T.isEmpty=function(e){if(T.isArray(e)||T.isString(e))return e.length===0;for(var t in e)if(T.has(e,t))return!1;return!0},T.isElement=function(e){return!!e&&e.nodeType==1},T.isArray=E||function(e){return l.call(e)=="[object Array]"},T.isObject=function(e){return e===Object(e)},T.isArguments=function(e){return l.call(e)=="[object Arguments]"},T.isArguments(arguments)||(T.isArguments=function(e){return!!e&&!!T.has(e,"callee")}),T.isFunction=function(e){return l.call(e)=="[object Function]"},T.isString=function(e){return l.call(e)=="[object String]"},T.isNumber=function(e){return l.call(e)=="[object Number]"},T.isNaN=function(e){return e!==e},T.isBoolean=function(e){return e===!0||e===!1||l.call(e)=="[object Boolean]"},T.isDate=function(e){return l.call(e)=="[object Date]"},T.isRegExp=function(e){return l.call(e)=="[object RegExp]"},T.isNull=function(e){return e===null},T.isUndefined=function(e){return e===void 0},T.has=function(e,t){return c.call(e,t)},T.noConflict=function(){return e._=r,this},T.identity=function(e){return e},T.times=function(e,t,n){for(var r=0;r<e;r++)t.call(n,r)},T.escape=function(e){return(""+e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")},T.mixin=function(e){N(T.functions(e),function(t){P(t,T[t]=e[t])})};var A=0;T.uniqueId=function(e){var t=A++;return e?e+t:t},T.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var O=/.^/,M=function(e){return e.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};T.template=function(e,t){var n=T.templateSettings,r="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(n.escape||O,function(e,t){return"',_.escape("+M(t)+"),'"}).replace(n.interpolate||O,function(e,t){return"',"+M(t)+",'"}).replace(n.evaluate||O,function(e,t){return"');"+M(t).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",i=new Function("obj","_",r);return t?i(t,T):function(e){return i.call(this,e,T)}},T.chain=function(e){return T(e).chain()};var _=function(e){this._wrapped=e};T.prototype=_.prototype;var D=function(e,t){return t?T(e).chain():e},P=function(e,t){_.prototype[e]=function(){var e=a.call(arguments);return f.call(e,this._wrapped),D(t.apply(T,e),this._chain)}};T.mixin(T),N(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=s[e];_.prototype[e]=function(){var n=this._wrapped;t.apply(n,arguments);var r=n.length;return(e=="shift"||e=="splice")&&r===0&&delete n[0],D(n,this._chain)}}),N(["concat","join","slice"],function(e){var t=s[e];_.prototype[e]=function(){return D(t.apply(this._wrapped,arguments),this._chain)}}),_.prototype.chain=function(){return this._chain=!0,this},_.prototype.value=function(){return this._wrapped}}).call(this)});
\ No newline at end of file
diff --git a/static/vendor/underscore.mod.js b/static/vendor/underscore.mod.js
new file mode 120000 (symlink)
index 0000000..88cd20c
--- /dev/null
@@ -0,0 +1 @@
+underscore-1.3.1.mod.js
\ No newline at end of file
diff --git a/static/vendor/underscore.mod.min.js b/static/vendor/underscore.mod.min.js
new file mode 120000 (symlink)
index 0000000..d9658c9
--- /dev/null
@@ -0,0 +1 @@
+underscore-1.3.1.mod.min.js
\ No newline at end of file
diff --git a/static/vendor/underscore.string-2.0.0.mod.js b/static/vendor/underscore.string-2.0.0.mod.js
new file mode 100644 (file)
index 0000000..8ca68bc
--- /dev/null
@@ -0,0 +1,485 @@
+require.define('/node_modules/underscore.string.js', function(require, module, exports, __dirname, __filename, undefined){
+
+// Underscore.string
+// (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
+// Underscore.strings is freely distributable under the terms of the MIT license.
+// Documentation: https://github.com/epeli/underscore.string
+// Some code is borrowed from MooTools and Alexandru Marasteanu.
+
+// Version 2.0.0
+
+(function(root){
+  'use strict';
+
+  // Defining helper functions.
+
+  var nativeTrim = String.prototype.trim;
+
+  var parseNumber = function(source) { return source * 1 || 0; };
+
+  var strRepeat = function(i, m) {
+    for (var o = []; m > 0; o[--m] = i) {}
+    return o.join('');
+  };
+
+  var slice = function(a){
+    return Array.prototype.slice.call(a);
+  };
+
+  var defaultToWhiteSpace = function(characters){
+    if (characters) {
+      return _s.escapeRegExp(characters);
+    }
+    return '\\s';
+  };
+
+  var sArgs = function(method){
+    return function(){
+      var args = slice(arguments);
+      for(var i=0; i<args.length; i++)
+        args[i] = args[i] == null ? '' : '' + args[i];
+      return method.apply(null, args);
+    };
+  };
+
+  // sprintf() for JavaScript 0.7-beta1
+  // http://www.diveintojavascript.com/projects/javascript-sprintf
+  //
+  // Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
+  // All rights reserved.
+
+  var sprintf = (function() {
+    function get_type(variable) {
+      return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
+    }
+
+    var str_repeat = strRepeat;
+
+    var str_format = function() {
+      if (!str_format.cache.hasOwnProperty(arguments[0])) {
+        str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
+      }
+      return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
+    };
+
+    str_format.format = function(parse_tree, argv) {
+      var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
+      for (i = 0; i < tree_length; i++) {
+        node_type = get_type(parse_tree[i]);
+        if (node_type === 'string') {
+          output.push(parse_tree[i]);
+        }
+        else if (node_type === 'array') {
+          match = parse_tree[i]; // convenience purposes only
+          if (match[2]) { // keyword argument
+            arg = argv[cursor];
+            for (k = 0; k < match[2].length; k++) {
+              if (!arg.hasOwnProperty(match[2][k])) {
+                throw(sprintf('[_.sprintf] property "%s" does not exist', match[2][k]));
+              }
+              arg = arg[match[2][k]];
+            }
+          } else if (match[1]) { // positional argument (explicit)
+            arg = argv[match[1]];
+          }
+          else { // positional argument (implicit)
+            arg = argv[cursor++];
+          }
+
+          if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
+            throw(sprintf('[_.sprintf] expecting number but found %s', get_type(arg)));
+          }
+          switch (match[8]) {
+            case 'b': arg = arg.toString(2); break;
+            case 'c': arg = String.fromCharCode(arg); break;
+            case 'd': arg = parseInt(arg, 10); break;
+            case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
+            case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
+            case 'o': arg = arg.toString(8); break;
+            case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
+            case 'u': arg = Math.abs(arg); break;
+            case 'x': arg = arg.toString(16); break;
+            case 'X': arg = arg.toString(16).toUpperCase(); break;
+          }
+          arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
+          pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
+          pad_length = match[6] - String(arg).length;
+          pad = match[6] ? str_repeat(pad_character, pad_length) : '';
+          output.push(match[5] ? arg + pad : pad + arg);
+        }
+      }
+      return output.join('');
+    };
+
+    str_format.cache = {};
+
+    str_format.parse = function(fmt) {
+      var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
+      while (_fmt) {
+        if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
+          parse_tree.push(match[0]);
+        }
+        else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
+          parse_tree.push('%');
+        }
+        else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
+          if (match[2]) {
+            arg_names |= 1;
+            var field_list = [], replacement_field = match[2], field_match = [];
+            if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
+              field_list.push(field_match[1]);
+              while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
+                if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
+                  field_list.push(field_match[1]);
+                }
+                else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
+                  field_list.push(field_match[1]);
+                }
+                else {
+                  throw('[_.sprintf] huh?');
+                }
+              }
+            }
+            else {
+              throw('[_.sprintf] huh?');
+            }
+            match[2] = field_list;
+          }
+          else {
+            arg_names |= 2;
+          }
+          if (arg_names === 3) {
+            throw('[_.sprintf] mixing positional and named placeholders is not (yet) supported');
+          }
+          parse_tree.push(match);
+        }
+        else {
+          throw('[_.sprintf] huh?');
+        }
+        _fmt = _fmt.substring(match[0].length);
+      }
+      return parse_tree;
+    };
+
+    return str_format;
+  })();
+
+
+
+  // Defining underscore.string
+
+  var _s = {
+              
+    VERSION: '2.0.0',
+
+    isBlank: sArgs(function(str){
+      return (/^\s*$/).test(str);
+    }),
+
+    stripTags: sArgs(function(str){
+      return str.replace(/<\/?[^>]+>/ig, '');
+    }),
+
+    capitalize : sArgs(function(str) {
+      return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
+    }),
+
+    chop: sArgs(function(str, step){
+      step = parseNumber(step) || str.length;
+      var arr = [];
+      for (var i = 0; i < str.length;) {
+        arr.push(str.slice(i,i + step));
+        i = i + step;
+      }
+      return arr;
+    }),
+
+    clean: sArgs(function(str){
+      return _s.strip(str.replace(/\s+/g, ' '));
+    }),
+
+    count: sArgs(function(str, substr){
+      var count = 0, index;
+      for (var i=0; i < str.length;) {
+        index = str.indexOf(substr, i);
+        index >= 0 && count++;
+        i = i + (index >= 0 ? index : 0) + substr.length;
+      }
+      return count;
+    }),
+
+    chars: sArgs(function(str) {
+      return str.split('');
+    }),
+
+    escapeHTML: sArgs(function(str) {
+      return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
+                            .replace(/"/g, '&quot;').replace(/'/g, "&apos;");
+    }),
+
+    unescapeHTML: sArgs(function(str) {
+      return str.replace(/&lt;/g, '<').replace(/&gt;/g, '>')
+                            .replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&');
+    }),
+
+    escapeRegExp: sArgs(function(str){
+      // From MooTools core 1.2.4
+      return str.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+    }),
+
+    insert: sArgs(function(str, i, substr){
+      var arr = str.split('');
+      arr.splice(parseNumber(i), 0, substr);
+      return arr.join('');
+    }),
+
+    include: sArgs(function(str, needle){
+      return str.indexOf(needle) !== -1;
+    }),
+
+    join: sArgs(function(sep) {
+      var args = slice(arguments);
+      return args.join(args.shift());
+    }),
+
+    lines: sArgs(function(str) {
+      return str.split("\n");
+    }),
+
+    reverse: sArgs(function(str){
+        return Array.prototype.reverse.apply(String(str).split('')).join('');
+    }),
+
+    splice: sArgs(function(str, i, howmany, substr){
+      var arr = str.split('');
+      arr.splice(parseNumber(i), parseNumber(howmany), substr);
+      return arr.join('');
+    }),
+
+    startsWith: sArgs(function(str, starts){
+      return str.length >= starts.length && str.substring(0, starts.length) === starts;
+    }),
+
+    endsWith: sArgs(function(str, ends){
+      return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
+    }),
+
+    succ: sArgs(function(str){
+      var arr = str.split('');
+      arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
+      return arr.join('');
+    }),
+
+    titleize: sArgs(function(str){
+      var arr = str.split(' '),
+          word;
+      for (var i=0; i < arr.length; i++) {
+        word = arr[i].split('');
+        if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase();
+        i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' ';
+      }
+      return arr.join('');
+    }),
+
+    camelize: sArgs(function(str){
+      return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
+        return chr ? chr.toUpperCase() : '';
+      });
+    }),
+
+    underscored: function(str){
+      return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
+    },
+
+    dasherize: function(str){
+      return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase();
+    },
+
+    humanize: function(str){
+      return _s.capitalize(this.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
+    },
+
+    trim: sArgs(function(str, characters){
+      if (!characters && nativeTrim) {
+        return nativeTrim.call(str);
+      }
+      characters = defaultToWhiteSpace(characters);
+      return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
+    }),
+
+    ltrim: sArgs(function(str, characters){
+      characters = defaultToWhiteSpace(characters);
+      return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
+    }),
+
+    rtrim: sArgs(function(str, characters){
+      characters = defaultToWhiteSpace(characters);
+      return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
+    }),
+
+    truncate: sArgs(function(str, length, truncateStr){
+      truncateStr = truncateStr || '...';
+      length = parseNumber(length);
+      return str.length > length ? str.slice(0,length) + truncateStr : str;
+    }),
+
+    /**
+     * _s.prune: a more elegant version of truncate
+     * prune extra chars, never leaving a half-chopped word.
+     * @author github.com/sergiokas
+     */
+    prune: sArgs(function(str, length, pruneStr){
+      // Function to check word/digit chars including non-ASCII encodings. 
+      var isWordChar = function(c) { return ((c.toUpperCase() != c.toLowerCase()) || /[-_\d]/.test(c)); }
+      
+      var template = '';
+      var pruned = '';
+      var i = 0;
+      
+      // Set default values
+      pruneStr = pruneStr || '...';
+      length = parseNumber(length);
+      
+      // Convert to an ASCII string to avoid problems with unicode chars.
+      for (i in str) {
+        template += (isWordChar(str[i]))?'A':' ';
+      } 
+
+      // Check if we're in the middle of a word
+      if( template.substring(length-1, length+1).search(/^\w\w$/) === 0 )
+        pruned = _s.rtrim(template.slice(0,length).replace(/([\W][\w]*)$/,''));
+      else
+        pruned = _s.rtrim(template.slice(0,length));
+
+      pruned = pruned.replace(/\W+$/,'');
+
+      return (pruned.length+pruneStr.length>str.length) ? str : str.substring(0, pruned.length)+pruneStr;
+    }),
+
+    words: function(str, delimiter) {
+      return String(str).split(delimiter || " ");
+    },
+
+    pad: sArgs(function(str, length, padStr, type) {
+      var padding = '',
+          padlen  = 0;
+
+      length = parseNumber(length);
+
+      if (!padStr) { padStr = ' '; }
+      else if (padStr.length > 1) { padStr = padStr.charAt(0); }
+      switch(type) {
+        case 'right':
+          padlen = (length - str.length);
+          padding = strRepeat(padStr, padlen);
+          str = str+padding;
+          break;
+        case 'both':
+          padlen = (length - str.length);
+          padding = {
+            'left' : strRepeat(padStr, Math.ceil(padlen/2)),
+            'right': strRepeat(padStr, Math.floor(padlen/2))
+          };
+          str = padding.left+str+padding.right;
+          break;
+        default: // 'left'
+          padlen = (length - str.length);
+          padding = strRepeat(padStr, padlen);;
+          str = padding+str;
+        }
+      return str;
+    }),
+
+    lpad: function(str, length, padStr) {
+      return _s.pad(str, length, padStr);
+    },
+
+    rpad: function(str, length, padStr) {
+      return _s.pad(str, length, padStr, 'right');
+    },
+
+    lrpad: function(str, length, padStr) {
+      return _s.pad(str, length, padStr, 'both');
+    },
+
+    sprintf: sprintf,
+
+    vsprintf: function(fmt, argv){
+      argv.unshift(fmt);
+      return sprintf.apply(null, argv);
+    },
+
+    toNumber: function(str, decimals) {
+      var num = parseNumber(parseNumber(str).toFixed(parseNumber(decimals)));
+      return (!(num === 0 && (str !== "0" && str !== 0))) ? num : Number.NaN;
+    },
+
+    strRight: sArgs(function(sourceStr, sep){
+      var pos =  (!sep) ? -1 : sourceStr.indexOf(sep);
+      return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
+    }),
+
+    strRightBack: sArgs(function(sourceStr, sep){
+      var pos =  (!sep) ? -1 : sourceStr.lastIndexOf(sep);
+      return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
+    }),
+
+    strLeft: sArgs(function(sourceStr, sep){
+      var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
+      return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
+    }),
+
+    strLeftBack: sArgs(function(sourceStr, sep){
+      var pos = sourceStr.lastIndexOf(sep);
+      return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
+    }),
+
+    exports: function() {
+      var result = {};
+
+      for (var prop in this) {
+        if (!this.hasOwnProperty(prop) || prop == 'include' || prop == 'contains' || prop == 'reverse') continue;
+        result[prop] = this[prop];
+      }
+
+      return result;
+    }
+
+  };
+
+  // Aliases
+
+  _s.strip    = _s.trim;
+  _s.lstrip   = _s.ltrim;
+  _s.rstrip   = _s.rtrim;
+  _s.center   = _s.lrpad;
+  _s.ljust    = _s.lpad;
+  _s.rjust    = _s.rpad;
+  _s.contains = _s.include;
+
+  // CommonJS module is defined
+  if (typeof exports !== 'undefined') {
+    if (typeof module !== 'undefined' && module.exports) {
+      // Export module
+      module.exports = _s;
+    }
+    exports._s = _s;
+
+  // Integrate with Underscore.js
+  } else if (typeof root._ !== 'undefined') {
+    // root._.mixin(_s);
+    root._.string = _s;
+    root._.str = root._.string;
+
+  // Or define it
+  } else {
+    root._ = {
+      string: _s,
+      str: _s
+    };
+  }
+
+}(this || window));
+
+
+});
diff --git a/static/vendor/underscore.string-2.0.0.mod.min.js b/static/vendor/underscore.string-2.0.0.mod.min.js
new file mode 100644 (file)
index 0000000..a7adfbb
--- /dev/null
@@ -0,0 +1 @@
+require.define("/node_modules/underscore.string.js",function(e,t,n,r,i,s){(function(e){"use strict";var r=String.prototype.trim,i=function(e){return e*1||0},s=function(e,t){for(var n=[];t>0;n[--t]=e);return n.join("")},o=function(e){return Array.prototype.slice.call(e)},u=function(e){return e?l.escapeRegExp(e):"\\s"},a=function(e){return function(){var t=o(arguments);for(var n=0;n<t.length;n++)t[n]=t[n]==null?"":""+t[n];return e.apply(null,t)}},f=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var t=s,n=function(){return n.cache.hasOwnProperty(arguments[0])||(n.cache[arguments[0]]=n.parse(arguments[0])),n.format.call(null,n.cache[arguments[0]],arguments)};return n.format=function(n,r){var i=1,s=n.length,o="",u,a=[],l,c,h,p,d,v;for(l=0;l<s;l++){o=e(n[l]);if(o==="string")a.push(n[l]);else if(o==="array"){h=n[l];if(h[2]){u=r[i];for(c=0;c<h[2].length;c++){if(!u.hasOwnProperty(h[2][c]))throw f('[_.sprintf] property "%s" does not exist',h[2][c]);u=u[h[2][c]]}}else h[1]?u=r[h[1]]:u=r[i++];if(/[^s]/.test(h[8])&&e(u)!="number")throw f("[_.sprintf] expecting number but found %s",e(u));switch(h[8]){case"b":u=u.toString(2);break;case"c":u=String.fromCharCode(u);break;case"d":u=parseInt(u,10);break;case"e":u=h[7]?u.toExponential(h[7]):u.toExponential();break;case"f":u=h[7]?parseFloat(u).toFixed(h[7]):parseFloat(u);break;case"o":u=u.toString(8);break;case"s":u=(u=String(u))&&h[7]?u.substring(0,h[7]):u;break;case"u":u=Math.abs(u);break;case"x":u=u.toString(16);break;case"X":u=u.toString(16).toUpperCase()}u=/[def]/.test(h[8])&&h[3]&&u>=0?"+"+u:u,d=h[4]?h[4]=="0"?"0":h[4].charAt(1):" ",v=h[6]-String(u).length,p=h[6]?t(d,v):"",a.push(h[5]?u+p:p+u)}}return a.join("")},n.cache={},n.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null)r.push(n[0]);else if((n=/^\x25{2}/.exec(t))!==null)r.push("%");else{if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))===null)throw"[_.sprintf] huh?";if(n[2]){i|=1;var s=[],o=n[2],u=[];if((u=/^([a-z_][a-z_\d]*)/i.exec(o))===null)throw"[_.sprintf] huh?";s.push(u[1]);while((o=o.substring(u[0].length))!=="")if((u=/^\.([a-z_][a-z_\d]*)/i.exec(o))!==null)s.push(u[1]);else{if((u=/^\[(\d+)\]/.exec(o))===null)throw"[_.sprintf] huh?";s.push(u[1])}n[2]=s}else i|=2;if(i===3)throw"[_.sprintf] mixing positional and named placeholders is not (yet) supported";r.push(n)}t=t.substring(n[0].length)}return r},n}(),l={VERSION:"2.0.0",isBlank:a(function(e){return/^\s*$/.test(e)}),stripTags:a(function(e){return e.replace(/<\/?[^>]+>/ig,"")}),capitalize:a(function(e){return e.charAt(0).toUpperCase()+e.substring(1).toLowerCase()}),chop:a(function(e,t){t=i(t)||e.length;var n=[];for(var r=0;r<e.length;)n.push(e.slice(r,r+t)),r+=t;return n}),clean:a(function(e){return l.strip(e.replace(/\s+/g," "))}),count:a(function(e,t){var n=0,r;for(var i=0;i<e.length;)r=e.indexOf(t,i),r>=0&&n++,i=i+(r>=0?r:0)+t.length;return n}),chars:a(function(e){return e.split("")}),escapeHTML:a(function(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}),unescapeHTML:a(function(e){return e.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'").replace(/&amp;/g,"&")}),escapeRegExp:a(function(e){return e.replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1")}),insert:a(function(e,t,n){var r=e.split("");return r.splice(i(t),0,n),r.join("")}),include:a(function(e,t){return e.indexOf(t)!==-1}),join:a(function(e){var t=o(arguments);return t.join(t.shift())}),lines:a(function(e){return e.split("\n")}),reverse:a(function(e){return Array.prototype.reverse.apply(String(e).split("")).join("")}),splice:a(function(e,t,n,r){var s=e.split("");return s.splice(i(t),i(n),r),s.join("")}),startsWith:a(function(e,t){return e.length>=t.length&&e.substring(0,t.length)===t}),endsWith:a(function(e,t){return e.length>=t.length&&e.substring(e.length-t.length)===t}),succ:a(function(e){var t=e.split("");return t.splice(e.length-1,1,String.fromCharCode(e.charCodeAt(e.length-1)+1)),t.join("")}),titleize:a(function(e){var t=e.split(" "),n;for(var r=0;r<t.length;r++)n=t[r].split(""),typeof n[0]!="undefined"&&(n[0]=n[0].toUpperCase()),r+1===t.length?t[r]=n.join(""):t[r]=n.join("")+" ";return t.join("")}),camelize:a(function(e){return l.trim(e).replace(/(\-|_|\s)+(.)?/g,function(e,t,n){return n?n.toUpperCase():""})}),underscored:function(e){return l.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/\-|\s+/g,"_").toLowerCase()},dasherize:function(e){return l.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1-$2").replace(/^([A-Z]+)/,"-$1").replace(/\_|\s+/g,"-").toLowerCase()},humanize:function(e){return l.capitalize(this.underscored(e).replace(/_id$/,"").replace(/_/g," "))},trim:a(function(e,t){return!t&&r?r.call(e):(t=u(t),e.replace(new RegExp("^["+t+"]+|["+t+"]+$","g"),""))}),ltrim:a(function(e,t){return t=u(t),e.replace(new RegExp("^["+t+"]+","g"),"")}),rtrim:a(function(e,t){return t=u(t),e.replace(new RegExp("["+t+"]+$","g"),"")}),truncate:a(function(e,t,n){return n=n||"...",t=i(t),e.length>t?e.slice(0,t)+n:e}),prune:a(function(e,t,n){var r=function(e){return e.toUpperCase()!=e.toLowerCase()||/[-_\d]/.test(e)},s="",o="",u=0;n=n||"...",t=i(t);for(u in e)s+=r(e[u])?"A":" ";return s.substring(t-1,t+1).search(/^\w\w$/)===0?o=l.rtrim(s.slice(0,t).replace(/([\W][\w]*)$/,"")):o=l.rtrim(s.slice(0,t)),o=o.replace(/\W+$/,""),o.length+n.length>e.length?e:e.substring(0,o.length)+n}),words:function(e,t){return String(e).split(t||" ")},pad:a(function(e,t,n,r){var o="",u=0;t=i(t),n?n.length>1&&(n=n.charAt(0)):n=" ";switch(r){case"right":u=t-e.length,o=s(n,u),e+=o;break;case"both":u=t-e.length,o={left:s(n,Math.ceil(u/2)),right:s(n,Math.floor(u/2))},e=o.left+e+o.right;break;default:u=t-e.length,o=s(n,u),e=o+e}return e}),lpad:function(e,t,n){return l.pad(e,t,n)},rpad:function(e,t,n){return l.pad(e,t,n,"right")},lrpad:function(e,t,n){return l.pad(e,t,n,"both")},sprintf:f,vsprintf:function(e,t){return t.unshift(e),f.apply(null,t)},toNumber:function(e,t){var n=i(i(e).toFixed(i(t)));return n!==0||e==="0"||e===0?n:Number.NaN},strRight:a(function(e,t){var n=t?e.indexOf(t):-1;return n!=-1?e.slice(n+t.length,e.length):e}),strRightBack:a(function(e,t){var n=t?e.lastIndexOf(t):-1;return n!=-1?e.slice(n+t.length,e.length):e}),strLeft:a(function(e,t){var n=t?e.indexOf(t):-1;return n!=-1?e.slice(0,n):e}),strLeftBack:a(function(e,t){var n=e.lastIndexOf(t);return n!=-1?e.slice(0,n):e}),exports:function(){var e={};for(var t in this){if(!this.hasOwnProperty(t)||t=="include"||t=="contains"||t=="reverse")continue;e[t]=this[t]}return e}};l.strip=l.trim,l.lstrip=l.ltrim,l.rstrip=l.rtrim,l.center=l.lrpad,l.ljust=l.lpad,l.rjust=l.rpad,l.contains=l.include,typeof n!="undefined"?(typeof t!="undefined"&&t.exports&&(t.exports=l),n._s=l):typeof e._!="undefined"?(e._.string=l,e._.str=e._.string):e._={string:l,str:l}})(this||window)});
\ No newline at end of file
diff --git a/static/vendor/underscore.string.mod.js b/static/vendor/underscore.string.mod.js
new file mode 120000 (symlink)
index 0000000..1c960ce
--- /dev/null
@@ -0,0 +1 @@
+underscore.string-2.0.0.mod.js
\ No newline at end of file
diff --git a/static/vendor/underscore.string.mod.min.js b/static/vendor/underscore.string.mod.min.js
new file mode 120000 (symlink)
index 0000000..66fd2a7
--- /dev/null
@@ -0,0 +1 @@
+underscore.string-2.0.0.mod.min.js
\ No newline at end of file