diff --git a/data.json b/data.json index eddf481..3afa326 100644 --- a/data.json +++ b/data.json @@ -1,11 +1,580 @@ -{ - "collection": "sandwiches", - "title": "Sandwiches", - "entrees": [ - { - "name": "Bánh Mì", - "description": "Slow roasted pork belly, with fresh pickled carrots, onion and cucumbers with jalapeños, red peppers and cilantro on panini bread with sweet chili mayo.", - "price": 14 - } - ] -} \ No newline at end of file +[ + { + "id": 0, + "title": "sandwich", + "collections": [ + { + "id": 0, + "title": "Sandwiches", + "additions": "◆ Add a cup of soup for $4 ◆", + "options": "Can be made with Gluten Free bread", + "entrees": [ + { + "id": 0, + "name": "Bánh Mì", + "description": "Slow roasted pork belly, with fresh pickled carrots, onion and cucumbers with jalapeños, red peppers and cilantro on panini bread with sweet chili mayo.", + "price": "14", + "options": "" + }, + { + "id": 1, + "name": "Club", + "description": "Turkey, ham, bacon, Havarti, sriracha mayo, lettuce & tomato on toasted sourdough.", + "price": "7.50 half / 13 whole", + "options": "" + }, + { + "id": 2, + "name": "Grilled Reuben", + "description": "Pastrami, Swiss, sauerkraut & our homemade sauce on Rye", + "price": "7.50 half / 13 whole", + "options": "" + }, + { + "id": 3, + "name": "Grilled Rachel", + "description": "Turkey, Swiss, sauerkraut & our homemade sauce on Rye", + "price": "7.50 half / 13 whole", + "options": "" + }, + { + "id": 4, + "name": "Grilled Veggie", + "description": "Grilled portobello, zucchini, peppers & onions with fresh greens & our homemade feta sauce on rye", + "price": "7.50 half / 13 whole", + "options": "" + }, + { + "id": 5, + "name": "Pulled Pork", + "description": "Our pulled pork, pepper jack cheese, sliced red onion & our homemade barbecue sauce on panini bread", + "price": "13", + "options": "" + }, + { + "id": 6, + "name": "Grilled Ham & Cheese", + "description": "Ham, Swiss, mayo, Dijon & dill pickle on sourdough", + "price": "7 half / 12 whole", + "options": "" + }, + { + "id": 7, + "name": "Spicy Ham Melt", + "description": "Ham, Havarti, sriracha mayo & red bell peppers on wheat", + "price": "7 half / 12 whole", + "options": "" + }, + { + "id": 8, + "name": "Tuna Melt", + "description": "Our freshly made tuna salad with cheddar on sourdough", + "price": "7 half / 12 whole", + "options": "" + }, + { + "id": 9, + "name": "Cold Veggie", + "description": "Cucumbers, carrots, red onions, red bell peppers, lettuce & tomato with provolone on seedy wheat with hummus", + "price": "7 half / 12 whole", + "options": "" + }, + { + "id": 10, + "name": "Basic Sandwich", + "description": "Choice of: Ham or Turkey; with cheddar, mayo, Dijon, lettuce, and tomato on sourdough", + "price": "7 half / 12 whole", + "options": "" + } + ] + } + ] + }, + { + "id": 1, + "title": "burger", + "collections": [ + { + "id": 0, + "title": "Burgers", + "additions": "◆ Add a cup of soup for $4 ◆", + "options": "Served on panini bread but can be made with Gluten Free bread", + "entrees": [ + { + "id": 0, + "name": "Big Burger", + "description": "Local grass-fed beef, bacon, blue cheese, tomato, lettuce & special sauce", + "price": "14", + "options": "" + }, + { + "id": 1, + "name": "Big Lamb Köfte", + "description": "Grass-fed lamb, tomato, cucumber, red onions & feta sauce", + "price": "14", + "options": "" + }, + { + "id": 2, + "name": "Chicken Burger", + "description": "Organic chicken breast, bacon, provolone, pesto, garlic aioli, lettuce & tomato", + "price": "14", + "options": "" + }, + { + "id": 3, + "name": "Big Classic", + "description": "Local grass-fed beef & house made pork sausage, with bacon, sun-dried tomato pesto, grilled onions & provolone", + "price": "14", + "options": "" + }, + { + "id": 4, + "name": "Portobello Burger", + "description": "Grilled portobello, marinated red onions, jalapeños, red peppers & fresh greens on panini bread with sun-dried tomato pest & caper aioli", + "price": "14", + "options": "" + } + ] + }, + { + "id": 1, + "title": "Sliders", + "additions": "◆ Add a cup of soup for $4 ◆", + "options": "Served on our homemade buttermilk biscuit but can be made with Gluten Free bread", + "entrees": [ + { + "id": 0, + "name": "House", + "description": "Local grass-fed beef, bacon, tomato & special sauce", + "price": "7.50", + "options": "" + }, + { + "id": 1, + "name": "Lamb Köfte", + "description": "Grass-fed lamb, tomato, cucumber, red onions & tiziki sauce", + "price": "7.50", + "options": "" + }, + { + "id": 2, + "name": "Portobello", + "description": "Marinated portobello, provolone, tomato, lettuce & balsamic mayo", + "price": "7.50", + "options": "Vegetarian" + }, + { + "id": 3, + "name": "Little Classic", + "description": "Local grass-fed beef & house made pork sausage with bacon, sun-dried tomato pesto, grilled onions & provolone", + "price": "7.50", + "options": "" + } + ] + } + ] + }, + { + "id": 2, + "title": "salad", + "collections": [ + { + "id": 0, + "title": "House Salads", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "Garden Salad", + "description": "Organic greens, cucumber, carrots, red bell peppers, red onion, beets, corn, peas, carrots, sunflower seeds & Balsamic dressing", + "price": "6.50 small / 10 medium / 15 large", + "options": "Add organic Chicken or Shrimp for $9" + }, + { + "id": 1, + "name": "Apple Blue Cheese Salad", + "description": "Organic greens, blue cheese crumbles, walnuts, red onions & apples with balsamic dressing", + "price": "6.50 small / 10 medium / 15 large", + "options": "" + } + ] + }, + { + "id": 1, + "title": "Taco Salads", + "additions": "", + "options": "Gluten Free", + "entrees": [ + { + "id": 0, + "name": "Beef / Chicken / Veggie", + "description": "Organic greens, black beans, corn, olives, tomatoes, green chillies, avocado, salsa, sour cream & our homemade chips", + "price": "16", + "options": "Veggie includes grilled zucchini & onions" + }, + { + "id": 1, + "name": "Pork Carnitas Taco Salad", + "description": "Organic greens, pinto beans, sweet potatoes, avocado, mango chipotle sauce, salsa, sour cream & our homemade chips", + "price": "16", + "options": "" + } + ] + }, + { + "id": 2, + "title": "Tacos", + "additions": "", + "options": "Gluten Free", + "entrees": [ + { + "id": 0, + "name": "Shrimp Tacos", + "description": "Slaw, mango chipotle sauce, corn tortillas rice & beans", + "price": "15", + "options": "" + }, + { + "id": 1, + "name": "Pork Carnitas Tacos", + "description": "Chimmichurri sauce, corn tortillas rice & beans", + "price": "15", + "options": "" + } + ] + } + ] + }, + { + "id": 3, + "title": "burrito", + "collections": [ + { + "id": 0, + "title": "Kebabs", + "additions": "", + "options": "Can be made Gluten Free with corn tortillas", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 1, + "title": "Burritos", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 2, + "title": "Nachos", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 3, + "title": "Soup", + "additions": "◆ Served with a buttermilk biscuit ◆", + "options": "Gluten Free / Vegan", + "entrees": [ + { + "id": 0, + "name": "Daily (look to the chalkboard)", + "description": "", + "price": "5 cup / 7 bowl", + "options": "" + } + ] + } + ] + }, + { + "id": 4, + "title": "vegan", + "collections": [ + { + "id": 0, + "title": "Buddha Bowls", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 2, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 3, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 1, + "title": "Salads & Wraps", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 2, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 3, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 2, + "title": "Sandwiches", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 2, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 3, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 3, + "title": "◈ Served After 5pm ◈", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 4, + "title": "Pasta", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 2, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 3, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + } + ] + }, + { + "id": 5, + "title": "evening", + "collections": [ + { + "id": 0, + "title": "◈ Served After 5pm ◈", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + }, + { + "id": 1, + "title": "◈ Served After 5pm ◈", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + } + ] + }, + { + "id": 6, + "title": "dessert", + "collections": [ + { + "id": 0, + "title": "Desserts", + "additions": "", + "options": "", + "entrees": [ + { + "id": 0, + "name": "", + "description": "", + "price": "", + "options": "" + }, + { + "id": 1, + "name": "", + "description": "", + "price": "", + "options": "" + } + ] + } + ] + } +] \ No newline at end of file diff --git a/images/IMG_5085.jpg b/images/IMG_5085.jpg deleted file mode 100644 index 18d9fb7..0000000 Binary files a/images/IMG_5085.jpg and /dev/null differ diff --git a/images/IMG_5079.jpg b/images/img_01_owl.jpg similarity index 100% rename from images/IMG_5079.jpg rename to images/img_01_owl.jpg diff --git a/images/IMG_5123.jpg b/images/img_02_sandwich.jpg similarity index 100% rename from images/IMG_5123.jpg rename to images/img_02_sandwich.jpg diff --git a/images/IMG_5057.jpg b/images/img_03_burger.jpg similarity index 100% rename from images/IMG_5057.jpg rename to images/img_03_burger.jpg diff --git a/images/IMG_5179.jpg b/images/img_04_salad.jpg similarity index 100% rename from images/IMG_5179.jpg rename to images/img_04_salad.jpg diff --git a/images/IMG_5114.jpg b/images/img_05_burrito.jpg similarity index 100% rename from images/IMG_5114.jpg rename to images/img_05_burrito.jpg diff --git a/images/IMG_5170.jpg b/images/img_06_vegan.jpg similarity index 100% rename from images/IMG_5170.jpg rename to images/img_06_vegan.jpg diff --git a/images/IMG_5069.jpg b/images/img_07_evening.jpg similarity index 100% rename from images/IMG_5069.jpg rename to images/img_07_evening.jpg diff --git a/images/IMG_5058.jpg b/images/img_08_dessert.jpg similarity index 100% rename from images/IMG_5058.jpg rename to images/img_08_dessert.jpg diff --git a/index.html b/index.html index 63a33ab..05bf7e4 100644 --- a/index.html +++ b/index.html @@ -17,39 +17,56 @@ -

Owl Sprit Cafe

-

Delicious Homemade Food

+
+

Owl Sprit Cafe

+

Delicious Homemade Food

-

Fresh Local Food

-

We believe in local and oranic food and love to share it with our guests.

-

For those who apperciate a cooked meal made from scratch.

-

Treat yourself and your date to homemade goodness when you take the time to enjoy a meal at the Owl Sprit Cafe.

+
+

Fresh Local Food

+

We believe in local and oranic food and love to share it with our guests.

+

For those who apperciate a cooked meal made from scratch.

+

Treat yourself and your date to homemade goodness when you take the time to enjoy a meal at the Owl Sprit Cafe.

-

Hours

-

- 11:00am - 7:30pm
- Closed Sundays -

-

Location

-
- 218 Polk St
- Port Townsend, WA 98368 -
+

Hours

+

+ 11:00am - 7:30pm
+ Closed Sundays +

+

Location

+
+ 218 Polk St
+ Port Townsend, WA 98368 +
-

Phone

-360.385.5275 - -
-

{{ menu.title }}

-
    -
  • - {{ item.name }}: {{ item.description }} -
  • -
-
+

Phone

+ 360.385.5275 +
+
- +
+
+
+

{{ collection.title }}

+

{{ collection.additions }}

+

{{ collection.options }}

+ +
+

{{ entree.name }} {{ entree.price }}

+ {{ entree.options }} +

{{ entree.description }}

