Ticket #34350: 34350.diff
| File 34350.diff, 113.7 KB (added by , 10 years ago) |
|---|
-
src/wp-includes/js/backbone.js
1 // Backbone.js 1. 1.21 // Backbone.js 1.2.3 2 2 3 // (c) 2010-201 4Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors3 // (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 4 // Backbone may be freely distributed under the MIT license. 5 5 // For all details and documentation: 6 6 // http://backbonejs.org 7 7 8 (function( root,factory) {8 (function(factory) { 9 9 10 // Establish the root object, `window` (`self`) in the browser, or `global` on the server. 11 // We use `self` instead of `window` for `WebWorker` support. 12 var root = (typeof self == 'object' && self.self == self && self) || 13 (typeof global == 'object' && global.global == global && global); 14 10 15 // Set up Backbone appropriately for the environment. Start with AMD. 11 16 if (typeof define === 'function' && define.amd) { 12 17 define(['underscore', 'jquery', 'exports'], function(_, $, exports) { … … 17 22 18 23 // Next for Node.js or CommonJS. jQuery may not be needed as a module. 19 24 } else if (typeof exports !== 'undefined') { 20 var _ = require('underscore'); 21 factory(root, exports, _); 25 var _ = require('underscore'), $; 26 try { $ = require('jquery'); } catch(e) {} 27 factory(root, exports, _, $); 22 28 23 29 // Finally, as a browser global. 24 30 } else { … … 25 31 root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); 26 32 } 27 33 28 }( this,function(root, Backbone, _, $) {34 }(function(root, Backbone, _, $) { 29 35 30 36 // Initial Setup 31 37 // ------------- … … 34 40 // restored later on, if `noConflict` is used. 35 41 var previousBackbone = root.Backbone; 36 42 37 // Create local references to array methods we'll want to use later. 38 var array = []; 39 var push = array.push; 40 var slice = array.slice; 41 var splice = array.splice; 43 // Create a local reference to a common array method we'll want to use later. 44 var slice = Array.prototype.slice; 42 45 43 46 // Current version of the library. Keep in sync with `package.json`. 44 Backbone.VERSION = '1. 1.2';47 Backbone.VERSION = '1.2.3'; 45 48 46 49 // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns 47 50 // the `$` variable. … … 60 63 Backbone.emulateHTTP = false; 61 64 62 65 // Turn on `emulateJSON` to support legacy servers that can't deal with direct 63 // `application/json` requests ... will encode the body as66 // `application/json` requests ... this will encode the body as 64 67 // `application/x-www-form-urlencoded` instead and will send the model in a 65 68 // form param named `model`. 66 69 Backbone.emulateJSON = false; 67 70 71 // Proxy Backbone class methods to Underscore functions, wrapping the model's 72 // `attributes` object or collection's `models` array behind the scenes. 73 // 74 // collection.filter(function(model) { return model.get('age') > 10 }); 75 // collection.each(this.addView); 76 // 77 // `Function#apply` can be slow so we use the method's arg count, if we know it. 78 var addMethod = function(length, method, attribute) { 79 switch (length) { 80 case 1: return function() { 81 return _[method](this[attribute]); 82 }; 83 case 2: return function(value) { 84 return _[method](this[attribute], value); 85 }; 86 case 3: return function(iteratee, context) { 87 return _[method](this[attribute], cb(iteratee, this), context); 88 }; 89 case 4: return function(iteratee, defaultVal, context) { 90 return _[method](this[attribute], cb(iteratee, this), defaultVal, context); 91 }; 92 default: return function() { 93 var args = slice.call(arguments); 94 args.unshift(this[attribute]); 95 return _[method].apply(_, args); 96 }; 97 } 98 }; 99 var addUnderscoreMethods = function(Class, methods, attribute) { 100 _.each(methods, function(length, method) { 101 if (_[method]) Class.prototype[method] = addMethod(length, method, attribute); 102 }); 103 }; 104 105 // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`. 106 var cb = function(iteratee, instance) { 107 if (_.isFunction(iteratee)) return iteratee; 108 if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee); 109 if (_.isString(iteratee)) return function(model) { return model.get(iteratee); }; 110 return iteratee; 111 }; 112 var modelMatcher = function(attrs) { 113 var matcher = _.matches(attrs); 114 return function(model) { 115 return matcher(model.attributes); 116 }; 117 }; 118 68 119 // Backbone.Events 69 120 // --------------- 70 121 71 122 // A module that can be mixed in to *any object* in order to provide it with 72 // custom events. You may bind with `on` or remove with `off` callback73 // functions to an event; `trigger`-ing an event fires all callbacks in123 // a custom event channel. You may bind a callback to an event with `on` or 124 // remove with `off`; `trigger`-ing an event fires all callbacks in 74 125 // succession. 75 126 // 76 127 // var object = {}; … … 78 129 // object.on('expand', function(){ alert('expanded'); }); 79 130 // object.trigger('expand'); 80 131 // 81 var Events = Backbone.Events = { 132 var Events = Backbone.Events = {}; 82 133 83 // Bind an event to a `callback` function. Passing `"all"` will bind 84 // the callback to all events fired. 85 on: function(name, callback, context) { 86 if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; 87 this._events || (this._events = {}); 88 var events = this._events[name] || (this._events[name] = []); 89 events.push({callback: callback, context: context, ctx: context || this}); 90 return this; 91 }, 134 // Regular expression used to split event strings. 135 var eventSplitter = /\s+/; 92 136 93 // Bind an event to only be triggered a single time. After the first time 94 // the callback is invoked, it will be removed. 95 once: function(name, callback, context) { 96 if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; 97 var self = this; 98 var once = _.once(function() { 99 self.off(name, once); 100 callback.apply(this, arguments); 101 }); 102 once._callback = callback; 103 return this.on(name, once, context); 104 }, 105 106 // Remove one or many callbacks. If `context` is null, removes all 107 // callbacks with that function. If `callback` is null, removes all 108 // callbacks for the event. If `name` is null, removes all bound 109 // callbacks for all events. 110 off: function(name, callback, context) { 111 var retain, ev, events, names, i, l, j, k; 112 if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; 113 if (!name && !callback && !context) { 114 this._events = void 0; 115 return this; 137 // Iterates over the standard `event, callback` (as well as the fancy multiple 138 // space-separated events `"change blur", callback` and jQuery-style event 139 // maps `{event: callback}`). 140 var eventsApi = function(iteratee, events, name, callback, opts) { 141 var i = 0, names; 142 if (name && typeof name === 'object') { 143 // Handle event maps. 144 if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; 145 for (names = _.keys(name); i < names.length ; i++) { 146 events = eventsApi(iteratee, events, names[i], name[names[i]], opts); 116 147 } 117 names = name ? [name] : _.keys(this._events); 118 for (i = 0, l = names.length; i < l; i++) { 119 name = names[i]; 120 if (events = this._events[name]) { 121 this._events[name] = retain = []; 122 if (callback || context) { 123 for (j = 0, k = events.length; j < k; j++) { 124 ev = events[j]; 125 if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || 126 (context && context !== ev.context)) { 127 retain.push(ev); 128 } 129 } 130 } 131 if (!retain.length) delete this._events[name]; 132 } 148 } else if (name && eventSplitter.test(name)) { 149 // Handle space separated event names by delegating them individually. 150 for (names = name.split(eventSplitter); i < names.length; i++) { 151 events = iteratee(events, names[i], callback, opts); 133 152 } 153 } else { 154 // Finally, standard events. 155 events = iteratee(events, name, callback, opts); 156 } 157 return events; 158 }; 134 159 135 return this; 136 }, 160 // Bind an event to a `callback` function. Passing `"all"` will bind 161 // the callback to all events fired. 162 Events.on = function(name, callback, context) { 163 return internalOn(this, name, callback, context); 164 }; 137 165 138 // Trigger one or many events, firing all bound callbacks. Callbacks are 139 // passed the same arguments as `trigger` is, apart from the event name 140 // (unless you're listening on `"all"`, which will cause your callback to 141 // receive the true name of the event as the first argument). 142 trigger: function(name) { 143 if (!this._events) return this; 144 var args = slice.call(arguments, 1); 145 if (!eventsApi(this, 'trigger', name, args)) return this; 146 var events = this._events[name]; 147 var allEvents = this._events.all; 148 if (events) triggerEvents(events, args); 149 if (allEvents) triggerEvents(allEvents, arguments); 150 return this; 151 }, 166 // Guard the `listening` argument from the public API. 167 var internalOn = function(obj, name, callback, context, listening) { 168 obj._events = eventsApi(onApi, obj._events || {}, name, callback, { 169 context: context, 170 ctx: obj, 171 listening: listening 172 }); 152 173 153 // Tell this object to stop listening to either specific events ... or 154 // to every object it's currently listening to. 155 stopListening: function(obj, name, callback) { 156 var listeningTo = this._listeningTo; 157 if (!listeningTo) return this; 158 var remove = !name && !callback; 159 if (!callback && typeof name === 'object') callback = this; 160 if (obj) (listeningTo = {})[obj._listenId] = obj; 161 for (var id in listeningTo) { 162 obj = listeningTo[id]; 163 obj.off(name, callback, this); 164 if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; 165 } 166 return this; 174 if (listening) { 175 var listeners = obj._listeners || (obj._listeners = {}); 176 listeners[listening.id] = listening; 167 177 } 168 178 179 return obj; 169 180 }; 170 181 171 // Regular expression used to split event strings. 172 var eventSplitter = /\s+/; 182 // Inversion-of-control versions of `on`. Tell *this* object to listen to 183 // an event in another object... keeping track of what it's listening to 184 // for easier unbinding later. 185 Events.listenTo = function(obj, name, callback) { 186 if (!obj) return this; 187 var id = obj._listenId || (obj._listenId = _.uniqueId('l')); 188 var listeningTo = this._listeningTo || (this._listeningTo = {}); 189 var listening = listeningTo[id]; 173 190 174 // Implement fancy features of the Events API such as multiple event 175 // names `"change blur"` and jQuery-style event maps `{change: action}` 176 // in terms of the existing API. 177 var eventsApi = function(obj, action, name, rest) { 178 if (!name) return true; 191 // This object is not listening to any other events on `obj` yet. 192 // Setup the necessary references to track the listening callbacks. 193 if (!listening) { 194 var thisId = this._listenId || (this._listenId = _.uniqueId('l')); 195 listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0}; 196 } 179 197 180 // Handle event maps. 181 if (typeof name === 'object') { 182 for (var key in name) { 183 obj[action].apply(obj, [key, name[key]].concat(rest)); 198 // Bind callbacks on obj, and keep track of them on listening. 199 internalOn(obj, name, callback, this, listening); 200 return this; 201 }; 202 203 // The reducing API that adds a callback to the `events` object. 204 var onApi = function(events, name, callback, options) { 205 if (callback) { 206 var handlers = events[name] || (events[name] = []); 207 var context = options.context, ctx = options.ctx, listening = options.listening; 208 if (listening) listening.count++; 209 210 handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening }); 211 } 212 return events; 213 }; 214 215 // Remove one or many callbacks. If `context` is null, removes all 216 // callbacks with that function. If `callback` is null, removes all 217 // callbacks for the event. If `name` is null, removes all bound 218 // callbacks for all events. 219 Events.off = function(name, callback, context) { 220 if (!this._events) return this; 221 this._events = eventsApi(offApi, this._events, name, callback, { 222 context: context, 223 listeners: this._listeners 224 }); 225 return this; 226 }; 227 228 // Tell this object to stop listening to either specific events ... or 229 // to every object it's currently listening to. 230 Events.stopListening = function(obj, name, callback) { 231 var listeningTo = this._listeningTo; 232 if (!listeningTo) return this; 233 234 var ids = obj ? [obj._listenId] : _.keys(listeningTo); 235 236 for (var i = 0; i < ids.length; i++) { 237 var listening = listeningTo[ids[i]]; 238 239 // If listening doesn't exist, this object is not currently 240 // listening to obj. Break out early. 241 if (!listening) break; 242 243 listening.obj.off(name, callback, this); 244 } 245 if (_.isEmpty(listeningTo)) this._listeningTo = void 0; 246 247 return this; 248 }; 249 250 // The reducing API that removes a callback from the `events` object. 251 var offApi = function(events, name, callback, options) { 252 if (!events) return; 253 254 var i = 0, listening; 255 var context = options.context, listeners = options.listeners; 256 257 // Delete all events listeners and "drop" events. 258 if (!name && !callback && !context) { 259 var ids = _.keys(listeners); 260 for (; i < ids.length; i++) { 261 listening = listeners[ids[i]]; 262 delete listeners[listening.id]; 263 delete listening.listeningTo[listening.objId]; 184 264 } 185 return false;265 return; 186 266 } 187 267 188 // Handle space separated event names. 189 if (eventSplitter.test(name)) { 190 var names = name.split(eventSplitter); 191 for (var i = 0, l = names.length; i < l; i++) { 192 obj[action].apply(obj, [names[i]].concat(rest)); 268 var names = name ? [name] : _.keys(events); 269 for (; i < names.length; i++) { 270 name = names[i]; 271 var handlers = events[name]; 272 273 // Bail out if there are no events stored. 274 if (!handlers) break; 275 276 // Replace events if there are any remaining. Otherwise, clean up. 277 var remaining = []; 278 for (var j = 0; j < handlers.length; j++) { 279 var handler = handlers[j]; 280 if ( 281 callback && callback !== handler.callback && 282 callback !== handler.callback._callback || 283 context && context !== handler.context 284 ) { 285 remaining.push(handler); 286 } else { 287 listening = handler.listening; 288 if (listening && --listening.count === 0) { 289 delete listeners[listening.id]; 290 delete listening.listeningTo[listening.objId]; 291 } 292 } 193 293 } 194 return false; 294 295 // Update tail event if the list has any events. Otherwise, clean up. 296 if (remaining.length) { 297 events[name] = remaining; 298 } else { 299 delete events[name]; 300 } 195 301 } 302 if (_.size(events)) return events; 303 }; 196 304 197 return true; 305 // Bind an event to only be triggered a single time. After the first time 306 // the callback is invoked, its listener will be removed. If multiple events 307 // are passed in using the space-separated syntax, the handler will fire 308 // once for each event, not once for a combination of all events. 309 Events.once = function(name, callback, context) { 310 // Map the event into a `{event: once}` object. 311 var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); 312 return this.on(events, void 0, context); 198 313 }; 199 314 315 // Inversion-of-control versions of `once`. 316 Events.listenToOnce = function(obj, name, callback) { 317 // Map the event into a `{event: once}` object. 318 var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj)); 319 return this.listenTo(obj, events); 320 }; 321 322 // Reduces the event callbacks into a map of `{event: onceWrapper}`. 323 // `offer` unbinds the `onceWrapper` after it has been called. 324 var onceMap = function(map, name, callback, offer) { 325 if (callback) { 326 var once = map[name] = _.once(function() { 327 offer(name, once); 328 callback.apply(this, arguments); 329 }); 330 once._callback = callback; 331 } 332 return map; 333 }; 334 335 // Trigger one or many events, firing all bound callbacks. Callbacks are 336 // passed the same arguments as `trigger` is, apart from the event name 337 // (unless you're listening on `"all"`, which will cause your callback to 338 // receive the true name of the event as the first argument). 339 Events.trigger = function(name) { 340 if (!this._events) return this; 341 342 var length = Math.max(0, arguments.length - 1); 343 var args = Array(length); 344 for (var i = 0; i < length; i++) args[i] = arguments[i + 1]; 345 346 eventsApi(triggerApi, this._events, name, void 0, args); 347 return this; 348 }; 349 350 // Handles triggering the appropriate event callbacks. 351 var triggerApi = function(objEvents, name, cb, args) { 352 if (objEvents) { 353 var events = objEvents[name]; 354 var allEvents = objEvents.all; 355 if (events && allEvents) allEvents = allEvents.slice(); 356 if (events) triggerEvents(events, args); 357 if (allEvents) triggerEvents(allEvents, [name].concat(args)); 358 } 359 return objEvents; 360 }; 361 200 362 // A difficult-to-believe, but optimized internal dispatch function for 201 363 // triggering events. Tries to keep the usual cases speedy (most internal 202 364 // Backbone events have 3 arguments). … … 211 373 } 212 374 }; 213 375 214 var listenMethods = {listenTo: 'on', listenToOnce: 'once'};215 216 // Inversion-of-control versions of `on` and `once`. Tell *this* object to217 // listen to an event in another object ... keeping track of what it's218 // listening to.219 _.each(listenMethods, function(implementation, method) {220 Events[method] = function(obj, name, callback) {221 var listeningTo = this._listeningTo || (this._listeningTo = {});222 var id = obj._listenId || (obj._listenId = _.uniqueId('l'));223 listeningTo[id] = obj;224 if (!callback && typeof name === 'object') callback = this;225 obj[implementation](name, callback, this);226 return this;227 };228 });229 230 376 // Aliases for backwards compatibility. 231 377 Events.bind = Events.on; 232 378 Events.unbind = Events.off; … … 248 394 var Model = Backbone.Model = function(attributes, options) { 249 395 var attrs = attributes || {}; 250 396 options || (options = {}); 251 this.cid = _.uniqueId( 'c');397 this.cid = _.uniqueId(this.cidPrefix); 252 398 this.attributes = {}; 253 399 if (options.collection) this.collection = options.collection; 254 400 if (options.parse) attrs = this.parse(attrs, options) || {}; … … 271 417 // CouchDB users may want to set this to `"_id"`. 272 418 idAttribute: 'id', 273 419 420 // The prefix is used to create the client id which is used to identify models locally. 421 // You may want to override this if you're experiencing name clashes with model ids. 422 cidPrefix: 'c', 423 274 424 // Initialize is an empty function by default. Override it with your own 275 425 // initialization logic. 276 426 initialize: function(){}, … … 302 452 return this.get(attr) != null; 303 453 }, 304 454 455 // Special-cased proxy to underscore's `_.matches` method. 456 matches: function(attrs) { 457 return !!_.iteratee(attrs, this)(this.attributes); 458 }, 459 305 460 // Set a hash of model attributes on the object, firing `"change"`. This is 306 461 // the core primitive operation of a model, updating the data and notifying 307 462 // anyone who needs to know about the change in state. The heart of the beast. 308 463 set: function(key, val, options) { 309 var attr, attrs, unset, changes, silent, changing, prev, current;310 464 if (key == null) return this; 311 465 312 466 // Handle both `"key", value` and `{key: value}` -style arguments. 467 var attrs; 313 468 if (typeof key === 'object') { 314 469 attrs = key; 315 470 options = val; … … 323 478 if (!this._validate(attrs, options)) return false; 324 479 325 480 // Extract attributes and options. 326 unset= options.unset;327 silent= options.silent;328 changes= [];329 changing= this._changing;330 this._changing = true;481 var unset = options.unset; 482 var silent = options.silent; 483 var changes = []; 484 var changing = this._changing; 485 this._changing = true; 331 486 332 487 if (!changing) { 333 488 this._previousAttributes = _.clone(this.attributes); 334 489 this.changed = {}; 335 490 } 336 current = this.attributes, prev = this._previousAttributes;337 491 338 // Check for changes of `id`. 339 if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; 492 var current = this.attributes; 493 var changed = this.changed; 494 var prev = this._previousAttributes; 340 495 341 496 // For each `set` attribute, update or delete the current value. 342 for ( attr in attrs) {497 for (var attr in attrs) { 343 498 val = attrs[attr]; 344 499 if (!_.isEqual(current[attr], val)) changes.push(attr); 345 500 if (!_.isEqual(prev[attr], val)) { 346 this.changed[attr] = val;501 changed[attr] = val; 347 502 } else { 348 delete this.changed[attr];503 delete changed[attr]; 349 504 } 350 505 unset ? delete current[attr] : current[attr] = val; 351 506 } 352 507 508 // Update the `id`. 509 this.id = this.get(this.idAttribute); 510 353 511 // Trigger all relevant attribute changes. 354 512 if (!silent) { 355 513 if (changes.length) this._pending = options; 356 for (var i = 0 , l = changes.length; i < l; i++) {514 for (var i = 0; i < changes.length; i++) { 357 515 this.trigger('change:' + changes[i], this, current[changes[i]], options); 358 516 } 359 517 } … … 401 559 // determining if there *would be* a change. 402 560 changedAttributes: function(diff) { 403 561 if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; 404 var val, changed = false;405 562 var old = this._changing ? this._previousAttributes : this.attributes; 563 var changed = {}; 406 564 for (var attr in diff) { 407 if (_.isEqual(old[attr], (val = diff[attr]))) continue; 408 (changed || (changed = {}))[attr] = val; 565 var val = diff[attr]; 566 if (_.isEqual(old[attr], val)) continue; 567 changed[attr] = val; 409 568 } 410 return changed;569 return _.size(changed) ? changed : false; 411 570 }, 412 571 413 572 // Get the previous value of an attribute, recorded at the time the last … … 423 582 return _.clone(this._previousAttributes); 424 583 }, 425 584 426 // Fetch the model from the server. If the server's representation of the 427 // model differs from its current attributes, they will be overridden, 428 // triggering a `"change"` event. 585 // Fetch the model from the server, merging the response with the model's 586 // local attributes. Any changed attributes will trigger a "change" event. 429 587 fetch: function(options) { 430 options = options ? _.clone(options) : {}; 431 if (options.parse === void 0) options.parse = true; 588 options = _.extend({parse: true}, options); 432 589 var model = this; 433 590 var success = options.success; 434 591 options.success = function(resp) { 435 if (!model.set(model.parse(resp, options), options)) return false; 436 if (success) success(model, resp, options); 592 var serverAttrs = options.parse ? model.parse(resp, options) : resp; 593 if (!model.set(serverAttrs, options)) return false; 594 if (success) success.call(options.context, model, resp, options); 437 595 model.trigger('sync', model, resp, options); 438 596 }; 439 597 wrapError(this, options); … … 444 602 // If the server returns an attributes hash that differs, the model's 445 603 // state will be `set` again. 446 604 save: function(key, val, options) { 447 var attrs, method, xhr, attributes = this.attributes;448 449 605 // Handle both `"key", value` and `{key: value}` -style arguments. 606 var attrs; 450 607 if (key == null || typeof key === 'object') { 451 608 attrs = key; 452 609 options = val; … … 454 611 (attrs = {})[key] = val; 455 612 } 456 613 457 options = _.extend({validate: true}, options); 614 options = _.extend({validate: true, parse: true}, options); 615 var wait = options.wait; 458 616 459 617 // If we're not waiting and attributes exist, save acts as 460 618 // `set(attr).save(null, opts)` with validation. Otherwise, check if 461 619 // the model will be valid when the attributes, if any, are set. 462 if (attrs && ! options.wait) {620 if (attrs && !wait) { 463 621 if (!this.set(attrs, options)) return false; 464 622 } else { 465 623 if (!this._validate(attrs, options)) return false; 466 624 } 467 625 468 // Set temporary attributes if `{wait: true}`.469 if (attrs && options.wait) {470 this.attributes = _.extend({}, attributes, attrs);471 }472 473 626 // After a successful server-side save, the client is (optionally) 474 627 // updated with the server-side state. 475 if (options.parse === void 0) options.parse = true;476 628 var model = this; 477 629 var success = options.success; 630 var attributes = this.attributes; 478 631 options.success = function(resp) { 479 632 // Ensure attributes are restored during synchronous saves. 480 633 model.attributes = attributes; 481 var serverAttrs = model.parse(resp, options); 482 if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); 483 if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { 484 return false; 485 } 486 if (success) success(model, resp, options); 634 var serverAttrs = options.parse ? model.parse(resp, options) : resp; 635 if (wait) serverAttrs = _.extend({}, attrs, serverAttrs); 636 if (serverAttrs && !model.set(serverAttrs, options)) return false; 637 if (success) success.call(options.context, model, resp, options); 487 638 model.trigger('sync', model, resp, options); 488 639 }; 489 640 wrapError(this, options); 490 641 491 method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); 492 if (method === 'patch') options.attrs = attrs; 493 xhr = this.sync(method, this, options); 642 // Set temporary attributes if `{wait: true}` to properly find new ids. 643 if (attrs && wait) this.attributes = _.extend({}, attributes, attrs); 494 644 645 var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); 646 if (method === 'patch' && !options.attrs) options.attrs = attrs; 647 var xhr = this.sync(method, this, options); 648 495 649 // Restore attributes. 496 if (attrs && options.wait)this.attributes = attributes;650 this.attributes = attributes; 497 651 498 652 return xhr; 499 653 }, … … 505 659 options = options ? _.clone(options) : {}; 506 660 var model = this; 507 661 var success = options.success; 662 var wait = options.wait; 508 663 509 664 var destroy = function() { 665 model.stopListening(); 510 666 model.trigger('destroy', model, model.collection, options); 511 667 }; 512 668 513 669 options.success = function(resp) { 514 if ( options.wait || model.isNew()) destroy();515 if (success) success (model, resp, options);670 if (wait) destroy(); 671 if (success) success.call(options.context, model, resp, options); 516 672 if (!model.isNew()) model.trigger('sync', model, resp, options); 517 673 }; 518 674 675 var xhr = false; 519 676 if (this.isNew()) { 520 options.success(); 521 return false; 677 _.defer(options.success); 678 } else { 679 wrapError(this, options); 680 xhr = this.sync('delete', this, options); 522 681 } 523 wrapError(this, options); 524 525 var xhr = this.sync('delete', this, options); 526 if (!options.wait) destroy(); 682 if (!wait) destroy(); 527 683 return xhr; 528 684 }, 529 685 … … 536 692 _.result(this.collection, 'url') || 537 693 urlError(); 538 694 if (this.isNew()) return base; 539 return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); 695 var id = this.get(this.idAttribute); 696 return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id); 540 697 }, 541 698 542 699 // **parse** converts a response into the hash of attributes to be `set` on … … 557 714 558 715 // Check if the model is currently in a valid state. 559 716 isValid: function(options) { 560 return this._validate({}, _. extend(options || {}, { validate: true }));717 return this._validate({}, _.defaults({validate: true}, options)); 561 718 }, 562 719 563 720 // Run validation against the next complete set of model attributes, … … 573 730 574 731 }); 575 732 576 // Underscore methods that we want to implement on the Model. 577 var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; 733 // Underscore methods that we want to implement on the Model, mapped to the 734 // number of arguments they take. 735 var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0, 736 omit: 0, chain: 1, isEmpty: 1 }; 578 737 579 738 // Mix in each Underscore method as a proxy to `Model#attributes`. 580 _.each(modelMethods, function(method) { 581 Model.prototype[method] = function() { 582 var args = slice.call(arguments); 583 args.unshift(this.attributes); 584 return _[method].apply(_, args); 585 }; 586 }); 739 addUnderscoreMethods(Model, modelMethods, 'attributes'); 587 740 588 741 // Backbone.Collection 589 742 // ------------------- 590 743 591 744 // If models tend to represent a single row of data, a Backbone Collection is 592 // more anal agous to a table full of data ... or a small slice or page of that745 // more analogous to a table full of data ... or a small slice or page of that 593 746 // table, or a collection of rows that belong together for a particular reason 594 747 // -- all of the messages in this particular folder, all of the documents 595 748 // belonging to this particular author, and so on. Collections maintain … … 611 764 var setOptions = {add: true, remove: true, merge: true}; 612 765 var addOptions = {add: true, remove: false}; 613 766 767 // Splices `insert` into `array` at index `at`. 768 var splice = function(array, insert, at) { 769 at = Math.min(Math.max(at, 0), array.length); 770 var tail = Array(array.length - at); 771 var length = insert.length; 772 for (var i = 0; i < tail.length; i++) tail[i] = array[i + at]; 773 for (i = 0; i < length; i++) array[i + at] = insert[i]; 774 for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i]; 775 }; 776 614 777 // Define the Collection's inheritable methods. 615 778 _.extend(Collection.prototype, Events, { 616 779 … … 625 788 // The JSON representation of a Collection is an array of the 626 789 // models' attributes. 627 790 toJSON: function(options) { 628 return this.map(function(model) { return model.toJSON(options); });791 return this.map(function(model) { return model.toJSON(options); }); 629 792 }, 630 793 631 794 // Proxy `Backbone.sync` by default. … … 633 796 return Backbone.sync.apply(this, arguments); 634 797 }, 635 798 636 // Add a model, or list of models to the set. 799 // Add a model, or list of models to the set. `models` may be Backbone 800 // Models or raw JavaScript objects to be converted to Models, or any 801 // combination of the two. 637 802 add: function(models, options) { 638 803 return this.set(models, _.extend({merge: false}, options, addOptions)); 639 804 }, … … 640 805 641 806 // Remove a model, or a list of models from the set. 642 807 remove: function(models, options) { 808 options = _.extend({}, options); 643 809 var singular = !_.isArray(models); 644 810 models = singular ? [models] : _.clone(models); 645 options || (options = {}); 646 var i, l, index, model; 647 for (i = 0, l = models.length; i < l; i++) { 648 model = models[i] = this.get(models[i]); 649 if (!model) continue; 650 delete this._byId[model.id]; 651 delete this._byId[model.cid]; 652 index = this.indexOf(model); 653 this.models.splice(index, 1); 654 this.length--; 655 if (!options.silent) { 656 options.index = index; 657 model.trigger('remove', model, this, options); 658 } 659 this._removeReference(model, options); 660 } 661 return singular ? models[0] : models; 811 var removed = this._removeModels(models, options); 812 if (!options.silent && removed) this.trigger('update', this, options); 813 return singular ? removed[0] : removed; 662 814 }, 663 815 664 816 // Update a collection by `set`-ing a new list of models, adding new ones, … … 666 818 // already exist in the collection, as necessary. Similar to **Model#set**, 667 819 // the core operation for updating the data contained by the collection. 668 820 set: function(models, options) { 821 if (models == null) return; 822 669 823 options = _.defaults({}, options, setOptions); 670 if (options.parse) models = this.parse(models, options); 824 if (options.parse && !this._isModel(models)) models = this.parse(models, options); 825 671 826 var singular = !_.isArray(models); 672 models = singular ? (models ? [models] : []) : _.clone(models);673 var i, l, id, model, attrs, existing, sort; 827 models = singular ? [models] : models.slice(); 828 674 829 var at = options.at; 675 var targetModel = this.model; 830 if (at != null) at = +at; 831 if (at < 0) at += this.length + 1; 832 833 var set = []; 834 var toAdd = []; 835 var toRemove = []; 836 var modelMap = {}; 837 838 var add = options.add; 839 var merge = options.merge; 840 var remove = options.remove; 841 842 var sort = false; 676 843 var sortable = this.comparator && (at == null) && options.sort !== false; 677 844 var sortAttr = _.isString(this.comparator) ? this.comparator : null; 678 var toAdd = [], toRemove = [], modelMap = {};679 var add = options.add, merge = options.merge, remove = options.remove;680 var order = !sortable && add && remove ? [] : false;681 845 682 846 // Turn bare objects into model references, and prevent invalid models 683 847 // from being added. 684 for (i = 0, l = models.length; i < l; i++) { 685 attrs = models[i] || {}; 686 if (attrs instanceof Model) { 687 id = model = attrs; 688 } else { 689 id = attrs[targetModel.prototype.idAttribute || 'id']; 690 } 848 var model; 849 for (var i = 0; i < models.length; i++) { 850 model = models[i]; 691 851 692 852 // If a duplicate is found, prevent it from being added and 693 853 // optionally merge it into the existing model. 694 if (existing = this.get(id)) {695 if (remove) modelMap[existing.cid] = true;696 if (merge ) {697 attrs = attrs === model ? model.attributes : attrs;854 var existing = this.get(model); 855 if (existing) { 856 if (merge && model !== existing) { 857 var attrs = this._isModel(model) ? model.attributes : model; 698 858 if (options.parse) attrs = existing.parse(attrs, options); 699 859 existing.set(attrs, options); 700 if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;860 if (sortable && !sort) sort = existing.hasChanged(sortAttr); 701 861 } 862 if (!modelMap[existing.cid]) { 863 modelMap[existing.cid] = true; 864 set.push(existing); 865 } 702 866 models[i] = existing; 703 867 704 868 // If this is a new, valid model, push it to the `toAdd` list. 705 869 } else if (add) { 706 model = models[i] = this._prepareModel(attrs, options); 707 if (!model) continue; 708 toAdd.push(model); 709 this._addReference(model, options); 870 model = models[i] = this._prepareModel(model, options); 871 if (model) { 872 toAdd.push(model); 873 this._addReference(model, options); 874 modelMap[model.cid] = true; 875 set.push(model); 876 } 710 877 } 711 712 // Do not add multiple models with the same `id`.713 model = existing || model;714 if (order && (model.isNew() || !modelMap[model.id])) order.push(model);715 modelMap[model.id] = true;716 878 } 717 879 718 // Remove nonexistent models if appropriate.880 // Remove stale models. 719 881 if (remove) { 720 for (i = 0, l = this.length; i < l; ++i) { 721 if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); 882 for (i = 0; i < this.length; i++) { 883 model = this.models[i]; 884 if (!modelMap[model.cid]) toRemove.push(model); 722 885 } 723 if (toRemove.length) this. remove(toRemove, options);886 if (toRemove.length) this._removeModels(toRemove, options); 724 887 } 725 888 726 889 // See if sorting is needed, update `length` and splice in new models. 727 if (toAdd.length || (order && order.length)) { 890 var orderChanged = false; 891 var replace = !sortable && add && remove; 892 if (set.length && replace) { 893 orderChanged = this.length != set.length || _.some(this.models, function(model, index) { 894 return model !== set[index]; 895 }); 896 this.models.length = 0; 897 splice(this.models, set, 0); 898 this.length = this.models.length; 899 } else if (toAdd.length) { 728 900 if (sortable) sort = true; 729 this.length += toAdd.length; 730 if (at != null) { 731 for (i = 0, l = toAdd.length; i < l; i++) { 732 this.models.splice(at + i, 0, toAdd[i]); 733 } 734 } else { 735 if (order) this.models.length = 0; 736 var orderedModels = order || toAdd; 737 for (i = 0, l = orderedModels.length; i < l; i++) { 738 this.models.push(orderedModels[i]); 739 } 740 } 901 splice(this.models, toAdd, at == null ? this.length : at); 902 this.length = this.models.length; 741 903 } 742 904 743 905 // Silently sort the collection if appropriate. … … 745 907 746 908 // Unless silenced, it's time to fire all appropriate add/sort events. 747 909 if (!options.silent) { 748 for (i = 0, l = toAdd.length; i < l; i++) { 749 (model = toAdd[i]).trigger('add', model, this, options); 910 for (i = 0; i < toAdd.length; i++) { 911 if (at != null) options.index = at + i; 912 model = toAdd[i]; 913 model.trigger('add', model, this, options); 750 914 } 751 if (sort || (order && order.length)) this.trigger('sort', this, options); 915 if (sort || orderChanged) this.trigger('sort', this, options); 916 if (toAdd.length || toRemove.length) this.trigger('update', this, options); 752 917 } 753 918 754 919 // Return the added (or merged) model (or models). … … 760 925 // any granular `add` or `remove` events. Fires `reset` when finished. 761 926 // Useful for bulk operations and optimizations. 762 927 reset: function(models, options) { 763 options || (options = {});764 for (var i = 0 , l = this.models.length; i < l; i++) {928 options = options ? _.clone(options) : {}; 929 for (var i = 0; i < this.models.length; i++) { 765 930 this._removeReference(this.models[i], options); 766 931 } 767 932 options.previousModels = this.models; … … 779 944 // Remove a model from the end of the collection. 780 945 pop: function(options) { 781 946 var model = this.at(this.length - 1); 782 this.remove(model, options); 783 return model; 947 return this.remove(model, options); 784 948 }, 785 949 786 950 // Add a model to the beginning of the collection. … … 791 955 // Remove a model from the beginning of the collection. 792 956 shift: function(options) { 793 957 var model = this.at(0); 794 this.remove(model, options); 795 return model; 958 return this.remove(model, options); 796 959 }, 797 960 798 961 // Slice out a sub-array of models from the collection. … … 803 966 // Get a model from the set by id. 804 967 get: function(obj) { 805 968 if (obj == null) return void 0; 806 return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid]; 969 var id = this.modelId(this._isModel(obj) ? obj.attributes : obj); 970 return this._byId[obj] || this._byId[id] || this._byId[obj.cid]; 807 971 }, 808 972 809 973 // Get the model at the given index. 810 974 at: function(index) { 975 if (index < 0) index += this.length; 811 976 return this.models[index]; 812 977 }, 813 978 … … 814 979 // Return models with matching attributes. Useful for simple cases of 815 980 // `filter`. 816 981 where: function(attrs, first) { 817 if (_.isEmpty(attrs)) return first ? void 0 : []; 818 return this[first ? 'find' : 'filter'](function(model) { 819 for (var key in attrs) { 820 if (attrs[key] !== model.get(key)) return false; 821 } 822 return true; 823 }); 982 return this[first ? 'find' : 'filter'](attrs); 824 983 }, 825 984 826 985 // Return the first model with matching attributes. Useful for simple cases … … 833 992 // normal circumstances, as the set will maintain sort order as each item 834 993 // is added. 835 994 sort: function(options) { 836 if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); 995 var comparator = this.comparator; 996 if (!comparator) throw new Error('Cannot sort a set without a comparator'); 837 997 options || (options = {}); 838 998 999 var length = comparator.length; 1000 if (_.isFunction(comparator)) comparator = _.bind(comparator, this); 1001 839 1002 // Run sort based on type of `comparator`. 840 if ( _.isString(this.comparator) || this.comparator.length === 1) {841 this.models = this.sortBy( this.comparator, this);1003 if (length === 1 || _.isString(comparator)) { 1004 this.models = this.sortBy(comparator); 842 1005 } else { 843 this.models.sort( _.bind(this.comparator, this));1006 this.models.sort(comparator); 844 1007 } 845 846 1008 if (!options.silent) this.trigger('sort', this, options); 847 1009 return this; 848 1010 }, … … 856 1018 // collection when they arrive. If `reset: true` is passed, the response 857 1019 // data will be passed through the `reset` method instead of `set`. 858 1020 fetch: function(options) { 859 options = options ? _.clone(options) : {}; 860 if (options.parse === void 0) options.parse = true; 1021 options = _.extend({parse: true}, options); 861 1022 var success = options.success; 862 1023 var collection = this; 863 1024 options.success = function(resp) { 864 1025 var method = options.reset ? 'reset' : 'set'; 865 1026 collection[method](resp, options); 866 if (success) success (collection, resp, options);1027 if (success) success.call(options.context, collection, resp, options); 867 1028 collection.trigger('sync', collection, resp, options); 868 1029 }; 869 1030 wrapError(this, options); … … 875 1036 // wait for the server to agree. 876 1037 create: function(model, options) { 877 1038 options = options ? _.clone(options) : {}; 878 if (!(model = this._prepareModel(model, options))) return false; 879 if (!options.wait) this.add(model, options); 1039 var wait = options.wait; 1040 model = this._prepareModel(model, options); 1041 if (!model) return false; 1042 if (!wait) this.add(model, options); 880 1043 var collection = this; 881 1044 var success = options.success; 882 options.success = function(model, resp ) {883 if ( options.wait) collection.add(model, options);884 if (success) success (model, resp, options);1045 options.success = function(model, resp, callbackOpts) { 1046 if (wait) collection.add(model, callbackOpts); 1047 if (success) success.call(callbackOpts.context, model, resp, callbackOpts); 885 1048 }; 886 1049 model.save(null, options); 887 1050 return model; … … 895 1058 896 1059 // Create a new collection with an identical list of models as this one. 897 1060 clone: function() { 898 return new this.constructor(this.models); 1061 return new this.constructor(this.models, { 1062 model: this.model, 1063 comparator: this.comparator 1064 }); 899 1065 }, 900 1066 1067 // Define how to uniquely identify models in the collection. 1068 modelId: function (attrs) { 1069 return attrs[this.model.prototype.idAttribute || 'id']; 1070 }, 1071 901 1072 // Private method to reset all internal state. Called when the collection 902 1073 // is first initialized or reset. 903 1074 _reset: function() { … … 909 1080 // Prepare a hash of attributes (or other model) to be added to this 910 1081 // collection. 911 1082 _prepareModel: function(attrs, options) { 912 if (attrs instanceof Model) return attrs; 1083 if (this._isModel(attrs)) { 1084 if (!attrs.collection) attrs.collection = this; 1085 return attrs; 1086 } 913 1087 options = options ? _.clone(options) : {}; 914 1088 options.collection = this; 915 1089 var model = new this.model(attrs, options); … … 918 1092 return false; 919 1093 }, 920 1094 1095 // Internal method called by both remove and set. 1096 _removeModels: function(models, options) { 1097 var removed = []; 1098 for (var i = 0; i < models.length; i++) { 1099 var model = this.get(models[i]); 1100 if (!model) continue; 1101 1102 var index = this.indexOf(model); 1103 this.models.splice(index, 1); 1104 this.length--; 1105 1106 if (!options.silent) { 1107 options.index = index; 1108 model.trigger('remove', model, this, options); 1109 } 1110 1111 removed.push(model); 1112 this._removeReference(model, options); 1113 } 1114 return removed.length ? removed : false; 1115 }, 1116 1117 // Method for checking whether an object should be considered a model for 1118 // the purposes of adding to the collection. 1119 _isModel: function (model) { 1120 return model instanceof Model; 1121 }, 1122 921 1123 // Internal method to create a model's ties to a collection. 922 1124 _addReference: function(model, options) { 923 1125 this._byId[model.cid] = model; 924 if (model.id != null) this._byId[model.id] = model;925 if ( !model.collection) model.collection = this;1126 var id = this.modelId(model.attributes); 1127 if (id != null) this._byId[id] = model; 926 1128 model.on('all', this._onModelEvent, this); 927 1129 }, 928 1130 929 1131 // Internal method to sever a model's ties to a collection. 930 1132 _removeReference: function(model, options) { 1133 delete this._byId[model.cid]; 1134 var id = this.modelId(model.attributes); 1135 if (id != null) delete this._byId[id]; 931 1136 if (this === model.collection) delete model.collection; 932 1137 model.off('all', this._onModelEvent, this); 933 1138 }, … … 939 1144 _onModelEvent: function(event, model, collection, options) { 940 1145 if ((event === 'add' || event === 'remove') && collection !== this) return; 941 1146 if (event === 'destroy') this.remove(model, options); 942 if (model && event === 'change:' + model.idAttribute) { 943 delete this._byId[model.previous(model.idAttribute)]; 944 if (model.id != null) this._byId[model.id] = model; 1147 if (event === 'change') { 1148 var prevId = this.modelId(model.previousAttributes()); 1149 var id = this.modelId(model.attributes); 1150 if (prevId !== id) { 1151 if (prevId != null) delete this._byId[prevId]; 1152 if (id != null) this._byId[id] = model; 1153 } 945 1154 } 946 1155 this.trigger.apply(this, arguments); 947 1156 } … … 951 1160 // Underscore methods that we want to implement on the Collection. 952 1161 // 90% of the core usefulness of Backbone Collections is actually implemented 953 1162 // right here: 954 var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', 955 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', 956 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 957 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', 958 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', 959 'lastIndexOf', 'isEmpty', 'chain', 'sample']; 1163 var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4, 1164 foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3, 1165 select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3, 1166 contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3, 1167 head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3, 1168 without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3, 1169 isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3, 1170 sortBy: 3, indexBy: 3}; 960 1171 961 1172 // Mix in each Underscore method as a proxy to `Collection#models`. 962 _.each(methods, function(method) { 963 Collection.prototype[method] = function() { 964 var args = slice.call(arguments); 965 args.unshift(this.models); 966 return _[method].apply(_, args); 967 }; 968 }); 1173 addUnderscoreMethods(Collection, collectionMethods, 'models'); 969 1174 970 // Underscore methods that take a property name as an argument.971 var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];972 973 // Use attributes instead of properties.974 _.each(attributeMethods, function(method) {975 Collection.prototype[method] = function(value, context) {976 var iterator = _.isFunction(value) ? value : function(model) {977 return model.get(value);978 };979 return _[method](this.models, iterator, context);980 };981 });982 983 1175 // Backbone.View 984 1176 // ------------- 985 1177 … … 995 1187 // if an existing element is not provided... 996 1188 var View = Backbone.View = function(options) { 997 1189 this.cid = _.uniqueId('view'); 998 options || (options = {});999 1190 _.extend(this, _.pick(options, viewOptions)); 1000 1191 this._ensureElement(); 1001 1192 this.initialize.apply(this, arguments); 1002 this.delegateEvents();1003 1193 }; 1004 1194 1005 1195 // Cached regex to split keys for `delegate`. 1006 1196 var delegateEventSplitter = /^(\S+)\s*(.*)$/; 1007 1197 1008 // List of view options to be mergedas properties.1198 // List of view options to be set as properties. 1009 1199 var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; 1010 1200 1011 1201 // Set up all inheritable **Backbone.View** properties and methods. … … 1034 1224 // Remove this view by taking the element out of the DOM, and removing any 1035 1225 // applicable Backbone.Events listeners. 1036 1226 remove: function() { 1037 this. $el.remove();1227 this._removeElement(); 1038 1228 this.stopListening(); 1039 1229 return this; 1040 1230 }, 1041 1231 1042 // Change the view's element (`this.el` property), including event 1043 // re-delegation. 1044 setElement: function(element, delegate) { 1045 if (this.$el) this.undelegateEvents(); 1046 this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); 1047 this.el = this.$el[0]; 1048 if (delegate !== false) this.delegateEvents(); 1232 // Remove this view's element from the document and all event listeners 1233 // attached to it. Exposed for subclasses using an alternative DOM 1234 // manipulation API. 1235 _removeElement: function() { 1236 this.$el.remove(); 1237 }, 1238 1239 // Change the view's element (`this.el` property) and re-delegate the 1240 // view's events on the new element. 1241 setElement: function(element) { 1242 this.undelegateEvents(); 1243 this._setElement(element); 1244 this.delegateEvents(); 1049 1245 return this; 1050 1246 }, 1051 1247 1248 // Creates the `this.el` and `this.$el` references for this view using the 1249 // given `el`. `el` can be a CSS selector or an HTML string, a jQuery 1250 // context or an element. Subclasses can override this to utilize an 1251 // alternative DOM manipulation API and are only required to set the 1252 // `this.el` property. 1253 _setElement: function(el) { 1254 this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); 1255 this.el = this.$el[0]; 1256 }, 1257 1052 1258 // Set callbacks, where `this.events` is a hash of 1053 1259 // 1054 1260 // *{"event selector": "callback"}* … … 1062 1268 // pairs. Callbacks will be bound to the view, with `this` set properly. 1063 1269 // Uses event delegation for efficiency. 1064 1270 // Omitting the selector binds the event to `this.el`. 1065 // This only works for delegate-able events: not `focus`, `blur`, and1066 // not `change`, `submit`, and `reset` in Internet Explorer.1067 1271 delegateEvents: function(events) { 1068 if (!(events || (events = _.result(this, 'events')))) return this; 1272 events || (events = _.result(this, 'events')); 1273 if (!events) return this; 1069 1274 this.undelegateEvents(); 1070 1275 for (var key in events) { 1071 1276 var method = events[key]; 1072 if (!_.isFunction(method)) method = this[ events[key]];1277 if (!_.isFunction(method)) method = this[method]; 1073 1278 if (!method) continue; 1074 1075 1279 var match = key.match(delegateEventSplitter); 1076 var eventName = match[1], selector = match[2]; 1077 method = _.bind(method, this); 1078 eventName += '.delegateEvents' + this.cid; 1079 if (selector === '') { 1080 this.$el.on(eventName, method); 1081 } else { 1082 this.$el.on(eventName, selector, method); 1083 } 1280 this.delegate(match[1], match[2], _.bind(method, this)); 1084 1281 } 1085 1282 return this; 1086 1283 }, 1087 1284 1088 // Clears all callbacks previously bound to the view with `delegateEvents`. 1285 // Add a single event listener to the view's element (or a child element 1286 // using `selector`). This only works for delegate-able events: not `focus`, 1287 // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer. 1288 delegate: function(eventName, selector, listener) { 1289 this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener); 1290 return this; 1291 }, 1292 1293 // Clears all callbacks previously bound to the view by `delegateEvents`. 1089 1294 // You usually don't need to use this, but may wish to if you have multiple 1090 1295 // Backbone views attached to the same DOM element. 1091 1296 undelegateEvents: function() { 1092 this.$el.off('.delegateEvents' + this.cid);1297 if (this.$el) this.$el.off('.delegateEvents' + this.cid); 1093 1298 return this; 1094 1299 }, 1095 1300 1301 // A finer-grained `undelegateEvents` for removing a single delegated event. 1302 // `selector` and `listener` are both optional. 1303 undelegate: function(eventName, selector, listener) { 1304 this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener); 1305 return this; 1306 }, 1307 1308 // Produces a DOM element to be assigned to your view. Exposed for 1309 // subclasses using an alternative DOM manipulation API. 1310 _createElement: function(tagName) { 1311 return document.createElement(tagName); 1312 }, 1313 1096 1314 // Ensure that the View has a DOM element to render into. 1097 1315 // If `this.el` is a string, pass it through `$()`, take the first 1098 1316 // matching element, and re-assign it to `el`. Otherwise, create … … 1102 1320 var attrs = _.extend({}, _.result(this, 'attributes')); 1103 1321 if (this.id) attrs.id = _.result(this, 'id'); 1104 1322 if (this.className) attrs['class'] = _.result(this, 'className'); 1105 var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);1106 this. setElement($el, false);1323 this.setElement(this._createElement(_.result(this, 'tagName'))); 1324 this._setAttributes(attrs); 1107 1325 } else { 1108 this.setElement(_.result(this, 'el') , false);1326 this.setElement(_.result(this, 'el')); 1109 1327 } 1328 }, 1329 1330 // Set attributes from a hash on this view's element. Exposed for 1331 // subclasses using an alternative DOM manipulation API. 1332 _setAttributes: function(attributes) { 1333 this.$el.attr(attributes); 1110 1334 } 1111 1335 1112 1336 }); … … 1175 1399 params.processData = false; 1176 1400 } 1177 1401 1178 // If we're sending a `PATCH` request, and we're in an old Internet Explorer 1179 // that still has ActiveX enabled by default, override jQuery to use that 1180 // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. 1181 if (params.type === 'PATCH' && noXhrPatch) { 1182 params.xhr = function() { 1183 return new ActiveXObject("Microsoft.XMLHTTP"); 1184 }; 1185 } 1402 // Pass along `textStatus` and `errorThrown` from jQuery. 1403 var error = options.error; 1404 options.error = function(xhr, textStatus, errorThrown) { 1405 options.textStatus = textStatus; 1406 options.errorThrown = errorThrown; 1407 if (error) error.call(options.context, xhr, textStatus, errorThrown); 1408 }; 1186 1409 1187 1410 // Make the request, allowing the user to override any Ajax options. 1188 1411 var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); … … 1190 1413 return xhr; 1191 1414 }; 1192 1415 1193 var noXhrPatch =1194 typeof window !== 'undefined' && !!window.ActiveXObject &&1195 !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);1196 1197 1416 // Map from CRUD to HTTP for our default `Backbone.sync` implementation. 1198 1417 var methodMap = { 1199 1418 'create': 'POST', … … 1251 1470 var router = this; 1252 1471 Backbone.history.route(route, function(fragment) { 1253 1472 var args = router._extractParameters(route, fragment); 1254 router.execute(callback, args); 1255 router.trigger.apply(router, ['route:' + name].concat(args)); 1256 router.trigger('route', name, args); 1257 Backbone.history.trigger('route', router, name, args); 1473 if (router.execute(callback, args, name) !== false) { 1474 router.trigger.apply(router, ['route:' + name].concat(args)); 1475 router.trigger('route', name, args); 1476 Backbone.history.trigger('route', router, name, args); 1477 } 1258 1478 }); 1259 1479 return this; 1260 1480 }, … … 1261 1481 1262 1482 // Execute a route handler with the provided parameters. This is an 1263 1483 // excellent place to do pre-route setup or post-route cleanup. 1264 execute: function(callback, args ) {1484 execute: function(callback, args, name) { 1265 1485 if (callback) callback.apply(this, args); 1266 1486 }, 1267 1487 … … 1319 1539 // falls back to polling. 1320 1540 var History = Backbone.History = function() { 1321 1541 this.handlers = []; 1322 _.bindAll(this, 'checkUrl');1542 this.checkUrl = _.bind(this.checkUrl, this); 1323 1543 1324 1544 // Ensure that `History` can be used outside of the browser. 1325 1545 if (typeof window !== 'undefined') { … … 1334 1554 // Cached regex for stripping leading and trailing slashes. 1335 1555 var rootStripper = /^\/+|\/+$/g; 1336 1556 1337 // Cached regex for detecting MSIE.1338 var isExplorer = /msie [\w.]+/;1339 1340 // Cached regex for removing a trailing slash.1341 var trailingSlash = /\/$/;1342 1343 1557 // Cached regex for stripping urls of hash. 1344 1558 var pathStripper = /#.*$/; 1345 1559 … … 1355 1569 1356 1570 // Are we at the app root? 1357 1571 atRoot: function() { 1358 return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root; 1572 var path = this.location.pathname.replace(/[^\/]$/, '$&/'); 1573 return path === this.root && !this.getSearch(); 1359 1574 }, 1360 1575 1576 // Does the pathname match the root? 1577 matchRoot: function() { 1578 var path = this.decodeFragment(this.location.pathname); 1579 var root = path.slice(0, this.root.length - 1) + '/'; 1580 return root === this.root; 1581 }, 1582 1583 // Unicode characters in `location.pathname` are percent encoded so they're 1584 // decoded for comparison. `%25` should not be decoded since it may be part 1585 // of an encoded parameter. 1586 decodeFragment: function(fragment) { 1587 return decodeURI(fragment.replace(/%25/g, '%2525')); 1588 }, 1589 1590 // In IE6, the hash fragment and search params are incorrect if the 1591 // fragment contains `?`. 1592 getSearch: function() { 1593 var match = this.location.href.replace(/#.*/, '').match(/\?.+/); 1594 return match ? match[0] : ''; 1595 }, 1596 1361 1597 // Gets the true hash value. Cannot use location.hash directly due to bug 1362 1598 // in Firefox where location.hash will always be decoded. 1363 1599 getHash: function(window) { … … 1365 1601 return match ? match[1] : ''; 1366 1602 }, 1367 1603 1368 // Get the cross-browser normalized URL fragment, either from the URL, 1369 // the hash, or the override. 1370 getFragment: function(fragment, forcePushState) { 1604 // Get the pathname and search params, without the root. 1605 getPath: function() { 1606 var path = this.decodeFragment( 1607 this.location.pathname + this.getSearch() 1608 ).slice(this.root.length - 1); 1609 return path.charAt(0) === '/' ? path.slice(1) : path; 1610 }, 1611 1612 // Get the cross-browser normalized URL fragment from the path or hash. 1613 getFragment: function(fragment) { 1371 1614 if (fragment == null) { 1372 if (this._hasPushState || !this._wantsHashChange || forcePushState) { 1373 fragment = decodeURI(this.location.pathname + this.location.search); 1374 var root = this.root.replace(trailingSlash, ''); 1375 if (!fragment.indexOf(root)) fragment = fragment.slice(root.length); 1615 if (this._usePushState || !this._wantsHashChange) { 1616 fragment = this.getPath(); 1376 1617 } else { 1377 1618 fragment = this.getHash(); 1378 1619 } … … 1383 1624 // Start the hash change handling, returning `true` if the current URL matches 1384 1625 // an existing route, and `false` otherwise. 1385 1626 start: function(options) { 1386 if (History.started) throw new Error( "Backbone.history has already been started");1627 if (History.started) throw new Error('Backbone.history has already been started'); 1387 1628 History.started = true; 1388 1629 1389 1630 // Figure out the initial configuration. Do we need an iframe? … … 1391 1632 this.options = _.extend({root: '/'}, this.options, options); 1392 1633 this.root = this.options.root; 1393 1634 this._wantsHashChange = this.options.hashChange !== false; 1635 this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7); 1636 this._useHashChange = this._wantsHashChange && this._hasHashChange; 1394 1637 this._wantsPushState = !!this.options.pushState; 1395 this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); 1396 var fragment = this.getFragment(); 1397 var docMode = document.documentMode; 1398 var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); 1638 this._hasPushState = !!(this.history && this.history.pushState); 1639 this._usePushState = this._wantsPushState && this._hasPushState; 1640 this.fragment = this.getFragment(); 1399 1641 1400 1642 // Normalize root to always include a leading and trailing slash. 1401 1643 this.root = ('/' + this.root + '/').replace(rootStripper, '/'); 1402 1644 1403 if (oldIE && this._wantsHashChange) {1404 var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');1405 this.iframe = frame.hide().appendTo('body')[0].contentWindow;1406 this.navigate(fragment);1407 }1408 1409 // Depending on whether we're using pushState or hashes, and whether1410 // 'onhashchange' is supported, determine how we check the URL state.1411 if (this._hasPushState) {1412 Backbone.$(window).on('popstate', this.checkUrl);1413 } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {1414 Backbone.$(window).on('hashchange', this.checkUrl);1415 } else if (this._wantsHashChange) {1416 this._checkUrlInterval = setInterval(this.checkUrl, this.interval);1417 }1418 1419 // Determine if we need to change the base url, for a pushState link1420 // opened by a non-pushState browser.1421 this.fragment = fragment;1422 var loc = this.location;1423 1424 1645 // Transition from hashChange to pushState or vice versa if both are 1425 1646 // requested. 1426 1647 if (this._wantsHashChange && this._wantsPushState) { … … 1428 1649 // If we've started off with a route from a `pushState`-enabled 1429 1650 // browser, but we're currently in a browser that doesn't support it... 1430 1651 if (!this._hasPushState && !this.atRoot()) { 1431 this.fragment = this.getFragment(null, true);1432 this.location.replace( this.root + '#' + this.fragment);1652 var root = this.root.slice(0, -1) || '/'; 1653 this.location.replace(root + '#' + this.getPath()); 1433 1654 // Return immediately as browser will do redirect to new url 1434 1655 return true; 1435 1656 1436 1657 // Or if we've started out with a hash-based route, but we're currently 1437 1658 // in a browser where it could be `pushState`-based instead... 1438 } else if (this._hasPushState && this.atRoot() && loc.hash) { 1439 this.fragment = this.getHash().replace(routeStripper, ''); 1440 this.history.replaceState({}, document.title, this.root + this.fragment); 1659 } else if (this._hasPushState && this.atRoot()) { 1660 this.navigate(this.getHash(), {replace: true}); 1441 1661 } 1442 1662 1443 1663 } 1444 1664 1665 // Proxy an iframe to handle location events if the browser doesn't 1666 // support the `hashchange` event, HTML5 history, or the user wants 1667 // `hashChange` but not `pushState`. 1668 if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) { 1669 this.iframe = document.createElement('iframe'); 1670 this.iframe.src = 'javascript:0'; 1671 this.iframe.style.display = 'none'; 1672 this.iframe.tabIndex = -1; 1673 var body = document.body; 1674 // Using `appendChild` will throw on IE < 9 if the document is not ready. 1675 var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow; 1676 iWindow.document.open(); 1677 iWindow.document.close(); 1678 iWindow.location.hash = '#' + this.fragment; 1679 } 1680 1681 // Add a cross-platform `addEventListener` shim for older browsers. 1682 var addEventListener = window.addEventListener || function (eventName, listener) { 1683 return attachEvent('on' + eventName, listener); 1684 }; 1685 1686 // Depending on whether we're using pushState or hashes, and whether 1687 // 'onhashchange' is supported, determine how we check the URL state. 1688 if (this._usePushState) { 1689 addEventListener('popstate', this.checkUrl, false); 1690 } else if (this._useHashChange && !this.iframe) { 1691 addEventListener('hashchange', this.checkUrl, false); 1692 } else if (this._wantsHashChange) { 1693 this._checkUrlInterval = setInterval(this.checkUrl, this.interval); 1694 } 1695 1445 1696 if (!this.options.silent) return this.loadUrl(); 1446 1697 }, 1447 1698 … … 1448 1699 // Disable Backbone.history, perhaps temporarily. Not useful in a real app, 1449 1700 // but possibly useful for unit testing Routers. 1450 1701 stop: function() { 1451 Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl); 1702 // Add a cross-platform `removeEventListener` shim for older browsers. 1703 var removeEventListener = window.removeEventListener || function (eventName, listener) { 1704 return detachEvent('on' + eventName, listener); 1705 }; 1706 1707 // Remove window listeners. 1708 if (this._usePushState) { 1709 removeEventListener('popstate', this.checkUrl, false); 1710 } else if (this._useHashChange && !this.iframe) { 1711 removeEventListener('hashchange', this.checkUrl, false); 1712 } 1713 1714 // Clean up the iframe if necessary. 1715 if (this.iframe) { 1716 document.body.removeChild(this.iframe); 1717 this.iframe = null; 1718 } 1719 1720 // Some environments will throw when clearing an undefined interval. 1452 1721 if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); 1453 1722 History.started = false; 1454 1723 }, … … 1463 1732 // calls `loadUrl`, normalizing across the hidden iframe. 1464 1733 checkUrl: function(e) { 1465 1734 var current = this.getFragment(); 1735 1736 // If the user pressed the back button, the iframe's hash will have 1737 // changed and we should use that for comparison. 1466 1738 if (current === this.fragment && this.iframe) { 1467 current = this.get Fragment(this.getHash(this.iframe));1739 current = this.getHash(this.iframe.contentWindow); 1468 1740 } 1741 1469 1742 if (current === this.fragment) return false; 1470 1743 if (this.iframe) this.navigate(current); 1471 1744 this.loadUrl(); … … 1475 1748 // match, returns `true`. If no defined routes matches the fragment, 1476 1749 // returns `false`. 1477 1750 loadUrl: function(fragment) { 1751 // If the root doesn't match, no routes can match either. 1752 if (!this.matchRoot()) return false; 1478 1753 fragment = this.fragment = this.getFragment(fragment); 1479 return _. any(this.handlers, function(handler) {1754 return _.some(this.handlers, function(handler) { 1480 1755 if (handler.route.test(fragment)) { 1481 1756 handler.callback(fragment); 1482 1757 return true; … … 1495 1770 if (!History.started) return false; 1496 1771 if (!options || options === true) options = {trigger: !!options}; 1497 1772 1498 var url = this.root + (fragment = this.getFragment(fragment || '')); 1773 // Normalize the fragment. 1774 fragment = this.getFragment(fragment || ''); 1499 1775 1500 // Strip the hash for matching. 1501 fragment = fragment.replace(pathStripper, ''); 1776 // Don't include a trailing slash on the root. 1777 var root = this.root; 1778 if (fragment === '' || fragment.charAt(0) === '?') { 1779 root = root.slice(0, -1) || '/'; 1780 } 1781 var url = root + fragment; 1502 1782 1783 // Strip the hash and decode for matching. 1784 fragment = this.decodeFragment(fragment.replace(pathStripper, '')); 1785 1503 1786 if (this.fragment === fragment) return; 1504 1787 this.fragment = fragment; 1505 1788 1506 // Don't include a trailing slash on the root.1507 if (fragment === '' && url !== '/') url = url.slice(0, -1);1508 1509 1789 // If pushState is available, we use it to set the fragment as a real URL. 1510 if (this._ hasPushState) {1790 if (this._usePushState) { 1511 1791 this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); 1512 1792 1513 1793 // If hash changes haven't been explicitly disabled, update the hash … … 1514 1794 // fragment to store history. 1515 1795 } else if (this._wantsHashChange) { 1516 1796 this._updateHash(this.location, fragment, options.replace); 1517 if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) { 1797 if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) { 1798 var iWindow = this.iframe.contentWindow; 1799 1518 1800 // Opening and closing the iframe tricks IE7 and earlier to push a 1519 1801 // history entry on hash-tag change. When replace is true, we don't 1520 1802 // want this. 1521 if(!options.replace) this.iframe.document.open().close(); 1522 this._updateHash(this.iframe.location, fragment, options.replace); 1803 if (!options.replace) { 1804 iWindow.document.open(); 1805 iWindow.document.close(); 1806 } 1807 1808 this._updateHash(iWindow.location, fragment, options.replace); 1523 1809 } 1524 1810 1525 1811 // If you've told us that you explicitly don't want fallback hashchange- … … 1550 1836 // Helpers 1551 1837 // ------- 1552 1838 1553 // Helper function to correctly set up the prototype chain ,for subclasses.1839 // Helper function to correctly set up the prototype chain for subclasses. 1554 1840 // Similar to `goog.inherits`, but uses a hash of prototype properties and 1555 1841 // class properties to be extended. 1556 1842 var extend = function(protoProps, staticProps) { … … 1559 1845 1560 1846 // The constructor function for the new subclass is either defined by you 1561 1847 // (the "constructor" property in your `extend` definition), or defaulted 1562 // by us to simply call the parent 'sconstructor.1848 // by us to simply call the parent constructor. 1563 1849 if (protoProps && _.has(protoProps, 'constructor')) { 1564 1850 child = protoProps.constructor; 1565 1851 } else { … … 1570 1856 _.extend(child, parent, staticProps); 1571 1857 1572 1858 // Set the prototype chain to inherit from `parent`, without calling 1573 // `parent` 'sconstructor function.1859 // `parent` constructor function. 1574 1860 var Surrogate = function(){ this.constructor = child; }; 1575 1861 Surrogate.prototype = parent.prototype; 1576 1862 child.prototype = new Surrogate; … … 1598 1884 var wrapError = function(model, options) { 1599 1885 var error = options.error; 1600 1886 options.error = function(resp) { 1601 if (error) error (model, resp, options);1887 if (error) error.call(options.context, model, resp, options); 1602 1888 model.trigger('error', model, resp, options); 1603 1889 }; 1604 1890 }; -
src/wp-includes/js/backbone.min.js
1 (function(t ,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h<u;h++){t=o[h];if(a=this._events[t]){this._events[t]=s=[];if(e||r){for(l=0,f=a.length;l<f;l++){n=a[l];if(e&&e!==n.callback&&e!==n.callback._callback||r&&r!==n.context){s.push(n)}}}if(!s.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=o.call(arguments,1);if(!c(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)f(i,e);if(r)f(r,arguments);return this},stopListening:function(t,e,r){var s=this._listeningTo;if(!s)return this;var n=!e&&!r;if(!r&&typeof e==="object")r=this;if(t)(s={})[t._listenId]=t;for(var a in s){t=s[a];t.off(e,r,this);if(n||i.isEmpty(t._events))delete this._listeningTo[a]}return this}};var l=/\s+/;var c=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(l.test(i)){var n=i.split(l);for(var a=0,o=n.length;a<o;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var f=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,o);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e);return}};var d={listenTo:"on",listenToOnce:"once"};i.each(d,function(t,e){u[e]=function(e,r,s){var n=this._listeningTo||(this._listeningTo={});var a=e._listenId||(e._listenId=i.uniqueId("l"));n[a]=e;if(!s&&typeof r==="object")s=this;e[t](r,s,this);return this}});u.bind=u.on;u.unbind=u.off;i.extend(e,u);var p=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId("c");this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(p.prototype,u,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,r){var s,n,a,o,h,u,l,c;if(t==null)return this;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;a=r.unset;h=r.silent;o=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=i.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in n)this.id=n[this.idAttribute];for(s in n){e=n[s];if(!i.isEqual(c[s],e))o.push(s);if(!i.isEqual(l[s],e)){this.changed[s]=e}else{delete this.changed[s]}a?delete c[s]:c[s]=e}if(!h){if(o.length)this._pending=r;for(var f=0,d=o.length;f<d;f++){this.trigger("change:"+o[f],this,c[o[f]],r)}}if(u)return this;if(!h){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e,r=false;var s=this._changing?this._previousAttributes:this.attributes;for(var n in t){if(i.isEqual(s[n],e=t[n]))continue;(r||(r={}))[n]=e}return r},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var r=t.success;t.success=function(i){if(!e.set(e.parse(i,t),t))return false;if(r)r(e,i,t);e.trigger("sync",e,i,t)};q(this,t);return this.sync("read",this,t)},save:function(t,e,r){var s,n,a,o=this.attributes;if(t==null||typeof t==="object"){s=t;r=e}else{(s={})[t]=e}r=i.extend({validate:true},r);if(s&&!r.wait){if(!this.set(s,r))return false}else{if(!this._validate(s,r))return false}if(s&&r.wait){this.attributes=i.extend({},o,s)}if(r.parse===void 0)r.parse=true;var h=this;var u=r.success;r.success=function(t){h.attributes=o;var e=h.parse(t,r);if(r.wait)e=i.extend(s||{},e);if(i.isObject(e)&&!h.set(e,r)){return false}if(u)u(h,t,r);h.trigger("sync",h,t,r)};q(this,r);n=this.isNew()?"create":r.patch?"patch":"update";if(n==="patch")r.attrs=s;a=this.sync(n,this,r);if(s&&r.wait)this.attributes=o;return a},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var s=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(t.wait||e.isNew())s();if(r)r(e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};if(this.isNew()){t.success();return false}q(this,t);var n=this.sync("delete",this,t);if(!t.wait)s();return n},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||M();if(this.isNew())return t;return t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var v=["keys","values","pairs","invert","pick","omit"];i.each(v,function(t){p.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.attributes);return i[t].apply(i,e)}});var g=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,remove:false};i.extend(g.prototype,u,{model:p,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,y))},remove:function(t,e){var r=!i.isArray(t);t=r?[t]:i.clone(t);e||(e={});var s,n,a,o;for(s=0,n=t.length;s<n;s++){o=t[s]=this.get(t[s]);if(!o)continue;delete this._byId[o.id];delete this._byId[o.cid];a=this.indexOf(o);this.models.splice(a,1);this.length--;if(!e.silent){e.index=a;o.trigger("remove",o,this,e)}this._removeReference(o,e)}return r?t[0]:t},set:function(t,e){e=i.defaults({},e,m);if(e.parse)t=this.parse(t,e);var r=!i.isArray(t);t=r?t?[t]:[]:i.clone(t);var s,n,a,o,h,u,l;var c=e.at;var f=this.model;var d=this.comparator&&c==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g=[],y=[],_={};var b=e.add,w=e.merge,x=e.remove;var E=!d&&b&&x?[]:false;for(s=0,n=t.length;s<n;s++){h=t[s]||{};if(h instanceof p){a=o=h}else{a=h[f.prototype.idAttribute||"id"]}if(u=this.get(a)){if(x)_[u.cid]=true;if(w){h=h===o?o.attributes:h;if(e.parse)h=u.parse(h,e);u.set(h,e);if(d&&!l&&u.hasChanged(v))l=true}t[s]=u}else if(b){o=t[s]=this._prepareModel(h,e);if(!o)continue;g.push(o);this._addReference(o,e)}o=u||o;if(E&&(o.isNew()||!_[o.id]))E.push(o);_[o.id]=true}if(x){for(s=0,n=this.length;s<n;++s){if(!_[(o=this.models[s]).cid])y.push(o)}if(y.length)this.remove(y,e)}if(g.length||E&&E.length){if(d)l=true;this.length+=g.length;if(c!=null){for(s=0,n=g.length;s<n;s++){this.models.splice(c+s,0,g[s])}}else{if(E)this.models.length=0;var k=E||g;for(s=0,n=k.length;s<n;s++){this.models.push(k[s])}}}if(l)this.sort({silent:true});if(!e.silent){for(s=0,n=g.length;s<n;s++){(o=g[s]).trigger("add",o,this,e)}if(l||E&&E.length)this.trigger("sort",this,e)}return r?t[0]:t},reset:function(t,e){e||(e={});for(var r=0,s=this.models.length;r<s;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(){return o.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(t,e){if(i.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(i.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(i.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var r=this;t.success=function(i){var s=t.reset?"reset":"set";r[s](i,t);if(e)e(r,i,t);r.trigger("sync",r,i,t)};q(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var r=this;var s=e.success;e.success=function(t,i){if(e.wait)r.add(t,e);if(s)s(t,i,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof p)return t;e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_addReference:function(t,e){this._byId[t.cid]=t;if(t.id!=null)this._byId[t.id]=t;if(!t.collection)t.collection=this;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","difference","indexOf","shuffle","lastIndexOf","isEmpty","chain","sample"];i.each(_,function(t){g.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.models);return i[t].apply(i,e)}});var b=["groupBy","countBy","sortBy","indexBy"];i.each(b,function(t){g.prototype[t]=function(e,r){var s=i.isFunction(e)?e:function(t){return t.get(e)};return i[t](this.models,s,r)}});var w=e.View=function(t){this.cid=i.uniqueId("view");t||(t={});i.extend(this,i.pick(t,E));this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];i.extend(w.prototype,u,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,i){if(this.$el)this.undelegateEvents();this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0];if(i!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=i.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[t[e]];if(!r)continue;var s=e.match(x);var n=s[1],a=s[2];r=i.bind(r,this);n+=".delegateEvents"+this.cid;if(a===""){this.$el.on(n,r)}else{this.$el.on(n,a,r)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");var r=e.$("<"+i.result(this,"tagName")+">").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=a.hide().appendTo("body")[0].contentWindow;this.navigate(r)}if(this._hasPushState){e.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!n){e.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=r;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){this.fragment=this.getFragment(null,true);this.location.replace(this.root+"#"+this.fragment);return true}else if(this._hasPushState&&this.atRoot()&&o.hash){this.fragment=this.getHash().replace(R,"");this.history.replaceState({},document.title,this.root+this.fragment)}}if(!this.options.silent)return this.loadUrl()},stop:function(){e.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){t=this.fragment=this.getFragment(t);return i.any(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};var i=this.root+(t=this.getFragment(t||""));t=t.replace(j,"");if(this.fragment===t)return;this.fragment=t;if(t===""&&i!=="/")i=i.slice(0,-1);if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var U=function(t,e){var r=this;var s;if(t&&i.has(t,"constructor")){s=t.constructor}else{s=function(){return r.apply(this,arguments)}}i.extend(s,r,e);var n=function(){this.constructor=s};n.prototype=r.prototype;s.prototype=new n;if(t)i.extend(s.prototype,t);s.__super__=r.prototype;return s};p.extend=g.extend=$.extend=w.extend=N.extend=U;var M=function(){throw new Error('A "url" property or function must be specified')};var q=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}};return e});1 (function(t){var e=typeof self=="object"&&self.self==self&&self||typeof global=="object"&&global.global==global&&global;if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,n){e.Backbone=t(e,n,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore"),r;try{r=require("jquery")}catch(n){}t(e,exports,i,r)}else{e.Backbone=t(e,{},e._,e.jQuery||e.Zepto||e.ender||e.$)}})(function(t,e,i,r){var n=t.Backbone;var s=Array.prototype.slice;e.VERSION="1.2.3";e.$=r;e.noConflict=function(){t.Backbone=n;return this};e.emulateHTTP=false;e.emulateJSON=false;var a=function(t,e,r){switch(t){case 1:return function(){return i[e](this[r])};case 2:return function(t){return i[e](this[r],t)};case 3:return function(t,n){return i[e](this[r],h(t,this),n)};case 4:return function(t,n,s){return i[e](this[r],h(t,this),n,s)};default:return function(){var t=s.call(arguments);t.unshift(this[r]);return i[e].apply(i,t)}}};var o=function(t,e,r){i.each(e,function(e,n){if(i[n])t.prototype[n]=a(e,n,r)})};var h=function(t,e){if(i.isFunction(t))return t;if(i.isObject(t)&&!e._isModel(t))return u(t);if(i.isString(t))return function(e){return e.get(t)};return t};var u=function(t){var e=i.matches(t);return function(t){return e(t.attributes)}};var l=e.Events={};var c=/\s+/;var f=function(t,e,r,n,s){var a=0,o;if(r&&typeof r==="object"){if(n!==void 0&&"context"in s&&s.context===void 0)s.context=n;for(o=i.keys(r);a<o.length;a++){e=f(t,e,o[a],r[o[a]],s)}}else if(r&&c.test(r)){for(o=r.split(c);a<o.length;a++){e=t(e,o[a],n,s)}}else{e=t(e,r,n,s)}return e};l.on=function(t,e,i){return d(this,t,e,i)};var d=function(t,e,i,r,n){t._events=f(v,t._events||{},e,i,{context:r,ctx:t,listening:n});if(n){var s=t._listeners||(t._listeners={});s[n.id]=n}return t};l.listenTo=function(t,e,r){if(!t)return this;var n=t._listenId||(t._listenId=i.uniqueId("l"));var s=this._listeningTo||(this._listeningTo={});var a=s[n];if(!a){var o=this._listenId||(this._listenId=i.uniqueId("l"));a=s[n]={obj:t,objId:n,id:o,listeningTo:s,count:0}}d(t,e,r,this,a);return this};var v=function(t,e,i,r){if(i){var n=t[e]||(t[e]=[]);var s=r.context,a=r.ctx,o=r.listening;if(o)o.count++;n.push({callback:i,context:s,ctx:s||a,listening:o})}return t};l.off=function(t,e,i){if(!this._events)return this;this._events=f(g,this._events,t,e,{context:i,listeners:this._listeners});return this};l.stopListening=function(t,e,r){var n=this._listeningTo;if(!n)return this;var s=t?[t._listenId]:i.keys(n);for(var a=0;a<s.length;a++){var o=n[s[a]];if(!o)break;o.obj.off(e,r,this)}if(i.isEmpty(n))this._listeningTo=void 0;return this};var g=function(t,e,r,n){if(!t)return;var s=0,a;var o=n.context,h=n.listeners;if(!e&&!r&&!o){var u=i.keys(h);for(;s<u.length;s++){a=h[u[s]];delete h[a.id];delete a.listeningTo[a.objId]}return}var l=e?[e]:i.keys(t);for(;s<l.length;s++){e=l[s];var c=t[e];if(!c)break;var f=[];for(var d=0;d<c.length;d++){var v=c[d];if(r&&r!==v.callback&&r!==v.callback._callback||o&&o!==v.context){f.push(v)}else{a=v.listening;if(a&&--a.count===0){delete h[a.id];delete a.listeningTo[a.objId]}}}if(f.length){t[e]=f}else{delete t[e]}}if(i.size(t))return t};l.once=function(t,e,r){var n=f(p,{},t,e,i.bind(this.off,this));return this.on(n,void 0,r)};l.listenToOnce=function(t,e,r){var n=f(p,{},e,r,i.bind(this.stopListening,this,t));return this.listenTo(t,n)};var p=function(t,e,r,n){if(r){var s=t[e]=i.once(function(){n(e,s);r.apply(this,arguments)});s._callback=r}return t};l.trigger=function(t){if(!this._events)return this;var e=Math.max(0,arguments.length-1);var i=Array(e);for(var r=0;r<e;r++)i[r]=arguments[r+1];f(m,this._events,t,void 0,i);return this};var m=function(t,e,i,r){if(t){var n=t[e];var s=t.all;if(n&&s)s=s.slice();if(n)_(n,r);if(s)_(s,[e].concat(r))}return t};var _=function(t,e){var i,r=-1,n=t.length,s=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<n)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<n)(i=t[r]).callback.call(i.ctx,s);return;case 2:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a);return;case 3:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a,o);return;default:while(++r<n)(i=t[r]).callback.apply(i.ctx,e);return}};l.bind=l.on;l.unbind=l.off;i.extend(e,l);var y=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId(this.cidPrefix);this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(y.prototype,l,{changed:null,validationError:null,idAttribute:"id",cidPrefix:"c",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},matches:function(t){return!!i.iteratee(t,this)(this.attributes)},set:function(t,e,r){if(t==null)return this;var n;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;var s=r.unset;var a=r.silent;var o=[];var h=this._changing;this._changing=true;if(!h){this._previousAttributes=i.clone(this.attributes);this.changed={}}var u=this.attributes;var l=this.changed;var c=this._previousAttributes;for(var f in n){e=n[f];if(!i.isEqual(u[f],e))o.push(f);if(!i.isEqual(c[f],e)){l[f]=e}else{delete l[f]}s?delete u[f]:u[f]=e}this.id=this.get(this.idAttribute);if(!a){if(o.length)this._pending=r;for(var d=0;d<o.length;d++){this.trigger("change:"+o[d],this,u[o[d]],r)}}if(h)return this;if(!a){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e=this._changing?this._previousAttributes:this.attributes;var r={};for(var n in t){var s=t[n];if(i.isEqual(e[n],s))continue;r[n]=s}return i.size(r)?r:false},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=i.extend({parse:true},t);var e=this;var r=t.success;t.success=function(i){var n=t.parse?e.parse(i,t):i;if(!e.set(n,t))return false;if(r)r.call(t.context,e,i,t);e.trigger("sync",e,i,t)};z(this,t);return this.sync("read",this,t)},save:function(t,e,r){var n;if(t==null||typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r=i.extend({validate:true,parse:true},r);var s=r.wait;if(n&&!s){if(!this.set(n,r))return false}else{if(!this._validate(n,r))return false}var a=this;var o=r.success;var h=this.attributes;r.success=function(t){a.attributes=h;var e=r.parse?a.parse(t,r):t;if(s)e=i.extend({},n,e);if(e&&!a.set(e,r))return false;if(o)o.call(r.context,a,t,r);a.trigger("sync",a,t,r)};z(this,r);if(n&&s)this.attributes=i.extend({},h,n);var u=this.isNew()?"create":r.patch?"patch":"update";if(u==="patch"&&!r.attrs)r.attrs=n;var l=this.sync(u,this,r);this.attributes=h;return l},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var n=t.wait;var s=function(){e.stopListening();e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(n)s();if(r)r.call(t.context,e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};var a=false;if(this.isNew()){i.defer(t.success)}else{z(this,t);a=this.sync("delete",this,t)}if(!n)s();return a},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||F();if(this.isNew())return t;var e=this.get(this.idAttribute);return t.replace(/[^\/]$/,"$&/")+encodeURIComponent(e)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.defaults({validate:true},t))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var b={keys:1,values:1,pairs:1,invert:1,pick:0,omit:0,chain:1,isEmpty:1};o(y,b,"attributes");var x=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var w={add:true,remove:true,merge:true};var E={add:true,remove:false};var k=function(t,e,i){i=Math.min(Math.max(i,0),t.length);var r=Array(t.length-i);var n=e.length;for(var s=0;s<r.length;s++)r[s]=t[s+i];for(s=0;s<n;s++)t[s+i]=e[s];for(s=0;s<r.length;s++)t[s+n+i]=r[s]};i.extend(x.prototype,l,{model:y,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,E))},remove:function(t,e){e=i.extend({},e);var r=!i.isArray(t);t=r?[t]:i.clone(t);var n=this._removeModels(t,e);if(!e.silent&&n)this.trigger("update",this,e);return r?n[0]:n},set:function(t,e){if(t==null)return;e=i.defaults({},e,w);if(e.parse&&!this._isModel(t))t=this.parse(t,e);var r=!i.isArray(t);t=r?[t]:t.slice();var n=e.at;if(n!=null)n=+n;if(n<0)n+=this.length+1;var s=[];var a=[];var o=[];var h={};var u=e.add;var l=e.merge;var c=e.remove;var f=false;var d=this.comparator&&n==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g;for(var p=0;p<t.length;p++){g=t[p];var m=this.get(g);if(m){if(l&&g!==m){var _=this._isModel(g)?g.attributes:g;if(e.parse)_=m.parse(_,e);m.set(_,e);if(d&&!f)f=m.hasChanged(v)}if(!h[m.cid]){h[m.cid]=true;s.push(m)}t[p]=m}else if(u){g=t[p]=this._prepareModel(g,e);if(g){a.push(g);this._addReference(g,e);h[g.cid]=true;s.push(g)}}}if(c){for(p=0;p<this.length;p++){g=this.models[p];if(!h[g.cid])o.push(g)}if(o.length)this._removeModels(o,e)}var y=false;var b=!d&&u&&c;if(s.length&&b){y=this.length!=s.length||i.some(this.models,function(t,e){return t!==s[e]});this.models.length=0;k(this.models,s,0);this.length=this.models.length}else if(a.length){if(d)f=true;k(this.models,a,n==null?this.length:n);this.length=this.models.length}if(f)this.sort({silent:true});if(!e.silent){for(p=0;p<a.length;p++){if(n!=null)e.index=n+p;g=a[p];g.trigger("add",g,this,e)}if(f||y)this.trigger("sort",this,e);if(a.length||o.length)this.trigger("update",this,e)}return r?t[0]:t},reset:function(t,e){e=e?i.clone(e):{};for(var r=0;r<this.models.length;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);return this.remove(e,t)},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);return this.remove(e,t)},slice:function(){return s.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;var e=this.modelId(this._isModel(t)?t.attributes:t);return this._byId[t]||this._byId[e]||this._byId[t.cid]},at:function(t){if(t<0)t+=this.length;return this.models[t]},where:function(t,e){return this[e?"find":"filter"](t)},findWhere:function(t){return this.where(t,true)},sort:function(t){var e=this.comparator;if(!e)throw new Error("Cannot sort a set without a comparator");t||(t={});var r=e.length;if(i.isFunction(e))e=i.bind(e,this);if(r===1||i.isString(e)){this.models=this.sortBy(e)}else{this.models.sort(e)}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=i.extend({parse:true},t);var e=t.success;var r=this;t.success=function(i){var n=t.reset?"reset":"set";r[n](i,t);if(e)e.call(t.context,r,i,t);r.trigger("sync",r,i,t)};z(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};var r=e.wait;t=this._prepareModel(t,e);if(!t)return false;if(!r)this.add(t,e);var n=this;var s=e.success;e.success=function(t,e,i){if(r)n.add(t,i);if(s)s.call(i.context,t,e,i)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models,{model:this.model,comparator:this.comparator})},modelId:function(t){return t[this.model.prototype.idAttribute||"id"]},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(this._isModel(t)){if(!t.collection)t.collection=this;return t}e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_removeModels:function(t,e){var i=[];for(var r=0;r<t.length;r++){var n=this.get(t[r]);if(!n)continue;var s=this.indexOf(n);this.models.splice(s,1);this.length--;if(!e.silent){e.index=s;n.trigger("remove",n,this,e)}i.push(n);this._removeReference(n,e)}return i.length?i:false},_isModel:function(t){return t instanceof y},_addReference:function(t,e){this._byId[t.cid]=t;var i=this.modelId(t.attributes);if(i!=null)this._byId[i]=t;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){delete this._byId[t.cid];var i=this.modelId(t.attributes);if(i!=null)delete this._byId[i];if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(t==="change"){var n=this.modelId(e.previousAttributes());var s=this.modelId(e.attributes);if(n!==s){if(n!=null)delete this._byId[n];if(s!=null)this._byId[s]=e}}this.trigger.apply(this,arguments)}});var S={forEach:3,each:3,map:3,collect:3,reduce:4,foldl:4,inject:4,reduceRight:4,foldr:4,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};o(x,S,"models");var I=e.View=function(t){this.cid=i.uniqueId("view");i.extend(this,i.pick(t,P));this._ensureElement();this.initialize.apply(this,arguments)};var T=/^(\S+)\s*(.*)$/;var P=["model","collection","el","id","attributes","className","tagName","events"];i.extend(I.prototype,l,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this._removeElement();this.stopListening();return this},_removeElement:function(){this.$el.remove()},setElement:function(t){this.undelegateEvents();this._setElement(t);this.delegateEvents();return this},_setElement:function(t){this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0]},delegateEvents:function(t){t||(t=i.result(this,"events"));if(!t)return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[r];if(!r)continue;var n=e.match(T);this.delegate(n[1],n[2],i.bind(r,this))}return this},delegate:function(t,e,i){this.$el.on(t+".delegateEvents"+this.cid,e,i);return this},undelegateEvents:function(){if(this.$el)this.$el.off(".delegateEvents"+this.cid);return this},undelegate:function(t,e,i){this.$el.off(t+".delegateEvents"+this.cid,e,i);return this},_createElement:function(t){return document.createElement(t)},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");this.setElement(this._createElement(i.result(this,"tagName")));this._setAttributes(t)}else{this.setElement(i.result(this,"el"))}},_setAttributes:function(t){this.$el.attr(t)}});e.sync=function(t,r,n){var s=H[t];i.defaults(n||(n={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:s,dataType:"json"};if(!n.url){a.url=i.result(r,"url")||F()}if(n.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(n.attrs||r.toJSON(n))}if(n.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(n.emulateHTTP&&(s==="PUT"||s==="DELETE"||s==="PATCH")){a.type="POST";if(n.emulateJSON)a.data._method=s;var o=n.beforeSend;n.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",s);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!n.emulateJSON){a.processData=false}var h=n.error;n.error=function(t,e,i){n.textStatus=e;n.errorThrown=i;if(h)h.call(n.context,t,e,i)};var u=n.xhr=e.ajax(i.extend(a,n));r.trigger("request",r,u,n);return u};var H={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var A=/\((.*?)\)/g;var C=/(\(\?)?:\w+/g;var R=/\*\w+/g;var j=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,l,{initialize:function(){},route:function(t,r,n){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){n=r;r=""}if(!n)n=this[r];var s=this;e.history.route(t,function(i){var a=s._extractParameters(t,i);if(s.execute(n,a,r)!==false){s.trigger.apply(s,["route:"+r].concat(a));s.trigger("route",r,a);e.history.trigger("route",s,r,a)}});return this},execute:function(t,e,i){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(j,"\\$&").replace(A,"(?:$1)?").replace(C,function(t,e){return e?t:"([^/?]+)"}).replace(R,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var M=e.History=function(){this.handlers=[];this.checkUrl=i.bind(this.checkUrl,this);if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var N=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var U=/#.*$/;M.started=false;i.extend(M.prototype,l,{interval:50,atRoot:function(){var t=this.location.pathname.replace(/[^\/]$/,"$&/");return t===this.root&&!this.getSearch()},matchRoot:function(){var t=this.decodeFragment(this.location.pathname);var e=t.slice(0,this.root.length-1)+"/";return e===this.root},decodeFragment:function(t){return decodeURI(t.replace(/%25/g,"%2525"))},getSearch:function(){var t=this.location.href.replace(/#.*/,"").match(/\?.+/);return t?t[0]:""},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getPath:function(){var t=this.decodeFragment(this.location.pathname+this.getSearch()).slice(this.root.length-1);return t.charAt(0)==="/"?t.slice(1):t},getFragment:function(t){if(t==null){if(this._usePushState||!this._wantsHashChange){t=this.getPath()}else{t=this.getHash()}}return t.replace(N,"")},start:function(t){if(M.started)throw new Error("Backbone.history has already been started");M.started=true;this.options=i.extend({root:"/"},this.options,t);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();this.root=("/"+this.root+"/").replace(O,"/");if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){var e=this.root.slice(0,-1)||"/";this.location.replace(e+"#"+this.getPath());return true}else if(this._hasPushState&&this.atRoot()){this.navigate(this.getHash(),{replace:true})}}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 r=document.body;var n=r.insertBefore(this.iframe,r.firstChild).contentWindow;n.document.open();n.document.close();n.location.hash="#"+this.fragment}var s=window.addEventListener||function(t,e){return attachEvent("on"+t,e)};if(this._usePushState){s("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){s("hashchange",this.checkUrl,false)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}if(!this.options.silent)return this.loadUrl()},stop:function(){var t=window.removeEventListener||function(t,e){return detachEvent("on"+t,e)};if(this._usePushState){t("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){t("hashchange",this.checkUrl,false)}if(this.iframe){document.body.removeChild(this.iframe);this.iframe=null}if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);M.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getHash(this.iframe.contentWindow)}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){if(!this.matchRoot())return false;t=this.fragment=this.getFragment(t);return i.some(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!M.started)return false;if(!e||e===true)e={trigger:!!e};t=this.getFragment(t||"");var i=this.root;if(t===""||t.charAt(0)==="?"){i=i.slice(0,-1)||"/"}var r=i+t;t=this.decodeFragment(t.replace(U,""));if(this.fragment===t)return;this.fragment=t;if(this._usePushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,r)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getHash(this.iframe.contentWindow)){var n=this.iframe.contentWindow;if(!e.replace){n.document.open();n.document.close()}this._updateHash(n.location,t,e.replace)}}else{return this.location.assign(r)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new M;var q=function(t,e){var r=this;var n;if(t&&i.has(t,"constructor")){n=t.constructor}else{n=function(){return r.apply(this,arguments)}}i.extend(n,r,e);var s=function(){this.constructor=n};s.prototype=r.prototype;n.prototype=new s;if(t)i.extend(n.prototype,t);n.__super__=r.prototype;return n};y.extend=x.extend=$.extend=I.extend=M.extend=q;var F=function(){throw new Error('A "url" property or function must be specified')};var z=function(t,e){var i=e.error;e.error=function(r){if(i)i.call(e.context,t,r,e);t.trigger("error",t,r,e)}};return e});