+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/scripts/index.js b/scripts/index.js index 6e7b378..29976f2 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -8,19 +8,23 @@ function ready(fn) { } ready(function() { + function renderJSON(json) { + + var app = new Vue({ + el: '#container', + data: { + spreads: json + } + }); + } + var getFile = function(url, cb) { var request = new XMLHttpRequest(); - request.open('GET', url, false); + request.open('GET', url, true); request.onload = function() { if (this.status >= 200 && this.status < 400) { // Success! - var json = JSON.parse(this.response); - var app = new Vue({ - el: '#container', - data: { - menu: json - } - }); + cb(JSON.parse(this.response)); } else { console.log("We reached our target server, but it returned an error"); } @@ -30,5 +34,5 @@ ready(function() { }; request.send(); }; - var data = getFile('/data.json'); + var data = getFile('/data.json', renderJSON); }); diff --git a/scripts/lib/backbone.js b/scripts/lib/backbone.js deleted file mode 100644 index 55ccb22..0000000 --- a/scripts/lib/backbone.js +++ /dev/null @@ -1,1920 +0,0 @@ -// Backbone.js 1.3.3 - -// (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org - -(function(factory) { - - // Establish the root object, `window` (`self`) in the browser, or `global` on the server. - // We use `self` instead of `window` for `WebWorker` support. - var root = (typeof self == 'object' && self.self === self && self) || - (typeof global == 'object' && global.global === global && global); - - // Set up Backbone appropriately for the environment. Start with AMD. - if (typeof define === 'function' && define.amd) { - define(['underscore', 'jquery', 'exports'], function(_, $, exports) { - // Export global even in AMD case in case this script is loaded with - // others that may still expect a global Backbone. - root.Backbone = factory(root, exports, _, $); - }); - - // Next for Node.js or CommonJS. jQuery may not be needed as a module. - } else if (typeof exports !== 'undefined') { - var _ = require('underscore'), $; - try { $ = require('jquery'); } catch (e) {} - factory(root, exports, _, $); - - // Finally, as a browser global. - } else { - root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); - } - -})(function(root, Backbone, _, $) { - - // Initial Setup - // ------------- - - // 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 a common array method we'll want to use later. - var slice = Array.prototype.slice; - - // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '1.3.3'; - - // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns - // the `$` variable. - Backbone.$ = $; - - // 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 `"PATCH"`, `"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 ... this 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; - - // Proxy Backbone class methods to Underscore functions, wrapping the model's - // `attributes` object or collection's `models` array behind the scenes. - // - // collection.filter(function(model) { return model.get('age') > 10 }); - // collection.each(this.addView); - // - // `Function#apply` can be slow so we use the method's arg count, if we know it. - var addMethod = function(length, method, attribute) { - switch (length) { - case 1: return function() { - return _[method](this[attribute]); - }; - case 2: return function(value) { - return _[method](this[attribute], value); - }; - case 3: return function(iteratee, context) { - return _[method](this[attribute], cb(iteratee, this), context); - }; - case 4: return function(iteratee, defaultVal, context) { - return _[method](this[attribute], cb(iteratee, this), defaultVal, context); - }; - default: return function() { - var args = slice.call(arguments); - args.unshift(this[attribute]); - return _[method].apply(_, args); - }; - } - }; - var addUnderscoreMethods = function(Class, methods, attribute) { - _.each(methods, function(length, method) { - if (_[method]) Class.prototype[method] = addMethod(length, method, attribute); - }); - }; - - // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`. - var cb = function(iteratee, instance) { - if (_.isFunction(iteratee)) return iteratee; - if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee); - if (_.isString(iteratee)) return function(model) { return model.get(iteratee); }; - return iteratee; - }; - var modelMatcher = function(attrs) { - var matcher = _.matches(attrs); - return function(model) { - return matcher(model.attributes); - }; - }; - - // Backbone.Events - // --------------- - - // A module that can be mixed in to *any object* in order to provide it with - // a custom event channel. You may bind a callback to an event with `on` or - // remove with `off`; `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 = {}; - - // Regular expression used to split event strings. - var eventSplitter = /\s+/; - - // Iterates over the standard `event, callback` (as well as the fancy multiple - // space-separated events `"change blur", callback` and jQuery-style event - // maps `{event: callback}`). - var eventsApi = function(iteratee, events, name, callback, opts) { - var i = 0, names; - if (name && typeof name === 'object') { - // Handle event maps. - if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; - for (names = _.keys(name); i < names.length ; i++) { - events = eventsApi(iteratee, events, names[i], name[names[i]], opts); - } - } else if (name && eventSplitter.test(name)) { - // Handle space-separated event names by delegating them individually. - for (names = name.split(eventSplitter); i < names.length; i++) { - events = iteratee(events, names[i], callback, opts); - } - } else { - // Finally, standard events. - events = iteratee(events, name, callback, opts); - } - return events; - }; - - // Bind an event to a `callback` function. Passing `"all"` will bind - // the callback to all events fired. - Events.on = function(name, callback, context) { - return internalOn(this, name, callback, context); - }; - - // Guard the `listening` argument from the public API. - var internalOn = function(obj, name, callback, context, listening) { - obj._events = eventsApi(onApi, obj._events || {}, name, callback, { - context: context, - ctx: obj, - listening: listening - }); - - if (listening) { - var listeners = obj._listeners || (obj._listeners = {}); - listeners[listening.id] = listening; - } - - return obj; - }; - - // Inversion-of-control versions of `on`. Tell *this* object to listen to - // an event in another object... keeping track of what it's listening to - // for easier unbinding later. - Events.listenTo = function(obj, name, callback) { - if (!obj) return this; - var id = obj._listenId || (obj._listenId = _.uniqueId('l')); - var listeningTo = this._listeningTo || (this._listeningTo = {}); - var listening = listeningTo[id]; - - // This object is not listening to any other events on `obj` yet. - // Setup the necessary references to track the listening callbacks. - if (!listening) { - var thisId = this._listenId || (this._listenId = _.uniqueId('l')); - listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0}; - } - - // Bind callbacks on obj, and keep track of them on listening. - internalOn(obj, name, callback, this, listening); - return this; - }; - - // The reducing API that adds a callback to the `events` object. - var onApi = function(events, name, callback, options) { - if (callback) { - var handlers = events[name] || (events[name] = []); - var context = options.context, ctx = options.ctx, listening = options.listening; - if (listening) listening.count++; - - handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening}); - } - return events; - }; - - // 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 `name` is null, removes all bound - // callbacks for all events. - Events.off = function(name, callback, context) { - if (!this._events) return this; - this._events = eventsApi(offApi, this._events, name, callback, { - context: context, - listeners: this._listeners - }); - return this; - }; - - // Tell this object to stop listening to either specific events ... or - // to every object it's currently listening to. - Events.stopListening = function(obj, name, callback) { - var listeningTo = this._listeningTo; - if (!listeningTo) return this; - - var ids = obj ? [obj._listenId] : _.keys(listeningTo); - - for (var i = 0; i < ids.length; i++) { - var listening = listeningTo[ids[i]]; - - // If listening doesn't exist, this object is not currently - // listening to obj. Break out early. - if (!listening) break; - - listening.obj.off(name, callback, this); - } - - return this; - }; - - // The reducing API that removes a callback from the `events` object. - var offApi = function(events, name, callback, options) { - if (!events) return; - - var i = 0, listening; - var context = options.context, listeners = options.listeners; - - // Delete all events listeners and "drop" events. - if (!name && !callback && !context) { - var ids = _.keys(listeners); - for (; i < ids.length; i++) { - listening = listeners[ids[i]]; - delete listeners[listening.id]; - delete listening.listeningTo[listening.objId]; - } - return; - } - - var names = name ? [name] : _.keys(events); - for (; i < names.length; i++) { - name = names[i]; - var handlers = events[name]; - - // Bail out if there are no events stored. - if (!handlers) break; - - // Replace events if there are any remaining. Otherwise, clean up. - var remaining = []; - for (var j = 0; j < handlers.length; j++) { - var handler = handlers[j]; - if ( - callback && callback !== handler.callback && - callback !== handler.callback._callback || - context && context !== handler.context - ) { - remaining.push(handler); - } else { - listening = handler.listening; - if (listening && --listening.count === 0) { - delete listeners[listening.id]; - delete listening.listeningTo[listening.objId]; - } - } - } - - // Update tail event if the list has any events. Otherwise, clean up. - if (remaining.length) { - events[name] = remaining; - } else { - delete events[name]; - } - } - return events; - }; - - // Bind an event to only be triggered a single time. After the first time - // the callback is invoked, its listener will be removed. If multiple events - // are passed in using the space-separated syntax, the handler will fire - // once for each event, not once for a combination of all events. - Events.once = function(name, callback, context) { - // Map the event into a `{event: once}` object. - var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); - if (typeof name === 'string' && context == null) callback = void 0; - return this.on(events, callback, context); - }; - - // Inversion-of-control versions of `once`. - Events.listenToOnce = function(obj, name, callback) { - // Map the event into a `{event: once}` object. - var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj)); - return this.listenTo(obj, events); - }; - - // Reduces the event callbacks into a map of `{event: onceWrapper}`. - // `offer` unbinds the `onceWrapper` after it has been called. - var onceMap = function(map, name, callback, offer) { - if (callback) { - var once = map[name] = _.once(function() { - offer(name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - } - return map; - }; - - // 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). - Events.trigger = function(name) { - if (!this._events) return this; - - var length = Math.max(0, arguments.length - 1); - var args = Array(length); - for (var i = 0; i < length; i++) args[i] = arguments[i + 1]; - - eventsApi(triggerApi, this._events, name, void 0, args); - return this; - }; - - // Handles triggering the appropriate event callbacks. - var triggerApi = function(objEvents, name, callback, args) { - if (objEvents) { - var events = objEvents[name]; - var allEvents = objEvents.all; - if (events && allEvents) allEvents = allEvents.slice(); - if (events) triggerEvents(events, args); - if (allEvents) triggerEvents(allEvents, [name].concat(args)); - } - return objEvents; - }; - - // A difficult-to-believe, but optimized internal dispatch function for - // triggering events. Tries to keep the usual cases speedy (most internal - // Backbone events have 3 arguments). - var triggerEvents = function(events, args) { - var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; - switch (args.length) { - case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; - case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; - case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; - case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; - default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; - } - }; - - // Aliases for backwards compatibility. - Events.bind = Events.on; - Events.unbind = Events.off; - - // Allow the `Backbone` object to serve as a global event bus, for folks who - // want global "pubsub" in a convenient place. - _.extend(Backbone, Events); - - // Backbone.Model - // -------------- - - // Backbone **Models** are the basic data object in the framework -- - // frequently representing a row in a table in a database on your server. - // A discrete chunk of data and a bunch of useful, related methods for - // performing computations and transformations on that data. - - // Create a new model with the specified attributes. A client id (`cid`) - // is automatically generated and assigned for you. - var Model = Backbone.Model = function(attributes, options) { - var attrs = attributes || {}; - options || (options = {}); - this.cid = _.uniqueId(this.cidPrefix); - this.attributes = {}; - if (options.collection) this.collection = options.collection; - if (options.parse) attrs = this.parse(attrs, options) || {}; - var defaults = _.result(this, 'defaults'); - attrs = _.defaults(_.extend({}, defaults, attrs), defaults); - this.set(attrs, options); - this.changed = {}; - 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, - - // The value returned during the last failed validation. - validationError: null, - - // The default name for the JSON `id` attribute is `"id"`. MongoDB and - // CouchDB users may want to set this to `"_id"`. - idAttribute: 'id', - - // The prefix is used to create the client id which is used to identify models locally. - // You may want to override this if you're experiencing name clashes with model ids. - cidPrefix: 'c', - - // 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); - }, - - // Proxy `Backbone.sync` by default -- but override this if you need - // custom syncing semantics for *this* particular model. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Get the value of an attribute. - get: function(attr) { - return this.attributes[attr]; - }, - - // Get the HTML-escaped value of an attribute. - escape: function(attr) { - return _.escape(this.get(attr)); - }, - - // Returns `true` if the attribute contains a value that is not null - // or undefined. - has: function(attr) { - return this.get(attr) != null; - }, - - // Special-cased proxy to underscore's `_.matches` method. - matches: function(attrs) { - return !!_.iteratee(attrs, this)(this.attributes); - }, - - // Set a hash of model attributes on the object, firing `"change"`. This is - // the core primitive operation of a model, updating the data and notifying - // anyone who needs to know about the change in state. The heart of the beast. - set: function(key, val, options) { - if (key == null) return this; - - // Handle both `"key", value` and `{key: value}` -style arguments. - var attrs; - if (typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options || (options = {}); - - // Run validation. - if (!this._validate(attrs, options)) return false; - - // Extract attributes and options. - var unset = options.unset; - var silent = options.silent; - var changes = []; - var changing = this._changing; - this._changing = true; - - if (!changing) { - this._previousAttributes = _.clone(this.attributes); - this.changed = {}; - } - - var current = this.attributes; - var changed = this.changed; - var prev = this._previousAttributes; - - // For each `set` attribute, update or delete the current value. - for (var attr in attrs) { - val = attrs[attr]; - if (!_.isEqual(current[attr], val)) changes.push(attr); - if (!_.isEqual(prev[attr], val)) { - changed[attr] = val; - } else { - delete changed[attr]; - } - unset ? delete current[attr] : current[attr] = val; - } - - // Update the `id`. - if (this.idAttribute in attrs) this.id = this.get(this.idAttribute); - - // Trigger all relevant attribute changes. - if (!silent) { - if (changes.length) this._pending = options; - for (var i = 0; i < changes.length; i++) { - this.trigger('change:' + changes[i], this, current[changes[i]], options); - } - } - - // You might be wondering why there's a `while` loop here. Changes can - // be recursively nested within `"change"` events. - if (changing) return this; - if (!silent) { - while (this._pending) { - options = this._pending; - this._pending = false; - this.trigger('change', this, options); - } - } - this._pending = false; - this._changing = false; - return this; - }, - - // Remove an attribute from the model, firing `"change"`. `unset` is a noop - // if the attribute doesn't exist. - unset: function(attr, options) { - return this.set(attr, void 0, _.extend({}, options, {unset: true})); - }, - - // Clear all attributes on the model, firing `"change"`. - clear: function(options) { - var attrs = {}; - for (var key in this.attributes) attrs[key] = void 0; - return this.set(attrs, _.extend({}, options, {unset: true})); - }, - - // 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 (attr == null) 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 old = this._changing ? this._previousAttributes : this.attributes; - var changed = {}; - for (var attr in diff) { - var val = diff[attr]; - if (_.isEqual(old[attr], val)) continue; - changed[attr] = val; - } - return _.size(changed) ? changed : false; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (attr == null || !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); - }, - - // Fetch the model from the server, merging the response with the model's - // local attributes. Any changed attributes will trigger a "change" event. - fetch: function(options) { - options = _.extend({parse: true}, options); - var model = this; - var success = options.success; - options.success = function(resp) { - var serverAttrs = options.parse ? model.parse(resp, options) : resp; - if (!model.set(serverAttrs, options)) return false; - if (success) success.call(options.context, model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - return this.sync('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, val, options) { - // Handle both `"key", value` and `{key: value}` -style arguments. - var attrs; - if (key == null || typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options = _.extend({validate: true, parse: true}, options); - var wait = options.wait; - - // If we're not waiting and attributes exist, save acts as - // `set(attr).save(null, opts)` with validation. Otherwise, check if - // the model will be valid when the attributes, if any, are set. - if (attrs && !wait) { - if (!this.set(attrs, options)) return false; - } else if (!this._validate(attrs, 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; - var attributes = this.attributes; - options.success = function(resp) { - // Ensure attributes are restored during synchronous saves. - model.attributes = attributes; - var serverAttrs = options.parse ? model.parse(resp, options) : resp; - if (wait) serverAttrs = _.extend({}, attrs, serverAttrs); - if (serverAttrs && !model.set(serverAttrs, options)) return false; - if (success) success.call(options.context, model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - - // Set temporary attributes if `{wait: true}` to properly find new ids. - if (attrs && wait) this.attributes = _.extend({}, attributes, attrs); - - var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); - if (method === 'patch' && !options.attrs) options.attrs = attrs; - var xhr = this.sync(method, this, options); - - // Restore attributes. - this.attributes = attributes; - - 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 wait = options.wait; - - var destroy = function() { - model.stopListening(); - model.trigger('destroy', model, model.collection, options); - }; - - options.success = function(resp) { - if (wait) destroy(); - if (success) success.call(options.context, model, resp, options); - if (!model.isNew()) model.trigger('sync', model, resp, options); - }; - - var xhr = false; - if (this.isNew()) { - _.defer(options.success); - } else { - wrapError(this, options); - xhr = this.sync('delete', this, options); - } - if (!wait) destroy(); - 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 = - _.result(this, 'urlRoot') || - _.result(this.collection, 'url') || - urlError(); - if (this.isNew()) return base; - var id = this.get(this.idAttribute); - return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(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, options) { - 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.has(this.idAttribute); - }, - - // Check if the model is currently in a valid state. - isValid: function(options) { - return this._validate({}, _.extend({}, options, {validate: true})); - }, - - // Run validation against the next complete set of model attributes, - // returning `true` if all is well. Otherwise, fire an `"invalid"` event. - _validate: function(attrs, options) { - if (!options.validate || !this.validate) return true; - attrs = _.extend({}, this.attributes, attrs); - var error = this.validationError = this.validate(attrs, options) || null; - if (!error) return true; - this.trigger('invalid', this, error, _.extend(options, {validationError: error})); - return false; - } - - }); - - // Underscore methods that we want to implement on the Model, mapped to the - // number of arguments they take. - var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0, - omit: 0, chain: 1, isEmpty: 1}; - - // Mix in each Underscore method as a proxy to `Model#attributes`. - addUnderscoreMethods(Model, modelMethods, 'attributes'); - - // Backbone.Collection - // ------------------- - - // If models tend to represent a single row of data, a Backbone Collection is - // more analogous to a table full of data ... or a small slice or page of that - // table, or a collection of rows that belong together for a particular reason - // -- all of the messages in this particular folder, all of the documents - // belonging to this particular author, and so on. Collections maintain - // indexes of their models, both in order, and for lookup by `id`. - - // Create a new **Collection**, perhaps to contain a specific type of `model`. - // 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 !== void 0) this.comparator = options.comparator; - this._reset(); - this.initialize.apply(this, arguments); - if (models) this.reset(models, _.extend({silent: true}, options)); - }; - - // Default options for `Collection#set`. - var setOptions = {add: true, remove: true, merge: true}; - var addOptions = {add: true, remove: false}; - - // Splices `insert` into `array` at index `at`. - var splice = function(array, insert, at) { - at = Math.min(Math.max(at, 0), array.length); - var tail = Array(array.length - at); - var length = insert.length; - var i; - for (i = 0; i < tail.length; i++) tail[i] = array[i + at]; - for (i = 0; i < length; i++) array[i + at] = insert[i]; - for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i]; - }; - - // 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); }); - }, - - // Proxy `Backbone.sync` by default. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Add a model, or list of models to the set. `models` may be Backbone - // Models or raw JavaScript objects to be converted to Models, or any - // combination of the two. - add: function(models, options) { - return this.set(models, _.extend({merge: false}, options, addOptions)); - }, - - // Remove a model, or a list of models from the set. - remove: function(models, options) { - options = _.extend({}, options); - var singular = !_.isArray(models); - models = singular ? [models] : models.slice(); - var removed = this._removeModels(models, options); - if (!options.silent && removed.length) { - options.changes = {added: [], merged: [], removed: removed}; - this.trigger('update', this, options); - } - return singular ? removed[0] : removed; - }, - - // Update a collection by `set`-ing a new list of models, adding new ones, - // removing models that are no longer present, and merging models that - // already exist in the collection, as necessary. Similar to **Model#set**, - // the core operation for updating the data contained by the collection. - set: function(models, options) { - if (models == null) return; - - options = _.extend({}, setOptions, options); - if (options.parse && !this._isModel(models)) { - models = this.parse(models, options) || []; - } - - var singular = !_.isArray(models); - models = singular ? [models] : models.slice(); - - var at = options.at; - if (at != null) at = +at; - if (at > this.length) at = this.length; - if (at < 0) at += this.length + 1; - - var set = []; - var toAdd = []; - var toMerge = []; - var toRemove = []; - var modelMap = {}; - - var add = options.add; - var merge = options.merge; - var remove = options.remove; - - var sort = false; - var sortable = this.comparator && at == null && options.sort !== false; - var sortAttr = _.isString(this.comparator) ? this.comparator : null; - - // Turn bare objects into model references, and prevent invalid models - // from being added. - var model, i; - for (i = 0; i < models.length; i++) { - model = models[i]; - - // If a duplicate is found, prevent it from being added and - // optionally merge it into the existing model. - var existing = this.get(model); - if (existing) { - if (merge && model !== existing) { - var attrs = this._isModel(model) ? model.attributes : model; - if (options.parse) attrs = existing.parse(attrs, options); - existing.set(attrs, options); - toMerge.push(existing); - if (sortable && !sort) sort = existing.hasChanged(sortAttr); - } - if (!modelMap[existing.cid]) { - modelMap[existing.cid] = true; - set.push(existing); - } - models[i] = existing; - - // If this is a new, valid model, push it to the `toAdd` list. - } else if (add) { - model = models[i] = this._prepareModel(model, options); - if (model) { - toAdd.push(model); - this._addReference(model, options); - modelMap[model.cid] = true; - set.push(model); - } - } - } - - // Remove stale models. - if (remove) { - for (i = 0; i < this.length; i++) { - model = this.models[i]; - if (!modelMap[model.cid]) toRemove.push(model); - } - if (toRemove.length) this._removeModels(toRemove, options); - } - - // See if sorting is needed, update `length` and splice in new models. - var orderChanged = false; - var replace = !sortable && add && remove; - if (set.length && replace) { - orderChanged = this.length !== set.length || _.some(this.models, function(m, index) { - return m !== set[index]; - }); - this.models.length = 0; - splice(this.models, set, 0); - this.length = this.models.length; - } else if (toAdd.length) { - if (sortable) sort = true; - splice(this.models, toAdd, at == null ? this.length : at); - this.length = this.models.length; - } - - // Silently sort the collection if appropriate. - if (sort) this.sort({silent: true}); - - // Unless silenced, it's time to fire all appropriate add/sort/update events. - if (!options.silent) { - for (i = 0; i < toAdd.length; i++) { - if (at != null) options.index = at + i; - model = toAdd[i]; - model.trigger('add', model, this, options); - } - if (sort || orderChanged) this.trigger('sort', this, options); - if (toAdd.length || toRemove.length || toMerge.length) { - options.changes = { - added: toAdd, - removed: toRemove, - merged: toMerge - }; - this.trigger('update', this, options); - } - } - - // Return the added (or merged) model (or models). - return singular ? models[0] : models; - }, - - // 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 granular `add` or `remove` events. Fires `reset` when finished. - // Useful for bulk operations and optimizations. - reset: function(models, options) { - options = options ? _.clone(options) : {}; - for (var i = 0; i < this.models.length; i++) { - this._removeReference(this.models[i], options); - } - options.previousModels = this.models; - this._reset(); - models = this.add(models, _.extend({silent: true}, options)); - if (!options.silent) this.trigger('reset', this, options); - return models; - }, - - // Add a model to the end of the collection. - push: function(model, options) { - return this.add(model, _.extend({at: this.length}, options)); - }, - - // Remove a model from the end of the collection. - pop: function(options) { - var model = this.at(this.length - 1); - return this.remove(model, options); - }, - - // Add a model to the beginning of the collection. - unshift: function(model, options) { - return this.add(model, _.extend({at: 0}, options)); - }, - - // Remove a model from the beginning of the collection. - shift: function(options) { - var model = this.at(0); - return this.remove(model, options); - }, - - // Slice out a sub-array of models from the collection. - slice: function() { - return slice.apply(this.models, arguments); - }, - - // Get a model from the set by id, cid, model object with id or cid - // properties, or an attributes object that is transformed through modelId. - get: function(obj) { - if (obj == null) return void 0; - return this._byId[obj] || - this._byId[this.modelId(obj.attributes || obj)] || - obj.cid && this._byId[obj.cid]; - }, - - // Returns `true` if the model is in the collection. - has: function(obj) { - return this.get(obj) != null; - }, - - // Get the model at the given index. - at: function(index) { - if (index < 0) index += this.length; - return this.models[index]; - }, - - // Return models with matching attributes. Useful for simple cases of - // `filter`. - where: function(attrs, first) { - return this[first ? 'find' : 'filter'](attrs); - }, - - // Return the first model with matching attributes. Useful for simple cases - // of `find`. - findWhere: function(attrs) { - return this.where(attrs, 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) { - var comparator = this.comparator; - if (!comparator) throw new Error('Cannot sort a set without a comparator'); - options || (options = {}); - - var length = comparator.length; - if (_.isFunction(comparator)) comparator = _.bind(comparator, this); - - // Run sort based on type of `comparator`. - if (length === 1 || _.isString(comparator)) { - this.models = this.sortBy(comparator); - } else { - this.models.sort(comparator); - } - if (!options.silent) this.trigger('sort', this, options); - return this; - }, - - // Pluck an attribute from each model in the collection. - pluck: function(attr) { - return this.map(attr + ''); - }, - - // Fetch the default set of models for this collection, resetting the - // collection when they arrive. If `reset: true` is passed, the response - // data will be passed through the `reset` method instead of `set`. - fetch: function(options) { - options = _.extend({parse: true}, options); - var success = options.success; - var collection = this; - options.success = function(resp) { - var method = options.reset ? 'reset' : 'set'; - collection[method](resp, options); - if (success) success.call(options.context, collection, resp, options); - collection.trigger('sync', collection, resp, options); - }; - wrapError(this, options); - return this.sync('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) { - options = options ? _.clone(options) : {}; - var wait = options.wait; - model = this._prepareModel(model, options); - if (!model) return false; - if (!wait) this.add(model, options); - var collection = this; - var success = options.success; - options.success = function(m, resp, callbackOpts) { - if (wait) collection.add(m, callbackOpts); - if (success) success.call(callbackOpts.context, m, resp, callbackOpts); - }; - 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, options) { - return resp; - }, - - // Create a new collection with an identical list of models as this one. - clone: function() { - return new this.constructor(this.models, { - model: this.model, - comparator: this.comparator - }); - }, - - // Define how to uniquely identify models in the collection. - modelId: function(attrs) { - return attrs[this.model.prototype.idAttribute || 'id']; - }, - - // Private method to reset all internal state. Called when the collection - // is first initialized or reset. - _reset: function() { - this.length = 0; - this.models = []; - this._byId = {}; - }, - - // Prepare a hash of attributes (or other model) to be added to this - // collection. - _prepareModel: function(attrs, options) { - if (this._isModel(attrs)) { - if (!attrs.collection) attrs.collection = this; - return attrs; - } - options = options ? _.clone(options) : {}; - options.collection = this; - var model = new this.model(attrs, options); - if (!model.validationError) return model; - this.trigger('invalid', this, model.validationError, options); - return false; - }, - - // Internal method called by both remove and set. - _removeModels: function(models, options) { - var removed = []; - for (var i = 0; i < models.length; i++) { - var model = this.get(models[i]); - if (!model) continue; - - var index = this.indexOf(model); - this.models.splice(index, 1); - this.length--; - - // Remove references before triggering 'remove' event to prevent an - // infinite loop. #3693 - delete this._byId[model.cid]; - var id = this.modelId(model.attributes); - if (id != null) delete this._byId[id]; - - if (!options.silent) { - options.index = index; - model.trigger('remove', model, this, options); - } - - removed.push(model); - this._removeReference(model, options); - } - return removed; - }, - - // Method for checking whether an object should be considered a model for - // the purposes of adding to the collection. - _isModel: function(model) { - return model instanceof Model; - }, - - // Internal method to create a model's ties to a collection. - _addReference: function(model, options) { - this._byId[model.cid] = model; - var id = this.modelId(model.attributes); - if (id != null) this._byId[id] = model; - model.on('all', this._onModelEvent, this); - }, - - // Internal method to sever a model's ties to a collection. - _removeReference: function(model, options) { - delete this._byId[model.cid]; - var id = this.modelId(model.attributes); - if (id != null) delete this._byId[id]; - 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 (model) { - if ((event === 'add' || event === 'remove') && collection !== this) return; - if (event === 'destroy') this.remove(model, options); - if (event === 'change') { - var prevId = this.modelId(model.previousAttributes()); - var id = this.modelId(model.attributes); - if (prevId !== id) { - if (prevId != null) delete this._byId[prevId]; - if (id != null) this._byId[id] = model; - } - } - } - this.trigger.apply(this, arguments); - } - - }); - - // Underscore methods that we want to implement on the Collection. - // 90% of the core usefulness of Backbone Collections is actually implemented - // right here: - var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0, - foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3, - select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3, - contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3, - head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3, - without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3, - isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3, - sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3}; - - // Mix in each Underscore method as a proxy to `Collection#models`. - addUnderscoreMethods(Collection, collectionMethods, 'models'); - - // Backbone.View - // ------------- - - // Backbone Views are almost more convention than they are actual code. A View - // is simply a JavaScript object that represents a logical chunk of UI in the - // DOM. This might be a single item, an entire list, a sidebar or panel, or - // even the surrounding frame which wraps your whole app. Defining a chunk of - // UI as a **View** allows you to define your DOM events declaratively, without - // having to worry about render order ... and makes it easy for the view to - // react to specific changes in the state of your models. - - // 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'); - _.extend(this, _.pick(options, viewOptions)); - this._ensureElement(); - this.initialize.apply(this, arguments); - }; - - // Cached regex to split keys for `delegate`. - var delegateEventSplitter = /^(\S+)\s*(.*)$/; - - // List of view options to be set as properties. - var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; - - // 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 preferred 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 by taking the element out of the DOM, and removing any - // applicable Backbone.Events listeners. - remove: function() { - this._removeElement(); - this.stopListening(); - return this; - }, - - // Remove this view's element from the document and all event listeners - // attached to it. Exposed for subclasses using an alternative DOM - // manipulation API. - _removeElement: function() { - this.$el.remove(); - }, - - // Change the view's element (`this.el` property) and re-delegate the - // view's events on the new element. - setElement: function(element) { - this.undelegateEvents(); - this._setElement(element); - this.delegateEvents(); - return this; - }, - - // Creates the `this.el` and `this.$el` references for this view using the - // given `el`. `el` can be a CSS selector or an HTML string, a jQuery - // context or an element. Subclasses can override this to utilize an - // alternative DOM manipulation API and are only required to set the - // `this.el` property. - _setElement: function(el) { - this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); - this.el = this.$el[0]; - }, - - // 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`. - delegateEvents: function(events) { - events || (events = _.result(this, 'events')); - if (!events) return this; - this.undelegateEvents(); - for (var key in events) { - var method = events[key]; - if (!_.isFunction(method)) method = this[method]; - if (!method) continue; - var match = key.match(delegateEventSplitter); - this.delegate(match[1], match[2], _.bind(method, this)); - } - return this; - }, - - // Add a single event listener to the view's element (or a child element - // using `selector`). This only works for delegate-able events: not `focus`, - // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer. - delegate: function(eventName, selector, listener) { - this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener); - return this; - }, - - // Clears all callbacks previously bound to the view by `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() { - if (this.$el) this.$el.off('.delegateEvents' + this.cid); - return this; - }, - - // A finer-grained `undelegateEvents` for removing a single delegated event. - // `selector` and `listener` are both optional. - undelegate: function(eventName, selector, listener) { - this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener); - return this; - }, - - // Produces a DOM element to be assigned to your view. Exposed for - // subclasses using an alternative DOM manipulation API. - _createElement: function(tagName) { - return document.createElement(tagName); - }, - - // 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 = _.extend({}, _.result(this, 'attributes')); - if (this.id) attrs.id = _.result(this, 'id'); - if (this.className) attrs['class'] = _.result(this, 'className'); - this.setElement(this._createElement(_.result(this, 'tagName'))); - this._setAttributes(attrs); - } else { - this.setElement(_.result(this, 'el')); - } - }, - - // Set attributes from a hash on this view's element. Exposed for - // subclasses using an alternative DOM manipulation API. - _setAttributes: function(attributes) { - this.$el.attr(attributes); - } - - }); - - // Backbone.sync - // ------------- - - // 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. - _.defaults(options || (options = {}), { - emulateHTTP: Backbone.emulateHTTP, - emulateJSON: Backbone.emulateJSON - }); - - // Default JSON-request options. - var params = {type: type, dataType: 'json'}; - - // Ensure that we have a URL. - if (!options.url) { - params.url = _.result(model, 'url') || urlError(); - } - - // Ensure that we have the appropriate request data. - if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { - params.contentType = 'application/json'; - params.data = JSON.stringify(options.attrs || model.toJSON(options)); - } - - // For older servers, emulate JSON by encoding the request into an HTML-form. - if (options.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 (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { - params.type = 'POST'; - if (options.emulateJSON) params.data._method = type; - var beforeSend = options.beforeSend; - options.beforeSend = function(xhr) { - xhr.setRequestHeader('X-HTTP-Method-Override', type); - if (beforeSend) return beforeSend.apply(this, arguments); - }; - } - - // Don't process data on a non-GET request. - if (params.type !== 'GET' && !options.emulateJSON) { - params.processData = false; - } - - // Pass along `textStatus` and `errorThrown` from jQuery. - var error = options.error; - options.error = function(xhr, textStatus, errorThrown) { - options.textStatus = textStatus; - options.errorThrown = errorThrown; - if (error) error.call(options.context, xhr, textStatus, errorThrown); - }; - - // Make the request, allowing the user to override any Ajax options. - var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); - model.trigger('request', model, xhr, options); - return xhr; - }; - - // Map from CRUD to HTTP for our default `Backbone.sync` implementation. - var methodMap = { - 'create': 'POST', - 'update': 'PUT', - 'patch': 'PATCH', - 'delete': 'DELETE', - 'read': 'GET' - }; - - // Set the default implementation of `Backbone.ajax` to proxy through to `$`. - // Override this if you'd like to use a different library. - Backbone.ajax = function() { - return Backbone.$.ajax.apply(Backbone.$, 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 optionalParam = /\((.*?)\)/g; - 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) { - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (_.isFunction(name)) { - callback = name; - name = ''; - } - if (!callback) callback = this[name]; - var router = this; - Backbone.history.route(route, function(fragment) { - var args = router._extractParameters(route, fragment); - if (router.execute(callback, args, name) !== false) { - router.trigger.apply(router, ['route:' + name].concat(args)); - router.trigger('route', name, args); - Backbone.history.trigger('route', router, name, args); - } - }); - return this; - }, - - // Execute a route handler with the provided parameters. This is an - // excellent place to do pre-route setup or post-route cleanup. - execute: function(callback, args, name) { - if (callback) callback.apply(this, args); - }, - - // Simple proxy to `Backbone.history` to save a fragment into the history. - navigate: function(fragment, options) { - Backbone.history.navigate(fragment, options); - return this; - }, - - // 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; - this.routes = _.result(this, 'routes'); - var route, routes = _.keys(this.routes); - while ((route = routes.pop()) != null) { - this.route(route, this.routes[route]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function(route) { - route = route.replace(escapeRegExp, '\\$&') - .replace(optionalParam, '(?:$1)?') - .replace(namedParam, function(match, optional) { - return optional ? match : '([^/?]+)'; - }) - .replace(splatParam, '([^?]*?)'); - return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted decoded parameters. Empty or unmatched parameters will be - // treated as `null` to normalize cross-browser behavior. - _extractParameters: function(route, fragment) { - var params = route.exec(fragment).slice(1); - return _.map(params, function(param, i) { - // Don't decode the search params. - if (i === params.length - 1) return param || null; - return param ? decodeURIComponent(param) : null; - }); - } - - }); - - // Backbone.History - // ---------------- - - // Handles cross-browser history management, based on either - // [pushState](http://diveintohtml5.info/history.html) and real URLs, or - // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) - // and URL fragments. If the browser supports neither (old IE, natch), - // falls back to polling. - var History = Backbone.History = function() { - this.handlers = []; - this.checkUrl = _.bind(this.checkUrl, this); - - // Ensure that `History` can be used outside of the browser. - if (typeof window !== 'undefined') { - this.location = window.location; - this.history = window.history; - } - }; - - // Cached regex for stripping a leading hash/slash and trailing space. - var routeStripper = /^[#\/]|\s+$/g; - - // Cached regex for stripping leading and trailing slashes. - var rootStripper = /^\/+|\/+$/g; - - // Cached regex for stripping urls of hash. - var pathStripper = /#.*$/; - - // 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, - - // Are we at the app root? - atRoot: function() { - var path = this.location.pathname.replace(/[^\/]$/, '$&/'); - return path === this.root && !this.getSearch(); - }, - - // Does the pathname match the root? - matchRoot: function() { - var path = this.decodeFragment(this.location.pathname); - var rootPath = path.slice(0, this.root.length - 1) + '/'; - return rootPath === this.root; - }, - - // Unicode characters in `location.pathname` are percent encoded so they're - // decoded for comparison. `%25` should not be decoded since it may be part - // of an encoded parameter. - decodeFragment: function(fragment) { - return decodeURI(fragment.replace(/%25/g, '%2525')); - }, - - // In IE6, the hash fragment and search params are incorrect if the - // fragment contains `?`. - getSearch: function() { - var match = this.location.href.replace(/#.*/, '').match(/\?.+/); - return match ? match[0] : ''; - }, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function(window) { - var match = (window || this).location.href.match(/#(.*)$/); - return match ? match[1] : ''; - }, - - // Get the pathname and search params, without the root. - getPath: function() { - var path = this.decodeFragment( - this.location.pathname + this.getSearch() - ).slice(this.root.length - 1); - return path.charAt(0) === '/' ? path.slice(1) : path; - }, - - // Get the cross-browser normalized URL fragment from the path or hash. - getFragment: function(fragment) { - if (fragment == null) { - if (this._usePushState || !this._wantsHashChange) { - fragment = this.getPath(); - } else { - fragment = this.getHash(); - } - } - 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.root = this.options.root; - this._wantsHashChange = this.options.hashChange !== false; - this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7); - this._useHashChange = this._wantsHashChange && this._hasHashChange; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.history && this.history.pushState); - this._usePushState = this._wantsPushState && this._hasPushState; - this.fragment = this.getFragment(); - - // Normalize root to always include a leading and trailing slash. - this.root = ('/' + this.root + '/').replace(rootStripper, '/'); - - // Transition from hashChange to pushState or vice versa if both are - // requested. - if (this._wantsHashChange && this._wantsPushState) { - - // 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._hasPushState && !this.atRoot()) { - var rootPath = this.root.slice(0, -1) || '/'; - this.location.replace(rootPath + '#' + this.getPath()); - // 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._hasPushState && this.atRoot()) { - this.navigate(this.getHash(), {replace: true}); - } - - } - - // Proxy an iframe to handle location events if the browser doesn't - // support the `hashchange` event, HTML5 history, or the user wants - // `hashChange` but not `pushState`. - if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) { - this.iframe = document.createElement('iframe'); - this.iframe.src = 'javascript:0'; - this.iframe.style.display = 'none'; - this.iframe.tabIndex = -1; - var body = document.body; - // Using `appendChild` will throw on IE < 9 if the document is not ready. - var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow; - iWindow.document.open(); - iWindow.document.close(); - iWindow.location.hash = '#' + this.fragment; - } - - // Add a cross-platform `addEventListener` shim for older browsers. - var addEventListener = window.addEventListener || function(eventName, listener) { - return attachEvent('on' + eventName, listener); - }; - - // Depending on whether we're using pushState or hashes, and whether - // 'onhashchange' is supported, determine how we check the URL state. - if (this._usePushState) { - addEventListener('popstate', this.checkUrl, false); - } else if (this._useHashChange && !this.iframe) { - addEventListener('hashchange', this.checkUrl, false); - } else if (this._wantsHashChange) { - this._checkUrlInterval = setInterval(this.checkUrl, this.interval); - } - - 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() { - // Add a cross-platform `removeEventListener` shim for older browsers. - var removeEventListener = window.removeEventListener || function(eventName, listener) { - return detachEvent('on' + eventName, listener); - }; - - // Remove window listeners. - if (this._usePushState) { - removeEventListener('popstate', this.checkUrl, false); - } else if (this._useHashChange && !this.iframe) { - removeEventListener('hashchange', this.checkUrl, false); - } - - // Clean up the iframe if necessary. - if (this.iframe) { - document.body.removeChild(this.iframe); - this.iframe = null; - } - - // Some environments will throw when clearing an undefined interval. - if (this._checkUrlInterval) 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 the user pressed the back button, the iframe's hash will have - // changed and we should use that for comparison. - if (current === this.fragment && this.iframe) { - current = this.getHash(this.iframe.contentWindow); - } - - if (current === this.fragment) return false; - if (this.iframe) this.navigate(current); - this.loadUrl(); - }, - - // 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(fragment) { - // If the root doesn't match, no routes can match either. - if (!this.matchRoot()) return false; - fragment = this.fragment = this.getFragment(fragment); - return _.some(this.handlers, function(handler) { - if (handler.route.test(fragment)) { - handler.callback(fragment); - return true; - } - }); - }, - - // 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}; - - // Normalize the fragment. - fragment = this.getFragment(fragment || ''); - - // Don't include a trailing slash on the root. - var rootPath = this.root; - if (fragment === '' || fragment.charAt(0) === '?') { - rootPath = rootPath.slice(0, -1) || '/'; - } - var url = rootPath + fragment; - - // Strip the hash and decode for matching. - fragment = this.decodeFragment(fragment.replace(pathStripper, '')); - - if (this.fragment === fragment) return; - this.fragment = fragment; - - // If pushState is available, we use it to set the fragment as a real URL. - if (this._usePushState) { - this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); - - // If hash changes haven't been explicitly disabled, update the hash - // fragment to store history. - } else if (this._wantsHashChange) { - this._updateHash(this.location, fragment, options.replace); - if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) { - var iWindow = this.iframe.contentWindow; - - // 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) { - iWindow.document.open(); - iWindow.document.close(); - } - - this._updateHash(iWindow.location, fragment, options.replace); - } - - // If you've told us that you explicitly don't want fallback hashchange- - // based history, then `navigate` becomes a page refresh. - } else { - return this.location.assign(url); - } - if (options.trigger) return 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) { - var href = location.href.replace(/(javascript:|#).*$/, ''); - location.replace(href + '#' + fragment); - } else { - // Some browsers require that `hash` contains a leading #. - location.hash = '#' + fragment; - } - } - - }); - - // Create the default Backbone.history. - Backbone.history = new History; - - // Helpers - // ------- - - // 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 extend = function(protoProps, staticProps) { - var parent = this; - 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 constructor. - if (protoProps && _.has(protoProps, 'constructor')) { - child = protoProps.constructor; - } else { - child = function(){ return parent.apply(this, arguments); }; - } - - // Add static properties to the constructor function, if supplied. - _.extend(child, parent, staticProps); - - // Set the prototype chain to inherit from `parent`, without calling - // `parent`'s constructor function and add the prototype properties. - child.prototype = _.create(parent.prototype, protoProps); - child.prototype.constructor = child; - - // Set a convenience property in case the parent's prototype is needed - // later. - child.__super__ = parent.prototype; - - return child; - }; - - // Set up inheritance for the model, collection, router, view and history. - Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; - - // 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'); - }; - - // Wrap an optional error callback with a fallback error event. - var wrapError = function(model, options) { - var error = options.error; - options.error = function(resp) { - if (error) error.call(options.context, model, resp, options); - model.trigger('error', model, resp, options); - }; - }; - - return Backbone; -}); diff --git a/scripts/lib/underscore.js b/scripts/lib/underscore.js deleted file mode 100644 index 8219dc5..0000000 --- a/scripts/lib/underscore.js +++ /dev/null @@ -1,1692 +0,0 @@ -// Underscore.js 1.9.1 -// http://underscorejs.org -// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Underscore may be freely distributed under the MIT license. - -(function() { - - // Baseline setup - // -------------- - - // Establish the root object, `window` (`self`) in the browser, `global` - // on the server, or `this` in some virtual machines. We use `self` - // instead of `window` for `WebWorker` support. - var root = typeof self == 'object' && self.self === self && self || - typeof global == 'object' && global.global === global && global || - this || - {}; - - // Save the previous value of the `_` variable. - var previousUnderscore = root._; - - // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype; - var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; - - // Create quick reference variables for speed access to core prototypes. - var push = ArrayProto.push, - slice = ArrayProto.slice, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; - - // All **ECMAScript 5** native function implementations that we hope to use - // are declared here. - var nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeCreate = Object.create; - - // Naked function reference for surrogate-prototype-swapping. - var Ctor = function(){}; - - // Create a safe reference to the Underscore object for use below. - var _ = function(obj) { - if (obj instanceof _) return obj; - if (!(this instanceof _)) return new _(obj); - this._wrapped = obj; - }; - - // Export the Underscore object for **Node.js**, with - // backwards-compatibility for their old module API. If we're in - // the browser, add `_` as a global object. - // (`nodeType` is checked to ensure that `module` - // and `exports` are not HTML elements.) - if (typeof exports != 'undefined' && !exports.nodeType) { - if (typeof module != 'undefined' && !module.nodeType && module.exports) { - exports = module.exports = _; - } - exports._ = _; - } else { - root._ = _; - } - - // Current version. - _.VERSION = '1.9.1'; - - // Internal function that returns an efficient (for current engines) version - // of the passed-in callback, to be repeatedly applied in other Underscore - // functions. - var optimizeCb = function(func, context, argCount) { - if (context === void 0) return func; - switch (argCount == null ? 3 : argCount) { - case 1: return function(value) { - return func.call(context, value); - }; - // The 2-argument case is omitted because we’re not using it. - case 3: return function(value, index, collection) { - return func.call(context, value, index, collection); - }; - case 4: return function(accumulator, value, index, collection) { - return func.call(context, accumulator, value, index, collection); - }; - } - return function() { - return func.apply(context, arguments); - }; - }; - - var builtinIteratee; - - // An internal function to generate callbacks that can be applied to each - // element in a collection, returning the desired result — either `identity`, - // an arbitrary callback, a property matcher, or a property accessor. - var cb = function(value, context, argCount) { - if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); - if (value == null) return _.identity; - if (_.isFunction(value)) return optimizeCb(value, context, argCount); - if (_.isObject(value) && !_.isArray(value)) return _.matcher(value); - return _.property(value); - }; - - // External wrapper for our callback generator. Users may customize - // `_.iteratee` if they want additional predicate/iteratee shorthand styles. - // This abstraction hides the internal-only argCount argument. - _.iteratee = builtinIteratee = function(value, context) { - return cb(value, context, Infinity); - }; - - // Some functions take a variable number of arguments, or a few expected - // arguments at the beginning and then a variable number of values to operate - // on. This helper accumulates all remaining arguments past the function’s - // argument length (or an explicit `startIndex`), into an array that becomes - // the last argument. Similar to ES6’s "rest parameter". - var restArguments = function(func, startIndex) { - startIndex = startIndex == null ? func.length - 1 : +startIndex; - return function() { - var length = Math.max(arguments.length - startIndex, 0), - rest = Array(length), - index = 0; - for (; index < length; index++) { - rest[index] = arguments[index + startIndex]; - } - switch (startIndex) { - case 0: return func.call(this, rest); - case 1: return func.call(this, arguments[0], rest); - case 2: return func.call(this, arguments[0], arguments[1], rest); - } - var args = Array(startIndex + 1); - for (index = 0; index < startIndex; index++) { - args[index] = arguments[index]; - } - args[startIndex] = rest; - return func.apply(this, args); - }; - }; - - // An internal function for creating a new object that inherits from another. - var baseCreate = function(prototype) { - if (!_.isObject(prototype)) return {}; - if (nativeCreate) return nativeCreate(prototype); - Ctor.prototype = prototype; - var result = new Ctor; - Ctor.prototype = null; - return result; - }; - - var shallowProperty = function(key) { - return function(obj) { - return obj == null ? void 0 : obj[key]; - }; - }; - - var has = function(obj, path) { - return obj != null && hasOwnProperty.call(obj, path); - } - - var deepGet = function(obj, path) { - var length = path.length; - for (var i = 0; i < length; i++) { - if (obj == null) return void 0; - obj = obj[path[i]]; - } - return length ? obj : void 0; - }; - - // Helper for collection methods to determine whether a collection - // should be iterated as an array or as an object. - // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength - // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 - var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; - var getLength = shallowProperty('length'); - var isArrayLike = function(collection) { - var length = getLength(collection); - return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; - }; - - // Collection Functions - // -------------------- - - // The cornerstone, an `each` implementation, aka `forEach`. - // Handles raw objects in addition to array-likes. Treats all - // sparse array-likes as if they were dense. - _.each = _.forEach = function(obj, iteratee, context) { - iteratee = optimizeCb(iteratee, context); - var i, length; - if (isArrayLike(obj)) { - for (i = 0, length = obj.length; i < length; i++) { - iteratee(obj[i], i, obj); - } - } else { - var keys = _.keys(obj); - for (i = 0, length = keys.length; i < length; i++) { - iteratee(obj[keys[i]], keys[i], obj); - } - } - return obj; - }; - - // Return the results of applying the iteratee to each element. - _.map = _.collect = function(obj, iteratee, context) { - iteratee = cb(iteratee, context); - var keys = !isArrayLike(obj) && _.keys(obj), - length = (keys || obj).length, - results = Array(length); - for (var index = 0; index < length; index++) { - var currentKey = keys ? keys[index] : index; - results[index] = iteratee(obj[currentKey], currentKey, obj); - } - return results; - }; - - // Create a reducing function iterating left or right. - var createReduce = function(dir) { - // Wrap code that reassigns argument variables in a separate function than - // the one that accesses `arguments.length` to avoid a perf hit. (#1991) - var reducer = function(obj, iteratee, memo, initial) { - var keys = !isArrayLike(obj) && _.keys(obj), - length = (keys || obj).length, - index = dir > 0 ? 0 : length - 1; - if (!initial) { - memo = obj[keys ? keys[index] : index]; - index += dir; - } - for (; index >= 0 && index < length; index += dir) { - var currentKey = keys ? keys[index] : index; - memo = iteratee(memo, obj[currentKey], currentKey, obj); - } - return memo; - }; - - return function(obj, iteratee, memo, context) { - var initial = arguments.length >= 3; - return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); - }; - }; - - // **Reduce** builds up a single result from a list of values, aka `inject`, - // or `foldl`. - _.reduce = _.foldl = _.inject = createReduce(1); - - // The right-associative version of reduce, also known as `foldr`. - _.reduceRight = _.foldr = createReduce(-1); - - // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, predicate, context) { - var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey; - var key = keyFinder(obj, predicate, context); - if (key !== void 0 && key !== -1) return obj[key]; - }; - - // Return all the elements that pass a truth test. - // Aliased as `select`. - _.filter = _.select = function(obj, predicate, context) { - var results = []; - predicate = cb(predicate, context); - _.each(obj, function(value, index, list) { - if (predicate(value, index, list)) results.push(value); - }); - return results; - }; - - // Return all the elements for which a truth test fails. - _.reject = function(obj, predicate, context) { - return _.filter(obj, _.negate(cb(predicate)), context); - }; - - // Determine whether all of the elements match a truth test. - // Aliased as `all`. - _.every = _.all = function(obj, predicate, context) { - predicate = cb(predicate, context); - var keys = !isArrayLike(obj) && _.keys(obj), - length = (keys || obj).length; - for (var index = 0; index < length; index++) { - var currentKey = keys ? keys[index] : index; - if (!predicate(obj[currentKey], currentKey, obj)) return false; - } - return true; - }; - - // Determine if at least one element in the object matches a truth test. - // Aliased as `any`. - _.some = _.any = function(obj, predicate, context) { - predicate = cb(predicate, context); - var keys = !isArrayLike(obj) && _.keys(obj), - length = (keys || obj).length; - for (var index = 0; index < length; index++) { - var currentKey = keys ? keys[index] : index; - if (predicate(obj[currentKey], currentKey, obj)) return true; - } - return false; - }; - - // Determine if the array or object contains a given item (using `===`). - // Aliased as `includes` and `include`. - _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { - if (!isArrayLike(obj)) obj = _.values(obj); - if (typeof fromIndex != 'number' || guard) fromIndex = 0; - return _.indexOf(obj, item, fromIndex) >= 0; - }; - - // Invoke a method (with arguments) on every item in a collection. - _.invoke = restArguments(function(obj, path, args) { - var contextPath, func; - if (_.isFunction(path)) { - func = path; - } else if (_.isArray(path)) { - contextPath = path.slice(0, -1); - path = path[path.length - 1]; - } - return _.map(obj, function(context) { - var method = func; - if (!method) { - if (contextPath && contextPath.length) { - context = deepGet(context, contextPath); - } - if (context == null) return void 0; - method = context[path]; - } - return method == null ? method : method.apply(context, args); - }); - }); - - // Convenience version of a common use case of `map`: fetching a property. - _.pluck = function(obj, key) { - return _.map(obj, _.property(key)); - }; - - // Convenience version of a common use case of `filter`: selecting only objects - // containing specific `key:value` pairs. - _.where = function(obj, attrs) { - return _.filter(obj, _.matcher(attrs)); - }; - - // Convenience version of a common use case of `find`: getting the first object - // containing specific `key:value` pairs. - _.findWhere = function(obj, attrs) { - return _.find(obj, _.matcher(attrs)); - }; - - // Return the maximum element (or element-based computation). - _.max = function(obj, iteratee, context) { - var result = -Infinity, lastComputed = -Infinity, - value, computed; - if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) { - obj = isArrayLike(obj) ? obj : _.values(obj); - for (var i = 0, length = obj.length; i < length; i++) { - value = obj[i]; - if (value != null && value > result) { - result = value; - } - } - } else { - iteratee = cb(iteratee, context); - _.each(obj, function(v, index, list) { - computed = iteratee(v, index, list); - if (computed > lastComputed || computed === -Infinity && result === -Infinity) { - result = v; - lastComputed = computed; - } - }); - } - return result; - }; - - // Return the minimum element (or element-based computation). - _.min = function(obj, iteratee, context) { - var result = Infinity, lastComputed = Infinity, - value, computed; - if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) { - obj = isArrayLike(obj) ? obj : _.values(obj); - for (var i = 0, length = obj.length; i < length; i++) { - value = obj[i]; - if (value != null && value < result) { - result = value; - } - } - } else { - iteratee = cb(iteratee, context); - _.each(obj, function(v, index, list) { - computed = iteratee(v, index, list); - if (computed < lastComputed || computed === Infinity && result === Infinity) { - result = v; - lastComputed = computed; - } - }); - } - return result; - }; - - // Shuffle a collection. - _.shuffle = function(obj) { - return _.sample(obj, Infinity); - }; - - // Sample **n** random values from a collection using the modern version of the - // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). - // If **n** is not specified, returns a single random element. - // The internal `guard` argument allows it to work with `map`. - _.sample = function(obj, n, guard) { - if (n == null || guard) { - if (!isArrayLike(obj)) obj = _.values(obj); - return obj[_.random(obj.length - 1)]; - } - var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj); - var length = getLength(sample); - n = Math.max(Math.min(n, length), 0); - var last = length - 1; - for (var index = 0; index < n; index++) { - var rand = _.random(index, last); - var temp = sample[index]; - sample[index] = sample[rand]; - sample[rand] = temp; - } - return sample.slice(0, n); - }; - - // Sort the object's values by a criterion produced by an iteratee. - _.sortBy = function(obj, iteratee, context) { - var index = 0; - iteratee = cb(iteratee, context); - return _.pluck(_.map(obj, function(value, key, list) { - return { - value: value, - index: index++, - criteria: iteratee(value, key, list) - }; - }).sort(function(left, right) { - var a = left.criteria; - var b = right.criteria; - if (a !== b) { - if (a > b || a === void 0) return 1; - if (a < b || b === void 0) return -1; - } - return left.index - right.index; - }), 'value'); - }; - - // An internal function used for aggregate "group by" operations. - var group = function(behavior, partition) { - return function(obj, iteratee, context) { - var result = partition ? [[], []] : {}; - iteratee = cb(iteratee, context); - _.each(obj, function(value, index) { - var key = iteratee(value, index, obj); - behavior(result, value, key); - }); - return result; - }; - }; - - // Groups the object's values by a criterion. Pass either a string attribute - // to group by, or a function that returns the criterion. - _.groupBy = group(function(result, value, key) { - if (has(result, key)) result[key].push(value); else result[key] = [value]; - }); - - // Indexes the object's values by a criterion, similar to `groupBy`, but for - // when you know that your index values will be unique. - _.indexBy = group(function(result, value, key) { - result[key] = value; - }); - - // Counts instances of an object that group by a certain criterion. Pass - // either a string attribute to count by, or a function that returns the - // criterion. - _.countBy = group(function(result, value, key) { - if (has(result, key)) result[key]++; else result[key] = 1; - }); - - var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g; - // Safely create a real, live array from anything iterable. - _.toArray = function(obj) { - if (!obj) return []; - if (_.isArray(obj)) return slice.call(obj); - if (_.isString(obj)) { - // Keep surrogate pair characters together - return obj.match(reStrSymbol); - } - if (isArrayLike(obj)) return _.map(obj, _.identity); - return _.values(obj); - }; - - // Return the number of elements in an object. - _.size = function(obj) { - if (obj == null) return 0; - return isArrayLike(obj) ? obj.length : _.keys(obj).length; - }; - - // Split a collection into two arrays: one whose elements all satisfy the given - // predicate, and one whose elements all do not satisfy the predicate. - _.partition = group(function(result, value, pass) { - result[pass ? 0 : 1].push(value); - }, true); - - // Array Functions - // --------------- - - // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head` and `take`. The **guard** check - // allows it to work with `_.map`. - _.first = _.head = _.take = function(array, n, guard) { - if (array == null || array.length < 1) return n == null ? void 0 : []; - if (n == null || guard) return array[0]; - return _.initial(array, array.length - n); - }; - - // Returns everything but the last entry of the array. Especially useful on - // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. - _.initial = function(array, n, guard) { - return slice.call(array, 0, Math.max(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. - _.last = function(array, n, guard) { - if (array == null || array.length < 1) return n == null ? void 0 : []; - if (n == null || guard) return array[array.length - 1]; - return _.rest(array, Math.max(0, array.length - n)); - }; - - // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. - // Especially useful on the arguments object. Passing an **n** will return - // the rest N values in the array. - _.rest = _.tail = _.drop = function(array, n, guard) { - return slice.call(array, n == null || guard ? 1 : n); - }; - - // Trim out all falsy values from an array. - _.compact = function(array) { - return _.filter(array, Boolean); - }; - - // Internal implementation of a recursive `flatten` function. - var flatten = function(input, shallow, strict, output) { - output = output || []; - var idx = output.length; - for (var i = 0, length = getLength(input); i < length; i++) { - var value = input[i]; - if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { - // Flatten current level of array or arguments object. - if (shallow) { - var j = 0, len = value.length; - while (j < len) output[idx++] = value[j++]; - } else { - flatten(value, shallow, strict, output); - idx = output.length; - } - } else if (!strict) { - output[idx++] = value; - } - } - return output; - }; - - // Flatten out an array, either recursively (by default), or just one level. - _.flatten = function(array, shallow) { - return flatten(array, shallow, false); - }; - - // Return a version of the array that does not contain the specified value(s). - _.without = restArguments(function(array, otherArrays) { - return _.difference(array, otherArrays); - }); - - // Produce a duplicate-free version of the array. If the array has already - // been sorted, you have the option of using a faster algorithm. - // The faster algorithm will not work with an iteratee if the iteratee - // is not a one-to-one function, so providing an iteratee will disable - // the faster algorithm. - // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iteratee, context) { - if (!_.isBoolean(isSorted)) { - context = iteratee; - iteratee = isSorted; - isSorted = false; - } - if (iteratee != null) iteratee = cb(iteratee, context); - var result = []; - var seen = []; - for (var i = 0, length = getLength(array); i < length; i++) { - var value = array[i], - computed = iteratee ? iteratee(value, i, array) : value; - if (isSorted && !iteratee) { - if (!i || seen !== computed) result.push(value); - seen = computed; - } else if (iteratee) { - if (!_.contains(seen, computed)) { - seen.push(computed); - result.push(value); - } - } else if (!_.contains(result, value)) { - result.push(value); - } - } - return result; - }; - - // Produce an array that contains the union: each distinct element from all of - // the passed-in arrays. - _.union = restArguments(function(arrays) { - return _.uniq(flatten(arrays, true, true)); - }); - - // Produce an array that contains every item shared between all the - // passed-in arrays. - _.intersection = function(array) { - var result = []; - var argsLength = arguments.length; - for (var i = 0, length = getLength(array); i < length; i++) { - var item = array[i]; - if (_.contains(result, item)) continue; - var j; - for (j = 1; j < argsLength; j++) { - if (!_.contains(arguments[j], item)) break; - } - if (j === argsLength) result.push(item); - } - return result; - }; - - // Take the difference between one array and a number of other arrays. - // Only the elements present in just the first array will remain. - _.difference = restArguments(function(array, rest) { - rest = flatten(rest, true, true); - return _.filter(array, function(value){ - return !_.contains(rest, value); - }); - }); - - // Complement of _.zip. Unzip accepts an array of arrays and groups - // each array's elements on shared indices. - _.unzip = function(array) { - var length = array && _.max(array, getLength).length || 0; - var result = Array(length); - - for (var index = 0; index < length; index++) { - result[index] = _.pluck(array, index); - } - return result; - }; - - // Zip together multiple lists into a single array -- elements that share - // an index go together. - _.zip = restArguments(_.unzip); - - // Converts lists into objects. Pass either a single array of `[key, value]` - // pairs, or two parallel arrays of the same length -- one of keys, and one of - // the corresponding values. Passing by pairs is the reverse of _.pairs. - _.object = function(list, values) { - var result = {}; - for (var i = 0, length = getLength(list); i < length; i++) { - if (values) { - result[list[i]] = values[i]; - } else { - result[list[i][0]] = list[i][1]; - } - } - return result; - }; - - // Generator function to create the findIndex and findLastIndex functions. - var createPredicateIndexFinder = function(dir) { - return function(array, predicate, context) { - predicate = cb(predicate, context); - var length = getLength(array); - var index = dir > 0 ? 0 : length - 1; - for (; index >= 0 && index < length; index += dir) { - if (predicate(array[index], index, array)) return index; - } - return -1; - }; - }; - - // Returns the first index on an array-like that passes a predicate test. - _.findIndex = createPredicateIndexFinder(1); - _.findLastIndex = createPredicateIndexFinder(-1); - - // Use a comparator function to figure out the smallest index at which - // an object should be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iteratee, context) { - iteratee = cb(iteratee, context, 1); - var value = iteratee(obj); - var low = 0, high = getLength(array); - while (low < high) { - var mid = Math.floor((low + high) / 2); - if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; - } - return low; - }; - - // Generator function to create the indexOf and lastIndexOf functions. - var createIndexFinder = function(dir, predicateFind, sortedIndex) { - return function(array, item, idx) { - var i = 0, length = getLength(array); - if (typeof idx == 'number') { - if (dir > 0) { - i = idx >= 0 ? idx : Math.max(idx + length, i); - } else { - length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; - } - } else if (sortedIndex && idx && length) { - idx = sortedIndex(array, item); - return array[idx] === item ? idx : -1; - } - if (item !== item) { - idx = predicateFind(slice.call(array, i, length), _.isNaN); - return idx >= 0 ? idx + i : -1; - } - for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { - if (array[idx] === item) return idx; - } - return -1; - }; - }; - - // Return the position of the first occurrence of an item in an array, - // or -1 if the item is not included in the array. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); - _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); - - // 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 (stop == null) { - stop = start || 0; - start = 0; - } - if (!step) { - step = stop < start ? -1 : 1; - } - - var length = Math.max(Math.ceil((stop - start) / step), 0); - var range = Array(length); - - for (var idx = 0; idx < length; idx++, start += step) { - range[idx] = start; - } - - return range; - }; - - // Chunk a single array into multiple arrays, each containing `count` or fewer - // items. - _.chunk = function(array, count) { - if (count == null || count < 1) return []; - var result = []; - var i = 0, length = array.length; - while (i < length) { - result.push(slice.call(array, i, i += count)); - } - return result; - }; - - // Function (ahem) Functions - // ------------------ - - // Determines whether to execute a function as a constructor - // or a normal function with the provided arguments. - var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { - if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); - var self = baseCreate(sourceFunc.prototype); - var result = sourceFunc.apply(self, args); - if (_.isObject(result)) return result; - return self; - }; - - // Create a function bound to a given object (assigning `this`, and arguments, - // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if - // available. - _.bind = restArguments(function(func, context, args) { - if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); - var bound = restArguments(function(callArgs) { - return executeBound(func, bound, context, this, args.concat(callArgs)); - }); - return bound; - }); - - // Partially apply a function by creating a version that has had some of its - // arguments pre-filled, without changing its dynamic `this` context. _ acts - // as a placeholder by default, allowing any combination of arguments to be - // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. - _.partial = restArguments(function(func, boundArgs) { - var placeholder = _.partial.placeholder; - var bound = function() { - var position = 0, length = boundArgs.length; - var args = Array(length); - for (var i = 0; i < length; i++) { - args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i]; - } - while (position < arguments.length) args.push(arguments[position++]); - return executeBound(func, bound, this, this, args); - }; - return bound; - }); - - _.partial.placeholder = _; - - // Bind a number of an object's methods to that object. Remaining arguments - // are the method names to be bound. Useful for ensuring that all callbacks - // defined on an object belong to it. - _.bindAll = restArguments(function(obj, keys) { - keys = flatten(keys, false, false); - var index = keys.length; - if (index < 1) throw new Error('bindAll must be passed function names'); - while (index--) { - var key = keys[index]; - obj[key] = _.bind(obj[key], obj); - } - }); - - // Memoize an expensive function by storing its results. - _.memoize = function(func, hasher) { - var memoize = function(key) { - var cache = memoize.cache; - var address = '' + (hasher ? hasher.apply(this, arguments) : key); - if (!has(cache, address)) cache[address] = func.apply(this, arguments); - return cache[address]; - }; - memoize.cache = {}; - return memoize; - }; - - // Delays a function for the given number of milliseconds, and then calls - // it with the arguments supplied. - _.delay = restArguments(function(func, wait, args) { - return setTimeout(function() { - return func.apply(null, args); - }, wait); - }); - - // Defers a function, scheduling it to run after the current call stack has - // cleared. - _.defer = _.partial(_.delay, _, 1); - - // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. Normally, the throttled function will run - // as much as it can, without ever going more than once per `wait` duration; - // but if you'd like to disable the execution on the leading edge, pass - // `{leading: false}`. To disable execution on the trailing edge, ditto. - _.throttle = function(func, wait, options) { - var timeout, context, args, result; - var previous = 0; - if (!options) options = {}; - - var later = function() { - previous = options.leading === false ? 0 : _.now(); - timeout = null; - result = func.apply(context, args); - if (!timeout) context = args = null; - }; - - var throttled = function() { - var now = _.now(); - if (!previous && options.leading === false) previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0 || remaining > wait) { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - previous = now; - result = func.apply(context, args); - if (!timeout) context = args = null; - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); - } - return result; - }; - - throttled.cancel = function() { - clearTimeout(timeout); - previous = 0; - timeout = context = args = null; - }; - - return throttled; - }; - - // 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. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - _.debounce = function(func, wait, immediate) { - var timeout, result; - - var later = function(context, args) { - timeout = null; - if (args) result = func.apply(context, args); - }; - - var debounced = restArguments(function(args) { - if (timeout) clearTimeout(timeout); - if (immediate) { - var callNow = !timeout; - timeout = setTimeout(later, wait); - if (callNow) result = func.apply(this, args); - } else { - timeout = _.delay(later, wait, this, args); - } - - return result; - }); - - debounced.cancel = function() { - clearTimeout(timeout); - timeout = null; - }; - - return debounced; - }; - - // 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 _.partial(wrapper, func); - }; - - // Returns a negated version of the passed-in predicate. - _.negate = function(predicate) { - return function() { - return !predicate.apply(this, arguments); - }; - }; - - // 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 args = arguments; - var start = args.length - 1; - return function() { - var i = start; - var result = args[start].apply(this, arguments); - while (i--) result = args[i].call(this, result); - return result; - }; - }; - - // Returns a function that will only be executed on and after the Nth call. - _.after = function(times, func) { - return function() { - if (--times < 1) { - return func.apply(this, arguments); - } - }; - }; - - // Returns a function that will only be executed up to (but not including) the Nth call. - _.before = function(times, func) { - var memo; - return function() { - if (--times > 0) { - memo = func.apply(this, arguments); - } - if (times <= 1) func = null; - return memo; - }; - }; - - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = _.partial(_.before, 2); - - _.restArguments = restArguments; - - // Object Functions - // ---------------- - - // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. - var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); - var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', - 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; - - var collectNonEnumProps = function(obj, keys) { - var nonEnumIdx = nonEnumerableProps.length; - var constructor = obj.constructor; - var proto = _.isFunction(constructor) && constructor.prototype || ObjProto; - - // Constructor is a special case. - var prop = 'constructor'; - if (has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); - - while (nonEnumIdx--) { - prop = nonEnumerableProps[nonEnumIdx]; - if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { - keys.push(prop); - } - } - }; - - // Retrieve the names of an object's own properties. - // Delegates to **ECMAScript 5**'s native `Object.keys`. - _.keys = function(obj) { - if (!_.isObject(obj)) return []; - if (nativeKeys) return nativeKeys(obj); - var keys = []; - for (var key in obj) if (has(obj, key)) keys.push(key); - // Ahem, IE < 9. - if (hasEnumBug) collectNonEnumProps(obj, keys); - return keys; - }; - - // Retrieve all the property names of an object. - _.allKeys = function(obj) { - if (!_.isObject(obj)) return []; - var keys = []; - for (var key in obj) keys.push(key); - // Ahem, IE < 9. - if (hasEnumBug) collectNonEnumProps(obj, keys); - return keys; - }; - - // Retrieve the values of an object's properties. - _.values = function(obj) { - var keys = _.keys(obj); - var length = keys.length; - var values = Array(length); - for (var i = 0; i < length; i++) { - values[i] = obj[keys[i]]; - } - return values; - }; - - // Returns the results of applying the iteratee to each element of the object. - // In contrast to _.map it returns an object. - _.mapObject = function(obj, iteratee, context) { - iteratee = cb(iteratee, context); - var keys = _.keys(obj), - length = keys.length, - results = {}; - for (var index = 0; index < length; index++) { - var currentKey = keys[index]; - results[currentKey] = iteratee(obj[currentKey], currentKey, obj); - } - return results; - }; - - // Convert an object into a list of `[key, value]` pairs. - // The opposite of _.object. - _.pairs = function(obj) { - var keys = _.keys(obj); - var length = keys.length; - var pairs = Array(length); - for (var i = 0; i < length; i++) { - pairs[i] = [keys[i], obj[keys[i]]]; - } - return pairs; - }; - - // Invert the keys and values of an object. The values must be serializable. - _.invert = function(obj) { - var result = {}; - var keys = _.keys(obj); - for (var i = 0, length = keys.length; i < length; i++) { - result[obj[keys[i]]] = keys[i]; - } - return result; - }; - - // 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(); - }; - - // An internal function for creating assigner functions. - var createAssigner = function(keysFunc, defaults) { - return function(obj) { - var length = arguments.length; - if (defaults) obj = Object(obj); - if (length < 2 || obj == null) return obj; - for (var index = 1; index < length; index++) { - var source = arguments[index], - keys = keysFunc(source), - l = keys.length; - for (var i = 0; i < l; i++) { - var key = keys[i]; - if (!defaults || obj[key] === void 0) obj[key] = source[key]; - } - } - return obj; - }; - }; - - // Extend a given object with all the properties in passed-in object(s). - _.extend = createAssigner(_.allKeys); - - // Assigns a given object with all the own properties in the passed-in object(s). - // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) - _.extendOwn = _.assign = createAssigner(_.keys); - - // Returns the first key on an object that passes a predicate test. - _.findKey = function(obj, predicate, context) { - predicate = cb(predicate, context); - var keys = _.keys(obj), key; - for (var i = 0, length = keys.length; i < length; i++) { - key = keys[i]; - if (predicate(obj[key], key, obj)) return key; - } - }; - - // Internal pick helper function to determine if `obj` has key `key`. - var keyInObj = function(value, key, obj) { - return key in obj; - }; - - // Return a copy of the object only containing the whitelisted properties. - _.pick = restArguments(function(obj, keys) { - var result = {}, iteratee = keys[0]; - if (obj == null) return result; - if (_.isFunction(iteratee)) { - if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]); - keys = _.allKeys(obj); - } else { - iteratee = keyInObj; - keys = flatten(keys, false, false); - obj = Object(obj); - } - for (var i = 0, length = keys.length; i < length; i++) { - var key = keys[i]; - var value = obj[key]; - if (iteratee(value, key, obj)) result[key] = value; - } - return result; - }); - - // Return a copy of the object without the blacklisted properties. - _.omit = restArguments(function(obj, keys) { - var iteratee = keys[0], context; - if (_.isFunction(iteratee)) { - iteratee = _.negate(iteratee); - if (keys.length > 1) context = keys[1]; - } else { - keys = _.map(flatten(keys, false, false), String); - iteratee = function(value, key) { - return !_.contains(keys, key); - }; - } - return _.pick(obj, iteratee, context); - }); - - // Fill in a given object with default properties. - _.defaults = createAssigner(_.allKeys, true); - - // Creates an object that inherits from the given prototype object. - // If additional properties are provided then they will be added to the - // created object. - _.create = function(prototype, props) { - var result = baseCreate(prototype); - if (props) _.extendOwn(result, props); - return result; - }; - - // 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; - }; - - // Returns whether an object has a given set of `key:value` pairs. - _.isMatch = function(object, attrs) { - var keys = _.keys(attrs), length = keys.length; - if (object == null) return !length; - var obj = Object(object); - for (var i = 0; i < length; i++) { - var key = keys[i]; - if (attrs[key] !== obj[key] || !(key in obj)) return false; - } - return true; - }; - - - // Internal recursive comparison function for `isEqual`. - var eq, deepEq; - eq = function(a, b, aStack, bStack) { - // 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; - // `null` or `undefined` only equal to itself (strict comparison). - if (a == null || b == null) return false; - // `NaN`s are equivalent, but non-reflexive. - if (a !== a) return b !== b; - // Exhaust primitive checks - var type = typeof a; - if (type !== 'function' && type !== 'object' && typeof b != 'object') return false; - return deepEq(a, b, aStack, bStack); - }; - - // Internal recursive comparison function for `isEqual`. - deepEq = function(a, b, aStack, bStack) { - // Unwrap any wrapped objects. - if (a instanceof _) a = a._wrapped; - if (b instanceof _) b = b._wrapped; - // Compare `[[Class]]` names. - var className = toString.call(a); - if (className !== toString.call(b)) return false; - switch (className) { - // Strings, numbers, regular expressions, dates, and booleans are compared by value. - case '[object RegExp]': - // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - return '' + a === '' + b; - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. - // Object(NaN) is equivalent to NaN. - if (+a !== +a) return +b !== +b; - // An `egal` comparison is performed for other numeric values. - return +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; - case '[object Symbol]': - return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b); - } - - var areArrays = className === '[object Array]'; - if (!areArrays) { - if (typeof a != 'object' || typeof b != 'object') return false; - - // Objects with different constructors are not equivalent, but `Object`s or `Array`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && - _.isFunction(bCtor) && bCtor instanceof bCtor) - && ('constructor' in a && 'constructor' in b)) { - 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`. - - // Initializing stack of traversed objects. - // It's done here since we only need them for objects and arrays comparison. - aStack = aStack || []; - bStack = bStack || []; - var length = aStack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (aStack[length] === a) return bStack[length] === b; - } - - // Add the first object to the stack of traversed objects. - aStack.push(a); - bStack.push(b); - - // Recursively compare objects and arrays. - if (areArrays) { - // Compare array lengths to determine if a deep comparison is necessary. - length = a.length; - if (length !== b.length) return false; - // Deep compare the contents, ignoring non-numeric properties. - while (length--) { - if (!eq(a[length], b[length], aStack, bStack)) return false; - } - } else { - // Deep compare objects. - var keys = _.keys(a), key; - length = keys.length; - // Ensure that both objects contain the same number of properties before comparing deep equality. - if (_.keys(b).length !== length) return false; - while (length--) { - // Deep compare each member - key = keys[length]; - if (!(has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; - } - } - // Remove the first object from the stack of traversed objects. - aStack.pop(); - bStack.pop(); - return true; - }; - - // 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 (obj == null) return true; - if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; - return _.keys(obj).length === 0; - }; - - // 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) { - var type = typeof obj; - return type === 'function' || type === 'object' && !!obj; - }; - - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet. - _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) { - _['is' + name] = function(obj) { - return toString.call(obj) === '[object ' + name + ']'; - }; - }); - - // Define a fallback version of the method in browsers (ahem, IE < 9), where - // there isn't any inspectable "Arguments" type. - if (!_.isArguments(arguments)) { - _.isArguments = function(obj) { - return has(obj, 'callee'); - }; - } - - // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, - // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236). - var nodelist = root.document && root.document.childNodes; - if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') { - _.isFunction = function(obj) { - return typeof obj == 'function' || false; - }; - } - - // Is a given object a finite number? - _.isFinite = function(obj) { - return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj)); - }; - - // Is the given value `NaN`? - _.isNaN = function(obj) { - return _.isNumber(obj) && isNaN(obj); - }; - - // Is a given value a boolean? - _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; - }; - - // 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; - }; - - // Shortcut function for checking if an object has a given property directly - // on itself (in other words, not on a prototype). - _.has = function(obj, path) { - if (!_.isArray(path)) { - return has(obj, path); - } - var length = path.length; - for (var i = 0; i < length; i++) { - var key = path[i]; - if (obj == null || !hasOwnProperty.call(obj, key)) { - return false; - } - obj = obj[key]; - } - return !!length; - }; - - // 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 iteratees. - _.identity = function(value) { - return value; - }; - - // Predicate-generating functions. Often useful outside of Underscore. - _.constant = function(value) { - return function() { - return value; - }; - }; - - _.noop = function(){}; - - // Creates a function that, when passed an object, will traverse that object’s - // properties down the given `path`, specified as an array of keys or indexes. - _.property = function(path) { - if (!_.isArray(path)) { - return shallowProperty(path); - } - return function(obj) { - return deepGet(obj, path); - }; - }; - - // Generates a function for a given object that returns a given property. - _.propertyOf = function(obj) { - if (obj == null) { - return function(){}; - } - return function(path) { - return !_.isArray(path) ? obj[path] : deepGet(obj, path); - }; - }; - - // Returns a predicate for checking whether an object has a given set of - // `key:value` pairs. - _.matcher = _.matches = function(attrs) { - attrs = _.extendOwn({}, attrs); - return function(obj) { - return _.isMatch(obj, attrs); - }; - }; - - // Run a function **n** times. - _.times = function(n, iteratee, context) { - var accum = Array(Math.max(0, n)); - iteratee = optimizeCb(iteratee, context, 1); - for (var i = 0; i < n; i++) accum[i] = iteratee(i); - return accum; - }; - - // Return a random integer between min and max (inclusive). - _.random = function(min, max) { - if (max == null) { - max = min; - min = 0; - } - return min + Math.floor(Math.random() * (max - min + 1)); - }; - - // A (possibly faster) way to get the current timestamp as an integer. - _.now = Date.now || function() { - return new Date().getTime(); - }; - - // List of HTML entities for escaping. - var escapeMap = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '`': '`' - }; - var unescapeMap = _.invert(escapeMap); - - // Functions for escaping and unescaping strings to/from HTML interpolation. - var createEscaper = function(map) { - var escaper = function(match) { - return map[match]; - }; - // Regexes for identifying a key that needs to be escaped. - var source = '(?:' + _.keys(map).join('|') + ')'; - var testRegexp = RegExp(source); - var replaceRegexp = RegExp(source, 'g'); - return function(string) { - string = string == null ? '' : '' + string; - return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; - }; - }; - _.escape = createEscaper(escapeMap); - _.unescape = createEscaper(unescapeMap); - - // Traverses the children of `obj` along `path`. If a child is a function, it - // is invoked with its parent as context. Returns the value of the final - // child, or `fallback` if any child is undefined. - _.result = function(obj, path, fallback) { - if (!_.isArray(path)) path = [path]; - var length = path.length; - if (!length) { - return _.isFunction(fallback) ? fallback.call(obj) : fallback; - } - for (var i = 0; i < length; i++) { - var prop = obj == null ? void 0 : obj[path[i]]; - if (prop === void 0) { - prop = fallback; - i = length; // Ensure we don't continue iterating. - } - obj = _.isFunction(prop) ? prop.call(obj) : prop; - } - return obj; - }; - - // 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 = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g; - - var escapeChar = function(match) { - return '\\' + escapes[match]; - }; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - // NB: `oldSettings` only exists for backwards compatibility. - _.template = function(text, settings, oldSettings) { - if (!settings && oldSettings) settings = oldSettings; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset).replace(escapeRegExp, escapeChar); - index = offset + match.length; - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } else if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } else if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - - // Adobe VMs need the match returned to produce the correct offset. - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + 'return __p;\n'; - - var render; - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled source as a convenience for precompilation. - var argument = settings.variable || 'obj'; - template.source = 'function(' + argument + '){\n' + source + '}'; - - return template; - }; - - // Add a "chain" function. Start chaining a wrapped Underscore object. - _.chain = function(obj) { - var instance = _(obj); - instance._chain = true; - return instance; - }; - - // OOP - // --------------- - // 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. - - // Helper function to continue chaining intermediate results. - var chainResult = function(instance, obj) { - return instance._chain ? _(obj).chain() : obj; - }; - - // Add your own custom functions to the Underscore object. - _.mixin = function(obj) { - _.each(_.functions(obj), function(name) { - var func = _[name] = obj[name]; - _.prototype[name] = function() { - var args = [this._wrapped]; - push.apply(args, arguments); - return chainResult(this, func.apply(_, args)); - }; - }); - return _; - }; - - // 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]; - _.prototype[name] = function() { - var obj = this._wrapped; - method.apply(obj, arguments); - if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; - return chainResult(this, obj); - }; - }); - - // Add all accessor Array functions to the wrapper. - _.each(['concat', 'join', 'slice'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - return chainResult(this, method.apply(this._wrapped, arguments)); - }; - }); - - // Extracts the result from a wrapped and chained object. - _.prototype.value = function() { - return this._wrapped; - }; - - // Provide unwrapping proxy for some methods used in engine operations - // such as arithmetic and JSON stringification. - _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; - - _.prototype.toString = function() { - return String(this._wrapped); - }; - - // AMD registration happens at the end for compatibility with AMD loaders - // that may not enforce next-turn semantics on modules. Even though general - // practice for AMD registration is to be anonymous, underscore registers - // as a named module because, like jQuery, it is a base library that is - // popular enough to be bundled in a third party lib, but not be part of - // an AMD load request. Those cases could generate an error when an - // anonymous define() is called outside of a loader request. - if (typeof define == 'function' && define.amd) { - define('underscore', [], function() { - return _; - }); - } -}()); diff --git a/scripts/lib/vue.js b/scripts/lib/vue.js new file mode 100644 index 0000000..88d54a4 --- /dev/null +++ b/scripts/lib/vue.js @@ -0,0 +1,6 @@ +/*! + * Vue.js v2.5.17 + * (c) 2014-2018 Evan You + * Released under the MIT License. + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";var y=Object.freeze({});function M(e){return null==e}function D(e){return null!=e}function S(e){return!0===e}function T(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function P(e){return null!==e&&"object"==typeof e}var r=Object.prototype.toString;function l(e){return"[object Object]"===r.call(e)}function i(e){var t=parseFloat(String(e));return 0<=t&&Math.floor(t)===t&&isFinite(e)}function t(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function F(e){var t=parseFloat(e);return isNaN(t)?e:t}function s(e,t){for(var n=Object.create(null),r=e.split(","),i=0;ie.id;)n--;bt.splice(n+1,0,e)}else bt.push(e);Ct||(Ct=!0,Ze(At))}}(this)},St.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||P(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Fe(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},St.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},St.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},St.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||f(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var Tt={enumerable:!0,configurable:!0,get:$,set:$};function Et(e,t,n){Tt.get=function(){return this[t][n]},Tt.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Tt)}function jt(e){e._watchers=[];var t=e.$options;t.props&&function(n,r){var i=n.$options.propsData||{},o=n._props={},a=n.$options._propKeys=[];n.$parent&&ge(!1);var e=function(e){a.push(e);var t=Ie(e,r,i,n);Ce(o,e,t),e in n||Et(n,"_props",e)};for(var t in r)e(t);ge(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?$:v(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){se();try{return e.call(t,t)}catch(e){return Fe(e,t,"data()"),{}}finally{ce()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&p(r,o)||(void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&Et(e,"_data",o))}var a;we(t,!0)}(e):we(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=Y();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new St(e,a||$,$,Nt)),i in e||Lt(e,i,o)}}(e,t.computed),t.watch&&t.watch!==G&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;iparseInt(this.max)&&bn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};$n=hn,Cn={get:function(){return j}},Object.defineProperty($n,"config",Cn),$n.util={warn:re,extend:m,mergeOptions:Ne,defineReactive:Ce},$n.set=xe,$n.delete=ke,$n.nextTick=Ze,$n.options=Object.create(null),k.forEach(function(e){$n.options[e+"s"]=Object.create(null)}),m(($n.options._base=$n).options.components,kn),$n.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(-1=a&&l()};setTimeout(function(){c\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,oo="[a-zA-Z_][\\w\\-\\.]*",ao="((?:"+oo+"\\:)?"+oo+")",so=new RegExp("^<"+ao),co=/^\s*(\/?)>/,lo=new RegExp("^<\\/"+ao+"[^>]*>"),uo=/^]+>/i,fo=/^",""":'"',"&":"&"," ":"\n"," ":"\t"},go=/&(?:lt|gt|quot|amp);/g,_o=/&(?:lt|gt|quot|amp|#10|#9);/g,bo=s("pre,textarea",!0),$o=function(e,t){return e&&bo(e)&&"\n"===t[0]};var wo,Co,xo,ko,Ao,Oo,So,To,Eo=/^@|^v-on:/,jo=/^v-|^@|^:/,No=/([^]*?)\s+(?:in|of)\s+([^]*)/,Lo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Io=/^\(|\)$/g,Mo=/:(.*)$/,Do=/^:|^v-bind:/,Po=/\.[^.]+/g,Fo=e(eo);function Ro(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,r=e.length;n]*>)","i")),n=i.replace(t,function(e,t,n){return r=n.length,ho(o)||"noscript"===o||(t=t.replace(//g,"$1").replace(//g,"$1")),$o(o,t)&&(t=t.slice(1)),d.chars&&d.chars(t),""});a+=i.length-n.length,i=n,A(o,a-r,a)}else{var s=i.indexOf("<");if(0===s){if(fo.test(i)){var c=i.indexOf("--\x3e");if(0<=c){d.shouldKeepComment&&d.comment(i.substring(4,c)),C(c+3);continue}}if(po.test(i)){var l=i.indexOf("]>");if(0<=l){C(l+2);continue}}var u=i.match(uo);if(u){C(u[0].length);continue}var f=i.match(lo);if(f){var p=a;C(f[0].length),A(f[1],p,a);continue}var _=x();if(_){k(_),$o(v,i)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(0<=s){for($=i.slice(s);!(lo.test($)||so.test($)||fo.test($)||po.test($)||(w=$.indexOf("<",1))<0);)s+=w,$=i.slice(s);b=i.substring(0,s),C(s)}s<0&&(b=i,i=""),d.chars&&b&&d.chars(b)}if(i===e){d.chars&&d.chars(i);break}}function C(e){a+=e,i=i.substring(e)}function x(){var e=i.match(so);if(e){var t,n,r={tagName:e[1],attrs:[],start:a};for(C(e[0].length);!(t=i.match(co))&&(n=i.match(io));)C(n[0].length),r.attrs.push(n);if(t)return r.unarySlash=t[1],C(t[0].length),r.end=a,r}}function k(e){var t=e.tagName,n=e.unarySlash;m&&("p"===v&&ro(t)&&A(v),g(t)&&v===t&&A(t));for(var r,i,o,a=y(t)||!!n,s=e.attrs.length,c=new Array(s),l=0;l-1"+("true"===d?":("+l+")":":_q("+l+","+d+")")),Ar(c,"change","var $$a="+l+",$$el=$event.target,$$c=$$el.checked?("+d+"):("+v+");if(Array.isArray($$a)){var $$v="+(f?"_n("+p+")":p)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Er(l,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Er(l,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Er(l,"$$c")+"}",null,!0);else if("input"===$&&"radio"===w)r=e,i=_,a=(o=b)&&o.number,s=Or(r,"value")||"null",Cr(r,"checked","_q("+i+","+(s=a?"_n("+s+")":s)+")"),Ar(r,"change",Er(i,s),null,!0);else if("input"===$||"textarea"===$)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,l=o?"change":"range"===r?Pr:"input",u="$event.target.value";s&&(u="$event.target.value.trim()"),a&&(u="_n("+u+")");var f=Er(t,u);c&&(f="if($event.target.composing)return;"+f),Cr(e,"value","("+t+")"),Ar(e,l,f,null,!0),(s||a)&&Ar(e,"blur","$forceUpdate()")}(e,_,b);else if(!j.isReservedTag($))return Tr(e,_,b),!1;return!0},text:function(e,t){t.value&&Cr(e,"textContent","_s("+t.value+")")},html:function(e,t){t.value&&Cr(e,"innerHTML","_s("+t.value+")")}},isPreTag:function(e){return"pre"===e},isUnaryTag:to,mustUseProp:Sn,canBeLeftOpenTag:no,isReservedTag:Un,getTagNamespace:Vn,staticKeys:(Go=Wo,Go.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(","))},Qo=e(function(e){return s("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(e?","+e:""))});function ea(e,t){e&&(Zo=Qo(t.staticKeys||""),Xo=t.isReservedTag||O,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||c(e.tag)||!Xo(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(Zo)))}(t);if(1===t.type){if(!Xo(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function\s*\(/,na=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,ra={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},ia={esc:"Escape",tab:"Tab",enter:"Enter",space:" ",up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete"]},oa=function(e){return"if("+e+")return null;"},aa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:oa("$event.target !== $event.currentTarget"),ctrl:oa("!$event.ctrlKey"),shift:oa("!$event.shiftKey"),alt:oa("!$event.altKey"),meta:oa("!$event.metaKey"),left:oa("'button' in $event && $event.button !== 0"),middle:oa("'button' in $event && $event.button !== 1"),right:oa("'button' in $event && $event.button !== 2")};function sa(e,t,n){var r=t?"nativeOn:{":"on:{";for(var i in e)r+='"'+i+'":'+ca(i,e[i])+",";return r.slice(0,-1)+"}"}function ca(t,e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return ca(t,e)}).join(",")+"]";var n=na.test(e.value),r=ta.test(e.value);if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(aa[s])o+=aa[s],ra[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=oa(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+="if(!('button' in $event)&&"+a.map(la).join("&&")+")return null;"),o&&(i+=o),"function($event){"+i+(n?"return "+e.value+"($event)":r?"return ("+e.value+")($event)":e.value)+"}"}return n||r?e.value:"function($event){"+e.value+"}"}function la(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=ra[e],r=ia[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var ua={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(t,n){t.wrapData=function(e){return"_b("+e+",'"+t.tag+"',"+n.value+","+(n.modifiers&&n.modifiers.prop?"true":"false")+(n.modifiers&&n.modifiers.sync?",true":"")+")"}},cloak:$},fa=function(e){this.options=e,this.warn=e.warn||$r,this.transforms=wr(e.modules,"transformCode"),this.dataGenFns=wr(e.modules,"genData"),this.directives=m(m({},ua),e.directives);var t=e.isReservedTag||O;this.maybeComponent=function(e){return!t(e.tag)},this.onceId=0,this.staticRenderFns=[]};function pa(e,t){var n=new fa(t);return{render:"with(this){return "+(e?da(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function da(e,t){if(e.staticRoot&&!e.staticProcessed)return va(e,t);if(e.once&&!e.onceProcessed)return ha(e,t);if(e.for&&!e.forProcessed)return f=t,v=(u=e).for,h=u.alias,m=u.iterator1?","+u.iterator1:"",y=u.iterator2?","+u.iterator2:"",u.forProcessed=!0,(d||"_l")+"(("+v+"),function("+h+m+y+"){return "+(p||da)(u,f)+"})";if(e.if&&!e.ifProcessed)return ma(e,t);if("template"!==e.tag||e.slotTarget){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=_a(e,t),i="_t("+n+(r?","+r:""),o=e.attrs&&"{"+e.attrs.map(function(e){return g(e.name)+":"+e.value}).join(",")+"}",a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)a=e.component,c=t,l=(s=e).inlineTemplate?null:_a(s,c,!0),n="_c("+a+","+ya(s,c)+(l?","+l:"")+")";else{var r=e.plain?void 0:ya(e,t),i=e.inlineTemplate?null:_a(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',0 -

Sandwiches

+

Sandwiches

-

Add a cup of soup for $4

+

Add a cup of soup for $4

-

Can be made with Gluten Free bread

+

Can be made with Gluten Free bread

-

Bánh Mì 14

+

Bánh Mì 14

-

Slow roasted pork belly, with fresh pickled carrots, onion & cucumbers with jalapeños, red peppers & cilantro on panini bread with sweet chili mayo

+

Slow roasted pork belly, with fresh pickled carrots, onion & cucumbers with jalapeños, red peppers & cilantro on panini bread with sweet chili mayo

-

Club 7.50 half 13 whole

+

Club 7.50 half 13 whole

-

Turkey, ham, bacon, Havarti, sriracha mayo, lettuce & tomato on toasted sourdough

+

Turkey, ham, bacon, Havarti, sriracha mayo, lettuce & tomato on toasted sourdough

-

Grilled Reuben 7.50 half 13 whole

+

Grilled Reuben 7.50 half 13 whole

-

Pastrami, Swiss, sauerkraut & our homemade sauce on Rye

+

Pastrami, Swiss, sauerkraut & our homemade sauce on Rye

-

Grilled Rachel 7.50 half 13 whole

+

Grilled Rachel 7.50 half 13 whole

-

Turkey, Swiss, sauerkraut & our homemade sauce on Rye

+

Turkey, Swiss, sauerkraut & our homemade sauce on Rye

-

Grilled Veggie 7.50 half 13 whole

+

Grilled Veggie 7.50 half 13 whole

-

Grilled portobello, zucchini, peppers & onions with fresh greens & our homemade feta sauce on rye

+

Grilled portobello, zucchini, peppers & onions with fresh greens & our homemade feta sauce on rye

-

Pulled Pork 13

+

Pulled Pork 13

-

Our pulled pork, pepper jack cheese, sliced red onion & our homemade barbecue sauce on panini bread

+

Our pulled pork, pepper jack cheese, sliced red onion & our homemade barbecue sauce on panini bread

-

Grilled Ham & Cheese 7 half 12 whole

+

Grilled Ham & Cheese 7 half 12 whole

-

Ham, Swiss, mayo, Dijon & dill pickle on sourdough

+

Ham, Swiss, mayo, Dijon & dill pickle on sourdough

-

Spicy Ham Melt 7 half 12 whole

+

Spicy Ham Melt 7 half 12 whole

-

Ham, Havarti, sriracha mayo & red bell peppers on wheat

+

Ham, Havarti, sriracha mayo & red bell peppers on wheat

-

Tuna Melt 7 half * 12* whole

+

Tuna Melt 7 half * 12* whole

-

Our freshly made tuna salad with cheddar on sourdough

+

Our freshly made tuna salad with cheddar on sourdough

-

Cold Veggie 7 half 12 whole

+

Cold Veggie 7 half 12 whole

-

Cucumbers, carrots, red onions, red bell peppers, lettuce & tomato with provolone on seedy wheat with hummus

+

Cucumbers, carrots, red onions, red bell peppers, lettuce & tomato with provolone on seedy wheat with hummus

-

Basic Sandwich 7 half 12 whole

+

Basic Sandwich 7 half 12 whole

-

Choice of Ham or Turkey, with cheddar, mayo, Dijon, lettuce & tomato on sourdough

+

Choice of Ham or Turkey, with cheddar, mayo, Dijon, lettuce & tomato on sourdough

-

Burgers

+

Burgers

-

Add a cup of soup for $4

+

Add a cup of soup for $4

-

Served on panini bread but can be made with Gluten Free bread

+

Served on panini bread but can be made with Gluten Free bread

-

Big Burger 14

+

Big Burger 14

-

Local grass-fed beef, bacon, blue cheese, tomato, lettuce & special sauce

+

Local grass-fed beef, bacon, blue cheese, tomato, lettuce & special sauce

-

Big Lamb Köfte 14

+

Big Lamb Köfte 14

-

Grass-fed lamb, tomato, cucumber, red onions & feta sauce

+

Grass-fed lamb, tomato, cucumber, red onions & feta sauce

-

Chicken Burger 14

+

Chicken Burger 14

-

Organic chicken breast, bacon, provolone, pesto, garlic aioli, lettuce & tomato

+

Organic chicken breast, bacon, provolone, pesto, garlic aioli, lettuce & tomato

-

Big Classic 14

+

Big Classic 14

-

Local grass-fed beef & house made pork sausage, with bacon, sun-dried tomato pesto, grilled onions & provolone

+

Local grass-fed beef & house made pork sausage, with bacon, sun-dried tomato pesto, grilled onions & provolone

-

Portobello Burger * *Vegetarian 14 +

Portobello Burger * *Vegetarian 14 Grilled portobello, marinated red onions, jalapeños, red peppers & fresh greens on panini bread with sun-dried tomato pest & caper aioli

-

Sliders

+

Sliders

-

Add a cup of soup for $4

+

Add a cup of soup for $4

-

Served on our homemade buttermilk biscuit but can be made with Gluten Free bread

+

Served on our homemade buttermilk biscuit but can be made with Gluten Free bread

-

House 7.50

+

House 7.50

-

Local grass-fed beef, bacon, tomato & special sauce

+

Local grass-fed beef, bacon, tomato & special sauce

-

Lamb Köfte 7.50

+

Lamb Köfte 7.50

-

Grass-fed lamb, tomato, cucumber, red onions & tiziki sauce

+

Grass-fed lamb, tomato, cucumber, red onions & tiziki sauce

-

Portobello * *Vegetarian 7.50

+

Portobello * *Vegetarian 7.50

-

Marinated portobello, provolone, tomato, lettuce & balsamic mayo

+

Marinated portobello, provolone, tomato, lettuce & balsamic mayo

-

Little Classic** 7.50

+

Little Classic** 7.50

-

Local grass-fed beef & house made pork sausage with bacon, sun-dried tomato pesto, grilled onions & provolone

+

Local grass-fed beef & house made pork sausage with bacon, sun-dried tomato pesto, grilled onions & provolone

-

House Salads

+

House Salads

-

Garden Salad +

Garden Salad + 6.50 small 10 medium 15 large

+ +

Add organic Chicken or Shrimp for $9

+ +

Organic greens, cucumber, carrots, red bell peppers, red onion, beets, corn, peas, carrots, sunflower seeds & Balsamic dressing

+ +

Apple Blue Cheese Salad 6.50 small 10 medium 15 large

-

Add organic Chicken or Shrimp for $9

- -

Organic greens, cucumber, carrots, red bell peppers, red onion, beets, corn, peas, carrots, sunflower seeds & Balsamic dressing

- -

Apple Blue Cheese Salad - 6.50 small 10 medium 15 large

- -

Organic greens, blue cheese crumbles, walnuts, red onions & apples with balsamic dressing

+

Organic greens, blue cheese crumbles, walnuts, red onions & apples with balsamic dressing

-

Taco Salads

+

Taco Salads

-

Gluten Free

+

Gluten Free

-

Organic greens, black beans, corn, olives, tomatoes, green chillies, avocado, salsa, sour cream & our homemade chips

+

Organic greens, black beans, corn, olives, tomatoes, green chillies, avocado, salsa, sour cream & our homemade chips

-

· Beef 16

+

· Beef 16

-

· Chicken 16

+

· Chicken 16

-

· Veggie _ includes grilled zucchini & onions_ 16

+

· Veggie _ includes grilled zucchini & onions_ 16

-

Pork Carnitas Taco Salad 16

+

Pork Carnitas Taco Salad 16

-

Organic greens, pinto beans, sweet potatoes, avocado, mango chipotle sauce, salsa, sour cream & our homemade chips

+

Organic greens, pinto beans, sweet potatoes, avocado, mango chipotle sauce, salsa, sour cream & our homemade chips

@@ -196,12 +196,16 @@

· Veggie _ includes grilled zucchini & onions_ 13

+ + + +

Soup

Served with a buttermilk biscuit

-

Let us know if you are Gluten Free or Vegan

+

Let us know if you are Gluten Free or Vegan

5 cup 7 bowl