at path:ROOT / wp-includes / js / backbone.js
run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
10.3 KB
2026-03-11 16:18:51
R W Run
3.41 KB
2026-03-11 16:18:51
R W Run
3.25 KB
2026-03-11 16:18:51
R W Run
1023 By
2026-03-11 16:18:51
R W Run
21.95 KB
2026-03-11 16:18:51
R W Run
5.67 KB
2026-03-11 16:18:51
R W Run
78.51 KB
2026-03-11 16:18:51
R W Run
23.73 KB
2026-03-11 16:18:51
R W Run
26.18 KB
2026-03-11 16:18:51
R W Run
8.8 KB
2026-03-11 16:18:51
R W Run
28.4 KB
2026-03-11 16:18:51
R W Run
16.11 KB
2026-03-11 16:18:51
R W Run
12.22 KB
2026-03-11 16:18:51
R W Run
2.96 KB
2026-03-11 16:18:51
R W Run
25.22 KB
2026-03-11 16:18:51
R W Run
7.67 KB
2026-03-11 16:18:51
R W Run
7.72 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:51
R W Run
6.66 KB
2026-03-11 16:18:51
R W Run
3.59 KB
2026-03-11 16:18:51
R W Run
14.67 KB
2026-03-11 16:18:51
R W Run
4.92 KB
2026-03-11 16:18:51
R W Run
22.71 KB
2026-03-11 16:18:51
R W Run
7.64 KB
2026-03-11 16:18:51
R W Run
27.93 KB
2026-03-11 16:18:51
R W Run
10.75 KB
2026-03-11 16:18:51
R W Run
32.55 KB
2026-03-11 16:18:51
R W Run
10.44 KB
2026-03-11 16:18:51
R W Run
5.1 KB
2026-03-11 16:18:51
R W Run
2.51 KB
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
5.81 KB
2026-03-11 16:18:51
R W Run
7.06 KB
2026-03-11 16:18:51
R W Run
1.46 KB
2026-03-11 16:18:51
R W Run
1.68 KB
2026-03-11 16:18:51
R W Run
5.39 KB
2026-03-11 16:18:51
R W Run
31 By
2026-03-11 16:18:51
R W Run
35 By
2026-03-11 16:18:51
R W Run
23.57 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:51
R W Run
9.54 KB
2026-03-11 16:18:51
R W Run
24.24 KB
2026-03-11 16:18:51
R W Run
11.77 KB
2026-03-11 16:18:51
R W Run
28.44 KB
2026-03-11 16:18:51
R W Run
10.63 KB
2026-03-11 16:18:51
R W Run
26.15 KB
2026-03-11 16:18:51
R W Run
12.98 KB
2026-03-11 16:18:51
R W Run
42.58 KB
2026-03-11 16:18:51
R W Run
12.97 KB
2026-03-11 16:18:51
R W Run
266.99 KB
2026-03-11 16:18:51
R W Run
108.18 KB
2026-03-11 16:18:51
R W Run
22.07 KB
2026-03-11 16:18:51
R W Run
10.87 KB
2026-03-11 16:18:51
R W Run
10.51 KB
2026-03-11 16:18:51
R W Run
2.58 KB
2026-03-11 16:18:51
R W Run
0 By
2026-03-11 16:18:51
R W Run
35 By
2026-03-11 16:18:51
R W Run
4.85 KB
2026-03-11 16:18:51
R W Run
3.21 KB
2026-03-11 16:18:51
R W Run
36.32 KB
2026-03-11 16:18:51
R W Run
19.39 KB
2026-03-11 16:18:51
R W Run
67.12 KB
2026-03-11 16:18:51
R W Run
18.46 KB
2026-03-11 16:18:51
R W Run
4.56 KB
2026-03-11 16:18:51
R W Run
1.82 KB
2026-03-11 16:18:51
R W Run
3.81 KB
2026-03-11 16:18:51
R W Run
2.51 KB
2026-03-11 16:18:51
R W Run
45.88 KB
2026-03-11 16:18:51
R W Run
14.34 KB
2026-03-11 16:18:51
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
1.62 KB
2026-03-11 16:18:51
R W Run
14.88 KB
2026-03-11 16:18:51
R W Run
2.97 KB
2026-03-11 16:18:51
R W Run
10.22 KB
2026-03-11 16:18:51
R W Run
4.34 KB
2026-03-11 16:18:51
R W Run
6.62 KB
2026-03-11 16:18:51
R W Run
3.1 KB
2026-03-11 16:18:51
R W Run
3.14 KB
2026-03-11 16:18:51
R W Run
1.22 KB
2026-03-11 16:18:51
R W Run
12.89 KB
2026-03-11 16:18:51
R W Run
2.82 KB
2026-03-11 16:18:51
R W Run
22.23 KB
2026-03-11 16:18:51
R W Run
8.59 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:51
R W Run
970 By
2026-03-11 16:18:51
R W Run
597 By
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
7.34 KB
2026-03-11 16:18:51
R W Run
9.99 KB
2026-03-11 16:18:51
R W Run
3.54 KB
2026-03-11 16:18:51
R W Run
1.3 KB
2026-03-11 16:18:51
R W Run
444 By
2026-03-11 16:18:51
R W Run
4.58 KB
2026-03-11 16:18:51
R W Run
1.4 KB
2026-03-11 16:18:51
R W Run
569 By
2026-03-11 16:18:51
R W Run
281 By
2026-03-11 16:18:51
R W Run
20.74 KB
2026-03-11 16:18:51
R W Run
11.05 KB
2026-03-11 16:18:51
R W Run
821 By
2026-03-11 16:18:51
R W Run
351 By
2026-03-11 16:18:51
R W Run
802.97 KB
2026-03-11 16:18:51
R W Run
error_log
📄backbone.js
1// Backbone.js 1.6.0
2
3// (c) 2010-2024 Jeremy Ashkenas and DocumentCloud
4// Backbone may be freely distributed under the MIT license.
5// For all details and documentation:
6// http://backbonejs.org
7
8(function(factory) {
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
15 // Set up Backbone appropriately for the environment. Start with AMD.
16 if (typeof define === 'function' && define.amd) {
17 define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
18 // Export global even in AMD case in case this script is loaded with
19 // others that may still expect a global Backbone.
20 root.Backbone = factory(root, exports, _, $);
21 });
22
23 // Next for Node.js or CommonJS. jQuery may not be needed as a module.
24 } else if (typeof exports !== 'undefined') {
25 var _ = require('underscore'), $;
26 try { $ = require('jquery'); } catch (e) {}
27 factory(root, exports, _, $);
28
29 // Finally, as a browser global.
30 } else {
31 root.Backbone = factory(root, {}, root._, root.jQuery || root.Zepto || root.ender || root.$);
32 }
33
34})(function(root, Backbone, _, $) {
35
36 // Initial Setup
37 // -------------
38
39 // Save the previous value of the `Backbone` variable, so that it can be
40 // restored later on, if `noConflict` is used.
41 var previousBackbone = root.Backbone;
42
43 // Create a local reference to a common array method we'll want to use later.
44 var slice = Array.prototype.slice;
45
46 // Current version of the library. Keep in sync with `package.json`.
47 Backbone.VERSION = '1.6.0';
48
49 // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
50 // the `$` variable.
51 Backbone.$ = $;
52
53 // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
54 // to its previous owner. Returns a reference to this Backbone object.
55 Backbone.noConflict = function() {
56 root.Backbone = previousBackbone;
57 return this;
58 };
59
60 // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
61 // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
62 // set a `X-Http-Method-Override` header.
63 Backbone.emulateHTTP = false;
64
65 // Turn on `emulateJSON` to support legacy servers that can't deal with direct
66 // `application/json` requests ... this will encode the body as
67 // `application/x-www-form-urlencoded` instead and will send the model in a
68 // form param named `model`.
69 Backbone.emulateJSON = false;
70
71 // Backbone.Events
72 // ---------------
73
74 // A module that can be mixed in to *any object* in order to provide it with
75 // a custom event channel. You may bind a callback to an event with `on` or
76 // remove with `off`; `trigger`-ing an event fires all callbacks in
77 // succession.
78 //
79 // var object = {};
80 // _.extend(object, Backbone.Events);
81 // object.on('expand', function(){ alert('expanded'); });
82 // object.trigger('expand');
83 //
84 var Events = Backbone.Events = {};
85
86 // Regular expression used to split event strings.
87 var eventSplitter = /\s+/;
88
89 // A private global variable to share between listeners and listenees.
90 var _listening;
91
92 // Iterates over the standard `event, callback` (as well as the fancy multiple
93 // space-separated events `"change blur", callback` and jQuery-style event
94 // maps `{event: callback}`).
95 var eventsApi = function(iteratee, events, name, callback, opts) {
96 var i = 0, names;
97 if (name && typeof name === 'object') {
98 // Handle event maps.
99 if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
100 for (names = _.keys(name); i < names.length ; i++) {
101 events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
102 }
103 } else if (name && eventSplitter.test(name)) {
104 // Handle space-separated event names by delegating them individually.
105 for (names = name.split(eventSplitter); i < names.length; i++) {
106 events = iteratee(events, names[i], callback, opts);
107 }
108 } else {
109 // Finally, standard events.
110 events = iteratee(events, name, callback, opts);
111 }
112 return events;
113 };
114
115 // Bind an event to a `callback` function. Passing `"all"` will bind
116 // the callback to all events fired.
117 Events.on = function(name, callback, context) {
118 this._events = eventsApi(onApi, this._events || {}, name, callback, {
119 context: context,
120 ctx: this,
121 listening: _listening
122 });
123
124 if (_listening) {
125 var listeners = this._listeners || (this._listeners = {});
126 listeners[_listening.id] = _listening;
127 // Allow the listening to use a counter, instead of tracking
128 // callbacks for library interop
129 _listening.interop = false;
130 }
131
132 return this;
133 };
134
135 // Inversion-of-control versions of `on`. Tell *this* object to listen to
136 // an event in another object... keeping track of what it's listening to
137 // for easier unbinding later.
138 Events.listenTo = function(obj, name, callback) {
139 if (!obj) return this;
140 var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
141 var listeningTo = this._listeningTo || (this._listeningTo = {});
142 var listening = _listening = listeningTo[id];
143
144 // This object is not listening to any other events on `obj` yet.
145 // Setup the necessary references to track the listening callbacks.
146 if (!listening) {
147 this._listenId || (this._listenId = _.uniqueId('l'));
148 listening = _listening = listeningTo[id] = new Listening(this, obj);
149 }
150
151 // Bind callbacks on obj.
152 var error = tryCatchOn(obj, name, callback, this);
153 _listening = void 0;
154
155 if (error) throw error;
156 // If the target obj is not Backbone.Events, track events manually.
157 if (listening.interop) listening.on(name, callback);
158
159 return this;
160 };
161
162 // The reducing API that adds a callback to the `events` object.
163 var onApi = function(events, name, callback, options) {
164 if (callback) {
165 var handlers = events[name] || (events[name] = []);
166 var context = options.context, ctx = options.ctx, listening = options.listening;
167 if (listening) listening.count++;
168
169 handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
170 }
171 return events;
172 };
173
174 // An try-catch guarded #on function, to prevent poisoning the global
175 // `_listening` variable.
176 var tryCatchOn = function(obj, name, callback, context) {
177 try {
178 obj.on(name, callback, context);
179 } catch (e) {
180 return e;
181 }
182 };
183
184 // Remove one or many callbacks. If `context` is null, removes all
185 // callbacks with that function. If `callback` is null, removes all
186 // callbacks for the event. If `name` is null, removes all bound
187 // callbacks for all events.
188 Events.off = function(name, callback, context) {
189 if (!this._events) return this;
190 this._events = eventsApi(offApi, this._events, name, callback, {
191 context: context,
192 listeners: this._listeners
193 });
194
195 return this;
196 };
197
198 // Tell this object to stop listening to either specific events ... or
199 // to every object it's currently listening to.
200 Events.stopListening = function(obj, name, callback) {
201 var listeningTo = this._listeningTo;
202 if (!listeningTo) return this;
203
204 var ids = obj ? [obj._listenId] : _.keys(listeningTo);
205 for (var i = 0; i < ids.length; i++) {
206 var listening = listeningTo[ids[i]];
207
208 // If listening doesn't exist, this object is not currently
209 // listening to obj. Break out early.
210 if (!listening) break;
211
212 listening.obj.off(name, callback, this);
213 if (listening.interop) listening.off(name, callback);
214 }
215 if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
216
217 return this;
218 };
219
220 // The reducing API that removes a callback from the `events` object.
221 var offApi = function(events, name, callback, options) {
222 if (!events) return;
223
224 var context = options.context, listeners = options.listeners;
225 var i = 0, names;
226
227 // Delete all event listeners and "drop" events.
228 if (!name && !context && !callback) {
229 for (names = _.keys(listeners); i < names.length; i++) {
230 listeners[names[i]].cleanup();
231 }
232 return;
233 }
234
235 names = name ? [name] : _.keys(events);
236 for (; i < names.length; i++) {
237 name = names[i];
238 var handlers = events[name];
239
240 // Bail out if there are no events stored.
241 if (!handlers) break;
242
243 // Find any remaining events.
244 var remaining = [];
245 for (var j = 0; j < handlers.length; j++) {
246 var handler = handlers[j];
247 if (
248 callback && callback !== handler.callback &&
249 callback !== handler.callback._callback ||
250 context && context !== handler.context
251 ) {
252 remaining.push(handler);
253 } else {
254 var listening = handler.listening;
255 if (listening) listening.off(name, callback);
256 }
257 }
258
259 // Replace events if there are any remaining. Otherwise, clean up.
260 if (remaining.length) {
261 events[name] = remaining;
262 } else {
263 delete events[name];
264 }
265 }
266
267 return events;
268 };
269
270 // Bind an event to only be triggered a single time. After the first time
271 // the callback is invoked, its listener will be removed. If multiple events
272 // are passed in using the space-separated syntax, the handler will fire
273 // once for each event, not once for a combination of all events.
274 Events.once = function(name, callback, context) {
275 // Map the event into a `{event: once}` object.
276 var events = eventsApi(onceMap, {}, name, callback, this.off.bind(this));
277 if (typeof name === 'string' && context == null) callback = void 0;
278 return this.on(events, callback, context);
279 };
280
281 // Inversion-of-control versions of `once`.
282 Events.listenToOnce = function(obj, name, callback) {
283 // Map the event into a `{event: once}` object.
284 var events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj));
285 return this.listenTo(obj, events);
286 };
287
288 // Reduces the event callbacks into a map of `{event: onceWrapper}`.
289 // `offer` unbinds the `onceWrapper` after it has been called.
290 var onceMap = function(map, name, callback, offer) {
291 if (callback) {
292 var once = map[name] = _.once(function() {
293 offer(name, once);
294 callback.apply(this, arguments);
295 });
296 once._callback = callback;
297 }
298 return map;
299 };
300
301 // Trigger one or many events, firing all bound callbacks. Callbacks are
302 // passed the same arguments as `trigger` is, apart from the event name
303 // (unless you're listening on `"all"`, which will cause your callback to
304 // receive the true name of the event as the first argument).
305 Events.trigger = function(name) {
306 if (!this._events) return this;
307
308 var length = Math.max(0, arguments.length - 1);
309 var args = Array(length);
310 for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
311
312 eventsApi(triggerApi, this._events, name, void 0, args);
313 return this;
314 };
315
316 // Handles triggering the appropriate event callbacks.
317 var triggerApi = function(objEvents, name, callback, args) {
318 if (objEvents) {
319 var events = objEvents[name];
320 var allEvents = objEvents.all;
321 if (events && allEvents) allEvents = allEvents.slice();
322 if (events) triggerEvents(events, args);
323 if (allEvents) triggerEvents(allEvents, [name].concat(args));
324 }
325 return objEvents;
326 };
327
328 // A difficult-to-believe, but optimized internal dispatch function for
329 // triggering events. Tries to keep the usual cases speedy (most internal
330 // Backbone events have 3 arguments).
331 var triggerEvents = function(events, args) {
332 var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
333 switch (args.length) {
334 case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
335 case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
336 case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
337 case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
338 default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
339 }
340 };
341
342 // A listening class that tracks and cleans up memory bindings
343 // when all callbacks have been offed.
344 var Listening = function(listener, obj) {
345 this.id = listener._listenId;
346 this.listener = listener;
347 this.obj = obj;
348 this.interop = true;
349 this.count = 0;
350 this._events = void 0;
351 };
352
353 Listening.prototype.on = Events.on;
354
355 // Offs a callback (or several).
356 // Uses an optimized counter if the listenee uses Backbone.Events.
357 // Otherwise, falls back to manual tracking to support events
358 // library interop.
359 Listening.prototype.off = function(name, callback) {
360 var cleanup;
361 if (this.interop) {
362 this._events = eventsApi(offApi, this._events, name, callback, {
363 context: void 0,
364 listeners: void 0
365 });
366 cleanup = !this._events;
367 } else {
368 this.count--;
369 cleanup = this.count === 0;
370 }
371 if (cleanup) this.cleanup();
372 };
373
374 // Cleans up memory bindings between the listener and the listenee.
375 Listening.prototype.cleanup = function() {
376 delete this.listener._listeningTo[this.obj._listenId];
377 if (!this.interop) delete this.obj._listeners[this.id];
378 };
379
380 // Aliases for backwards compatibility.
381 Events.bind = Events.on;
382 Events.unbind = Events.off;
383
384 // Allow the `Backbone` object to serve as a global event bus, for folks who
385 // want global "pubsub" in a convenient place.
386 _.extend(Backbone, Events);
387
388 // Backbone.Model
389 // --------------
390
391 // Backbone **Models** are the basic data object in the framework --
392 // frequently representing a row in a table in a database on your server.
393 // A discrete chunk of data and a bunch of useful, related methods for
394 // performing computations and transformations on that data.
395
396 // Create a new model with the specified attributes. A client id (`cid`)
397 // is automatically generated and assigned for you.
398 var Model = Backbone.Model = function(attributes, options) {
399 var attrs = attributes || {};
400 options || (options = {});
401 this.preinitialize.apply(this, arguments);
402 this.cid = _.uniqueId(this.cidPrefix);
403 this.attributes = {};
404 if (options.collection) this.collection = options.collection;
405 if (options.parse) attrs = this.parse(attrs, options) || {};
406 var defaults = _.result(this, 'defaults');
407
408 // Just _.defaults would work fine, but the additional _.extends
409 // is in there for historical reasons. See #3843.
410 attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
411
412 this.set(attrs, options);
413 this.changed = {};
414 this.initialize.apply(this, arguments);
415 };
416
417 // Attach all inheritable methods to the Model prototype.
418 _.extend(Model.prototype, Events, {
419
420 // A hash of attributes whose current and previous value differ.
421 changed: null,
422
423 // The value returned during the last failed validation.
424 validationError: null,
425
426 // The default name for the JSON `id` attribute is `"id"`. MongoDB and
427 // CouchDB users may want to set this to `"_id"`.
428 idAttribute: 'id',
429
430 // The prefix is used to create the client id which is used to identify models locally.
431 // You may want to override this if you're experiencing name clashes with model ids.
432 cidPrefix: 'c',
433
434 // preinitialize is an empty function by default. You can override it with a function
435 // or object. preinitialize will run before any instantiation logic is run in the Model.
436 preinitialize: function(){},
437
438 // Initialize is an empty function by default. Override it with your own
439 // initialization logic.
440 initialize: function(){},
441
442 // Return a copy of the model's `attributes` object.
443 toJSON: function(options) {
444 return _.clone(this.attributes);
445 },
446
447 // Proxy `Backbone.sync` by default -- but override this if you need
448 // custom syncing semantics for *this* particular model.
449 sync: function() {
450 return Backbone.sync.apply(this, arguments);
451 },
452
453 // Get the value of an attribute.
454 get: function(attr) {
455 return this.attributes[attr];
456 },
457
458 // Get the HTML-escaped value of an attribute.
459 escape: function(attr) {
460 return _.escape(this.get(attr));
461 },
462
463 // Returns `true` if the attribute contains a value that is not null
464 // or undefined.
465 has: function(attr) {
466 return this.get(attr) != null;
467 },
468
469 // Special-cased proxy to underscore's `_.matches` method.
470 matches: function(attrs) {
471 return !!_.iteratee(attrs, this)(this.attributes);
472 },
473
474 // Set a hash of model attributes on the object, firing `"change"`. This is
475 // the core primitive operation of a model, updating the data and notifying
476 // anyone who needs to know about the change in state. The heart of the beast.
477 set: function(key, val, options) {
478 if (key == null) return this;
479
480 // Handle both `"key", value` and `{key: value}` -style arguments.
481 var attrs;
482 if (typeof key === 'object') {
483 attrs = key;
484 options = val;
485 } else {
486 (attrs = {})[key] = val;
487 }
488
489 options || (options = {});
490
491 // Run validation.
492 if (!this._validate(attrs, options)) return false;
493
494 // Extract attributes and options.
495 var unset = options.unset;
496 var silent = options.silent;
497 var changes = [];
498 var changing = this._changing;
499 this._changing = true;
500
501 if (!changing) {
502 this._previousAttributes = _.clone(this.attributes);
503 this.changed = {};
504 }
505
506 var current = this.attributes;
507 var changed = this.changed;
508 var prev = this._previousAttributes;
509
510 // For each `set` attribute, update or delete the current value.
511 for (var attr in attrs) {
512 val = attrs[attr];
513 if (!_.isEqual(current[attr], val)) changes.push(attr);
514 if (!_.isEqual(prev[attr], val)) {
515 changed[attr] = val;
516 } else {
517 delete changed[attr];
518 }
519 unset ? delete current[attr] : current[attr] = val;
520 }
521
522 // Update the `id`.
523 if (this.idAttribute in attrs) {
524 var prevId = this.id;
525 this.id = this.get(this.idAttribute);
526 this.trigger('changeId', this, prevId, options);
527 }
528
529 // Trigger all relevant attribute changes.
530 if (!silent) {
531 if (changes.length) this._pending = options;
532 for (var i = 0; i < changes.length; i++) {
533 this.trigger('change:' + changes[i], this, current[changes[i]], options);
534 }
535 }
536
537 // You might be wondering why there's a `while` loop here. Changes can
538 // be recursively nested within `"change"` events.
539 if (changing) return this;
540 if (!silent) {
541 while (this._pending) {
542 options = this._pending;
543 this._pending = false;
544 this.trigger('change', this, options);
545 }
546 }
547 this._pending = false;
548 this._changing = false;
549 return this;
550 },
551
552 // Remove an attribute from the model, firing `"change"`. `unset` is a noop
553 // if the attribute doesn't exist.
554 unset: function(attr, options) {
555 return this.set(attr, void 0, _.extend({}, options, {unset: true}));
556 },
557
558 // Clear all attributes on the model, firing `"change"`.
559 clear: function(options) {
560 var attrs = {};
561 for (var key in this.attributes) attrs[key] = void 0;
562 return this.set(attrs, _.extend({}, options, {unset: true}));
563 },
564
565 // Determine if the model has changed since the last `"change"` event.
566 // If you specify an attribute name, determine if that attribute has changed.
567 hasChanged: function(attr) {
568 if (attr == null) return !_.isEmpty(this.changed);
569 return _.has(this.changed, attr);
570 },
571
572 // Return an object containing all the attributes that have changed, or
573 // false if there are no changed attributes. Useful for determining what
574 // parts of a view need to be updated and/or what attributes need to be
575 // persisted to the server. Unset attributes will be set to undefined.
576 // You can also pass an attributes object to diff against the model,
577 // determining if there *would be* a change.
578 changedAttributes: function(diff) {
579 if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
580 var old = this._changing ? this._previousAttributes : this.attributes;
581 var changed = {};
582 var hasChanged;
583 for (var attr in diff) {
584 var val = diff[attr];
585 if (_.isEqual(old[attr], val)) continue;
586 changed[attr] = val;
587 hasChanged = true;
588 }
589 return hasChanged ? changed : false;
590 },
591
592 // Get the previous value of an attribute, recorded at the time the last
593 // `"change"` event was fired.
594 previous: function(attr) {
595 if (attr == null || !this._previousAttributes) return null;
596 return this._previousAttributes[attr];
597 },
598
599 // Get all of the attributes of the model at the time of the previous
600 // `"change"` event.
601 previousAttributes: function() {
602 return _.clone(this._previousAttributes);
603 },
604
605 // Fetch the model from the server, merging the response with the model's
606 // local attributes. Any changed attributes will trigger a "change" event.
607 fetch: function(options) {
608 options = _.extend({parse: true}, options);
609 var model = this;
610 var success = options.success;
611 options.success = function(resp) {
612 var serverAttrs = options.parse ? model.parse(resp, options) : resp;
613 if (!model.set(serverAttrs, options)) return false;
614 if (success) success.call(options.context, model, resp, options);
615 model.trigger('sync', model, resp, options);
616 };
617 wrapError(this, options);
618 return this.sync('read', this, options);
619 },
620
621 // Set a hash of model attributes, and sync the model to the server.
622 // If the server returns an attributes hash that differs, the model's
623 // state will be `set` again.
624 save: function(key, val, options) {
625 // Handle both `"key", value` and `{key: value}` -style arguments.
626 var attrs;
627 if (key == null || typeof key === 'object') {
628 attrs = key;
629 options = val;
630 } else {
631 (attrs = {})[key] = val;
632 }
633
634 options = _.extend({validate: true, parse: true}, options);
635 var wait = options.wait;
636
637 // If we're not waiting and attributes exist, save acts as
638 // `set(attr).save(null, opts)` with validation. Otherwise, check if
639 // the model will be valid when the attributes, if any, are set.
640 if (attrs && !wait) {
641 if (!this.set(attrs, options)) return false;
642 } else if (!this._validate(attrs, options)) {
643 return false;
644 }
645
646 // After a successful server-side save, the client is (optionally)
647 // updated with the server-side state.
648 var model = this;
649 var success = options.success;
650 var attributes = this.attributes;
651 options.success = function(resp) {
652 // Ensure attributes are restored during synchronous saves.
653 model.attributes = attributes;
654 var serverAttrs = options.parse ? model.parse(resp, options) : resp;
655 if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
656 if (serverAttrs && !model.set(serverAttrs, options)) return false;
657 if (success) success.call(options.context, model, resp, options);
658 model.trigger('sync', model, resp, options);
659 };
660 wrapError(this, options);
661
662 // Set temporary attributes if `{wait: true}` to properly find new ids.
663 if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
664
665 var method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update';
666 if (method === 'patch' && !options.attrs) options.attrs = attrs;
667 var xhr = this.sync(method, this, options);
668
669 // Restore attributes.
670 this.attributes = attributes;
671
672 return xhr;
673 },
674
675 // Destroy this model on the server if it was already persisted.
676 // Optimistically removes the model from its collection, if it has one.
677 // If `wait: true` is passed, waits for the server to respond before removal.
678 destroy: function(options) {
679 options = options ? _.clone(options) : {};
680 var model = this;
681 var success = options.success;
682 var wait = options.wait;
683
684 var destroy = function() {
685 model.stopListening();
686 model.trigger('destroy', model, model.collection, options);
687 };
688
689 options.success = function(resp) {
690 if (wait) destroy();
691 if (success) success.call(options.context, model, resp, options);
692 if (!model.isNew()) model.trigger('sync', model, resp, options);
693 };
694
695 var xhr = false;
696 if (this.isNew()) {
697 _.defer(options.success);
698 } else {
699 wrapError(this, options);
700 xhr = this.sync('delete', this, options);
701 }
702 if (!wait) destroy();
703 return xhr;
704 },
705
706 // Default URL for the model's representation on the server -- if you're
707 // using Backbone's restful methods, override this to change the endpoint
708 // that will be called.
709 url: function() {
710 var base =
711 _.result(this, 'urlRoot') ||
712 _.result(this.collection, 'url') ||
713 urlError();
714 if (this.isNew()) return base;
715 var id = this.get(this.idAttribute);
716 return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
717 },
718
719 // **parse** converts a response into the hash of attributes to be `set` on
720 // the model. The default implementation is just to pass the response along.
721 parse: function(resp, options) {
722 return resp;
723 },
724
725 // Create a new model with identical attributes to this one.
726 clone: function() {
727 return new this.constructor(this.attributes);
728 },
729
730 // A model is new if it has never been saved to the server, and lacks an id.
731 isNew: function() {
732 return !this.has(this.idAttribute);
733 },
734
735 // Check if the model is currently in a valid state.
736 isValid: function(options) {
737 return this._validate({}, _.extend({}, options, {validate: true}));
738 },
739
740 // Run validation against the next complete set of model attributes,
741 // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
742 _validate: function(attrs, options) {
743 if (!options.validate || !this.validate) return true;
744 attrs = _.extend({}, this.attributes, attrs);
745 var error = this.validationError = this.validate(attrs, options) || null;
746 if (!error) return true;
747 this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
748 return false;
749 }
750
751 });
752
753 // Backbone.Collection
754 // -------------------
755
756 // If models tend to represent a single row of data, a Backbone Collection is
757 // more analogous to a table full of data ... or a small slice or page of that
758 // table, or a collection of rows that belong together for a particular reason
759 // -- all of the messages in this particular folder, all of the documents
760 // belonging to this particular author, and so on. Collections maintain
761 // indexes of their models, both in order, and for lookup by `id`.
762
763 // Create a new **Collection**, perhaps to contain a specific type of `model`.
764 // If a `comparator` is specified, the Collection will maintain
765 // its models in sort order, as they're added and removed.
766 var Collection = Backbone.Collection = function(models, options) {
767 options || (options = {});
768 this.preinitialize.apply(this, arguments);
769 if (options.model) this.model = options.model;
770 if (options.comparator !== void 0) this.comparator = options.comparator;
771 this._reset();
772 this.initialize.apply(this, arguments);
773 if (models) this.reset(models, _.extend({silent: true}, options));
774 };
775
776 // Default options for `Collection#set`.
777 var setOptions = {add: true, remove: true, merge: true};
778 var addOptions = {add: true, remove: false};
779
780 // Splices `insert` into `array` at index `at`.
781 var splice = function(array, insert, at) {
782 at = Math.min(Math.max(at, 0), array.length);
783 var tail = Array(array.length - at);
784 var length = insert.length;
785 var i;
786 for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
787 for (i = 0; i < length; i++) array[i + at] = insert[i];
788 for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
789 };
790
791 // Define the Collection's inheritable methods.
792 _.extend(Collection.prototype, Events, {
793
794 // The default model for a collection is just a **Backbone.Model**.
795 // This should be overridden in most cases.
796 model: Model,
797
798
799 // preinitialize is an empty function by default. You can override it with a function
800 // or object. preinitialize will run before any instantiation logic is run in the Collection.
801 preinitialize: function(){},
802
803 // Initialize is an empty function by default. Override it with your own
804 // initialization logic.
805 initialize: function(){},
806
807 // The JSON representation of a Collection is an array of the
808 // models' attributes.
809 toJSON: function(options) {
810 return this.map(function(model) { return model.toJSON(options); });
811 },
812
813 // Proxy `Backbone.sync` by default.
814 sync: function() {
815 return Backbone.sync.apply(this, arguments);
816 },
817
818 // Add a model, or list of models to the set. `models` may be Backbone
819 // Models or raw JavaScript objects to be converted to Models, or any
820 // combination of the two.
821 add: function(models, options) {
822 return this.set(models, _.extend({merge: false}, options, addOptions));
823 },
824
825 // Remove a model, or a list of models from the set.
826 remove: function(models, options) {
827 options = _.extend({}, options);
828 var singular = !_.isArray(models);
829 models = singular ? [models] : models.slice();
830 var removed = this._removeModels(models, options);
831 if (!options.silent && removed.length) {
832 options.changes = {added: [], merged: [], removed: removed};
833 this.trigger('update', this, options);
834 }
835 return singular ? removed[0] : removed;
836 },
837
838 // Update a collection by `set`-ing a new list of models, adding new ones,
839 // removing models that are no longer present, and merging models that
840 // already exist in the collection, as necessary. Similar to **Model#set**,
841 // the core operation for updating the data contained by the collection.
842 set: function(models, options) {
843 if (models == null) return;
844
845 options = _.extend({}, setOptions, options);
846 if (options.parse && !this._isModel(models)) {
847 models = this.parse(models, options) || [];
848 }
849
850 var singular = !_.isArray(models);
851 models = singular ? [models] : models.slice();
852
853 var at = options.at;
854 if (at != null) at = +at;
855 if (at > this.length) at = this.length;
856 if (at < 0) at += this.length + 1;
857
858 var set = [];
859 var toAdd = [];
860 var toMerge = [];
861 var toRemove = [];
862 var modelMap = {};
863
864 var add = options.add;
865 var merge = options.merge;
866 var remove = options.remove;
867
868 var sort = false;
869 var sortable = this.comparator && at == null && options.sort !== false;
870 var sortAttr = _.isString(this.comparator) ? this.comparator : null;
871
872 // Turn bare objects into model references, and prevent invalid models
873 // from being added.
874 var model, i;
875 for (i = 0; i < models.length; i++) {
876 model = models[i];
877
878 // If a duplicate is found, prevent it from being added and
879 // optionally merge it into the existing model.
880 var existing = this.get(model);
881 if (existing) {
882 if (merge && model !== existing) {
883 var attrs = this._isModel(model) ? model.attributes : model;
884 if (options.parse) attrs = existing.parse(attrs, options);
885 existing.set(attrs, options);
886 toMerge.push(existing);
887 if (sortable && !sort) sort = existing.hasChanged(sortAttr);
888 }
889 if (!modelMap[existing.cid]) {
890 modelMap[existing.cid] = true;
891 set.push(existing);
892 }
893 models[i] = existing;
894
895 // If this is a new, valid model, push it to the `toAdd` list.
896 } else if (add) {
897 model = models[i] = this._prepareModel(model, options);
898 if (model) {
899 toAdd.push(model);
900 this._addReference(model, options);
901 modelMap[model.cid] = true;
902 set.push(model);
903 }
904 }
905 }
906
907 // Remove stale models.
908 if (remove) {
909 for (i = 0; i < this.length; i++) {
910 model = this.models[i];
911 if (!modelMap[model.cid]) toRemove.push(model);
912 }
913 if (toRemove.length) this._removeModels(toRemove, options);
914 }
915
916 // See if sorting is needed, update `length` and splice in new models.
917 var orderChanged = false;
918 var replace = !sortable && add && remove;
919 if (set.length && replace) {
920 orderChanged = this.length !== set.length || _.some(this.models, function(m, index) {
921 return m !== set[index];
922 });
923 this.models.length = 0;
924 splice(this.models, set, 0);
925 this.length = this.models.length;
926 } else if (toAdd.length) {
927 if (sortable) sort = true;
928 splice(this.models, toAdd, at == null ? this.length : at);
929 this.length = this.models.length;
930 }
931
932 // Silently sort the collection if appropriate.
933 if (sort) this.sort({silent: true});
934
935 // Unless silenced, it's time to fire all appropriate add/sort/update events.
936 if (!options.silent) {
937 for (i = 0; i < toAdd.length; i++) {
938 if (at != null) options.index = at + i;
939 model = toAdd[i];
940 model.trigger('add', model, this, options);
941 }
942 if (sort || orderChanged) this.trigger('sort', this, options);
943 if (toAdd.length || toRemove.length || toMerge.length) {
944 options.changes = {
945 added: toAdd,
946 removed: toRemove,
947 merged: toMerge
948 };
949 this.trigger('update', this, options);
950 }
951 }
952
953 // Return the added (or merged) model (or models).
954 return singular ? models[0] : models;
955 },
956
957 // When you have more items than you want to add or remove individually,
958 // you can reset the entire set with a new list of models, without firing
959 // any granular `add` or `remove` events. Fires `reset` when finished.
960 // Useful for bulk operations and optimizations.
961 reset: function(models, options) {
962 options = options ? _.clone(options) : {};
963 for (var i = 0; i < this.models.length; i++) {
964 this._removeReference(this.models[i], options);
965 }
966 options.previousModels = this.models;
967 this._reset();
968 models = this.add(models, _.extend({silent: true}, options));
969 if (!options.silent) this.trigger('reset', this, options);
970 return models;
971 },
972
973 // Add a model to the end of the collection.
974 push: function(model, options) {
975 return this.add(model, _.extend({at: this.length}, options));
976 },
977
978 // Remove a model from the end of the collection.
979 pop: function(options) {
980 var model = this.at(this.length - 1);
981 return this.remove(model, options);
982 },
983
984 // Add a model to the beginning of the collection.
985 unshift: function(model, options) {
986 return this.add(model, _.extend({at: 0}, options));
987 },
988
989 // Remove a model from the beginning of the collection.
990 shift: function(options) {
991 var model = this.at(0);
992 return this.remove(model, options);
993 },
994
995 // Slice out a sub-array of models from the collection.
996 slice: function() {
997 return slice.apply(this.models, arguments);
998 },
999
1000 // Get a model from the set by id, cid, model object with id or cid
1001 // properties, or an attributes object that is transformed through modelId.
1002 get: function(obj) {
1003 if (obj == null) return void 0;
1004 return this._byId[obj] ||
1005 this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj, obj.idAttribute)] ||
1006 obj.cid && this._byId[obj.cid];
1007 },
1008
1009 // Returns `true` if the model is in the collection.
1010 has: function(obj) {
1011 return this.get(obj) != null;
1012 },
1013
1014 // Get the model at the given index.
1015 at: function(index) {
1016 if (index < 0) index += this.length;
1017 return this.models[index];
1018 },
1019
1020 // Return models with matching attributes. Useful for simple cases of
1021 // `filter`.
1022 where: function(attrs, first) {
1023 return this[first ? 'find' : 'filter'](attrs);
1024 },
1025
1026 // Return the first model with matching attributes. Useful for simple cases
1027 // of `find`.
1028 findWhere: function(attrs) {
1029 return this.where(attrs, true);
1030 },
1031
1032 // Force the collection to re-sort itself. You don't need to call this under
1033 // normal circumstances, as the set will maintain sort order as each item
1034 // is added.
1035 sort: function(options) {
1036 var comparator = this.comparator;
1037 if (!comparator) throw new Error('Cannot sort a set without a comparator');
1038 options || (options = {});
1039
1040 var length = comparator.length;
1041 if (_.isFunction(comparator)) comparator = comparator.bind(this);
1042
1043 // Run sort based on type of `comparator`.
1044 if (length === 1 || _.isString(comparator)) {
1045 this.models = this.sortBy(comparator);
1046 } else {
1047 this.models.sort(comparator);
1048 }
1049 if (!options.silent) this.trigger('sort', this, options);
1050 return this;
1051 },
1052
1053 // Pluck an attribute from each model in the collection.
1054 pluck: function(attr) {
1055 return this.map(attr + '');
1056 },
1057
1058 // Fetch the default set of models for this collection, resetting the
1059 // collection when they arrive. If `reset: true` is passed, the response
1060 // data will be passed through the `reset` method instead of `set`.
1061 fetch: function(options) {
1062 options = _.extend({parse: true}, options);
1063 var success = options.success;
1064 var collection = this;
1065 options.success = function(resp) {
1066 var method = options.reset ? 'reset' : 'set';
1067 collection[method](resp, options);
1068 if (success) success.call(options.context, collection, resp, options);
1069 collection.trigger('sync', collection, resp, options);
1070 };
1071 wrapError(this, options);
1072 return this.sync('read', this, options);
1073 },
1074
1075 // Create a new instance of a model in this collection. Add the model to the
1076 // collection immediately, unless `wait: true` is passed, in which case we
1077 // wait for the server to agree.
1078 create: function(model, options) {
1079 options = options ? _.clone(options) : {};
1080 var wait = options.wait;
1081 model = this._prepareModel(model, options);
1082 if (!model) return false;
1083 if (!wait) this.add(model, options);
1084 var collection = this;
1085 var success = options.success;
1086 options.success = function(m, resp, callbackOpts) {
1087 if (wait) {
1088 m.off('error', collection._forwardPristineError, collection);
1089 collection.add(m, callbackOpts);
1090 }
1091 if (success) success.call(callbackOpts.context, m, resp, callbackOpts);
1092 };
1093 // In case of wait:true, our collection is not listening to any
1094 // of the model's events yet, so it will not forward the error
1095 // event. In this special case, we need to listen for it
1096 // separately and handle the event just once.
1097 // (The reason we don't need to do this for the sync event is
1098 // in the success handler above: we add the model first, which
1099 // causes the collection to listen, and then invoke the callback
1100 // that triggers the event.)
1101 if (wait) {
1102 model.once('error', this._forwardPristineError, this);
1103 }
1104 model.save(null, options);
1105 return model;
1106 },
1107
1108 // **parse** converts a response into a list of models to be added to the
1109 // collection. The default implementation is just to pass it through.
1110 parse: function(resp, options) {
1111 return resp;
1112 },
1113
1114 // Create a new collection with an identical list of models as this one.
1115 clone: function() {
1116 return new this.constructor(this.models, {
1117 model: this.model,
1118 comparator: this.comparator
1119 });
1120 },
1121
1122 // Define how to uniquely identify models in the collection.
1123 modelId: function(attrs, idAttribute) {
1124 return attrs[idAttribute || this.model.prototype.idAttribute || 'id'];
1125 },
1126
1127 // Get an iterator of all models in this collection.
1128 values: function() {
1129 return new CollectionIterator(this, ITERATOR_VALUES);
1130 },
1131
1132 // Get an iterator of all model IDs in this collection.
1133 keys: function() {
1134 return new CollectionIterator(this, ITERATOR_KEYS);
1135 },
1136
1137 // Get an iterator of all [ID, model] tuples in this collection.
1138 entries: function() {
1139 return new CollectionIterator(this, ITERATOR_KEYSVALUES);
1140 },
1141
1142 // Private method to reset all internal state. Called when the collection
1143 // is first initialized or reset.
1144 _reset: function() {
1145 this.length = 0;
1146 this.models = [];
1147 this._byId = {};
1148 },
1149
1150 // Prepare a hash of attributes (or other model) to be added to this
1151 // collection.
1152 _prepareModel: function(attrs, options) {
1153 if (this._isModel(attrs)) {
1154 if (!attrs.collection) attrs.collection = this;
1155 return attrs;
1156 }
1157 options = options ? _.clone(options) : {};
1158 options.collection = this;
1159
1160 var model;
1161 if (this.model.prototype) {
1162 model = new this.model(attrs, options);
1163 } else {
1164 // ES class methods didn't have prototype
1165 model = this.model(attrs, options);
1166 }
1167
1168 if (!model.validationError) return model;
1169 this.trigger('invalid', this, model.validationError, options);
1170 return false;
1171 },
1172
1173 // Internal method called by both remove and set.
1174 _removeModels: function(models, options) {
1175 var removed = [];
1176 for (var i = 0; i < models.length; i++) {
1177 var model = this.get(models[i]);
1178 if (!model) continue;
1179
1180 var index = this.indexOf(model);
1181 this.models.splice(index, 1);
1182 this.length--;
1183
1184 // Remove references before triggering 'remove' event to prevent an
1185 // infinite loop. #3693
1186 delete this._byId[model.cid];
1187 var id = this.modelId(model.attributes, model.idAttribute);
1188 if (id != null) delete this._byId[id];
1189
1190 if (!options.silent) {
1191 options.index = index;
1192 model.trigger('remove', model, this, options);
1193 }
1194
1195 removed.push(model);
1196 this._removeReference(model, options);
1197 }
1198 if (models.length > 0 && !options.silent) delete options.index;
1199 return removed;
1200 },
1201
1202 // Method for checking whether an object should be considered a model for
1203 // the purposes of adding to the collection.
1204 _isModel: function(model) {
1205 return model instanceof Model;
1206 },
1207
1208 // Internal method to create a model's ties to a collection.
1209 _addReference: function(model, options) {
1210 this._byId[model.cid] = model;
1211 var id = this.modelId(model.attributes, model.idAttribute);
1212 if (id != null) this._byId[id] = model;
1213 model.on('all', this._onModelEvent, this);
1214 },
1215
1216 // Internal method to sever a model's ties to a collection.
1217 _removeReference: function(model, options) {
1218 delete this._byId[model.cid];
1219 var id = this.modelId(model.attributes, model.idAttribute);
1220 if (id != null) delete this._byId[id];
1221 if (this === model.collection) delete model.collection;
1222 model.off('all', this._onModelEvent, this);
1223 },
1224
1225 // Internal method called every time a model in the set fires an event.
1226 // Sets need to update their indexes when models change ids. All other
1227 // events simply proxy through. "add" and "remove" events that originate
1228 // in other collections are ignored.
1229 _onModelEvent: function(event, model, collection, options) {
1230 if (model) {
1231 if ((event === 'add' || event === 'remove') && collection !== this) return;
1232 if (event === 'destroy') this.remove(model, options);
1233 if (event === 'changeId') {
1234 var prevId = this.modelId(model.previousAttributes(), model.idAttribute);
1235 var id = this.modelId(model.attributes, model.idAttribute);
1236 if (prevId != null) delete this._byId[prevId];
1237 if (id != null) this._byId[id] = model;
1238 }
1239 }
1240 this.trigger.apply(this, arguments);
1241 },
1242
1243 // Internal callback method used in `create`. It serves as a
1244 // stand-in for the `_onModelEvent` method, which is not yet bound
1245 // during the `wait` period of the `create` call. We still want to
1246 // forward any `'error'` event at the end of the `wait` period,
1247 // hence a customized callback.
1248 _forwardPristineError: function(model, collection, options) {
1249 // Prevent double forward if the model was already in the
1250 // collection before the call to `create`.
1251 if (this.has(model)) return;
1252 this._onModelEvent('error', model, collection, options);
1253 }
1254 });
1255
1256 // Defining an @@iterator method implements JavaScript's Iterable protocol.
1257 // In modern ES2015 browsers, this value is found at Symbol.iterator.
1258 /* global Symbol */
1259 var $$iterator = typeof Symbol === 'function' && Symbol.iterator;
1260 if ($$iterator) {
1261 Collection.prototype[$$iterator] = Collection.prototype.values;
1262 }
1263
1264 // CollectionIterator
1265 // ------------------
1266
1267 // A CollectionIterator implements JavaScript's Iterator protocol, allowing the
1268 // use of `for of` loops in modern browsers and interoperation between
1269 // Backbone.Collection and other JavaScript functions and third-party libraries
1270 // which can operate on Iterables.
1271 var CollectionIterator = function(collection, kind) {
1272 this._collection = collection;
1273 this._kind = kind;
1274 this._index = 0;
1275 };
1276
1277 // This "enum" defines the three possible kinds of values which can be emitted
1278 // by a CollectionIterator that correspond to the values(), keys() and entries()
1279 // methods on Collection, respectively.
1280 var ITERATOR_VALUES = 1;
1281 var ITERATOR_KEYS = 2;
1282 var ITERATOR_KEYSVALUES = 3;
1283
1284 // All Iterators should themselves be Iterable.
1285 if ($$iterator) {
1286 CollectionIterator.prototype[$$iterator] = function() {
1287 return this;
1288 };
1289 }
1290
1291 CollectionIterator.prototype.next = function() {
1292 if (this._collection) {
1293
1294 // Only continue iterating if the iterated collection is long enough.
1295 if (this._index < this._collection.length) {
1296 var model = this._collection.at(this._index);
1297 this._index++;
1298
1299 // Construct a value depending on what kind of values should be iterated.
1300 var value;
1301 if (this._kind === ITERATOR_VALUES) {
1302 value = model;
1303 } else {
1304 var id = this._collection.modelId(model.attributes, model.idAttribute);
1305 if (this._kind === ITERATOR_KEYS) {
1306 value = id;
1307 } else { // ITERATOR_KEYSVALUES
1308 value = [id, model];
1309 }
1310 }
1311 return {value: value, done: false};
1312 }
1313
1314 // Once exhausted, remove the reference to the collection so future
1315 // calls to the next method always return done.
1316 this._collection = void 0;
1317 }
1318
1319 return {value: void 0, done: true};
1320 };
1321
1322 // Backbone.View
1323 // -------------
1324
1325 // Backbone Views are almost more convention than they are actual code. A View
1326 // is simply a JavaScript object that represents a logical chunk of UI in the
1327 // DOM. This might be a single item, an entire list, a sidebar or panel, or
1328 // even the surrounding frame which wraps your whole app. Defining a chunk of
1329 // UI as a **View** allows you to define your DOM events declaratively, without
1330 // having to worry about render order ... and makes it easy for the view to
1331 // react to specific changes in the state of your models.
1332
1333 // Creating a Backbone.View creates its initial element outside of the DOM,
1334 // if an existing element is not provided...
1335 var View = Backbone.View = function(options) {
1336 this.cid = _.uniqueId('view');
1337 this.preinitialize.apply(this, arguments);
1338 _.extend(this, _.pick(options, viewOptions));
1339 this._ensureElement();
1340 this.initialize.apply(this, arguments);
1341 };
1342
1343 // Cached regex to split keys for `delegate`.
1344 var delegateEventSplitter = /^(\S+)\s*(.*)$/;
1345
1346 // List of view options to be set as properties.
1347 var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
1348
1349 // Set up all inheritable **Backbone.View** properties and methods.
1350 _.extend(View.prototype, Events, {
1351
1352 // The default `tagName` of a View's element is `"div"`.
1353 tagName: 'div',
1354
1355 // jQuery delegate for element lookup, scoped to DOM elements within the
1356 // current view. This should be preferred to global lookups where possible.
1357 $: function(selector) {
1358 return this.$el.find(selector);
1359 },
1360
1361 // preinitialize is an empty function by default. You can override it with a function
1362 // or object. preinitialize will run before any instantiation logic is run in the View
1363 preinitialize: function(){},
1364
1365 // Initialize is an empty function by default. Override it with your own
1366 // initialization logic.
1367 initialize: function(){},
1368
1369 // **render** is the core function that your view should override, in order
1370 // to populate its element (`this.el`), with the appropriate HTML. The
1371 // convention is for **render** to always return `this`.
1372 render: function() {
1373 return this;
1374 },
1375
1376 // Remove this view by taking the element out of the DOM, and removing any
1377 // applicable Backbone.Events listeners.
1378 remove: function() {
1379 this._removeElement();
1380 this.stopListening();
1381 return this;
1382 },
1383
1384 // Remove this view's element from the document and all event listeners
1385 // attached to it. Exposed for subclasses using an alternative DOM
1386 // manipulation API.
1387 _removeElement: function() {
1388 this.$el.remove();
1389 },
1390
1391 // Change the view's element (`this.el` property) and re-delegate the
1392 // view's events on the new element.
1393 setElement: function(element) {
1394 this.undelegateEvents();
1395 this._setElement(element);
1396 this.delegateEvents();
1397 return this;
1398 },
1399
1400 // Creates the `this.el` and `this.$el` references for this view using the
1401 // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
1402 // context or an element. Subclasses can override this to utilize an
1403 // alternative DOM manipulation API and are only required to set the
1404 // `this.el` property.
1405 _setElement: function(el) {
1406 this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
1407 this.el = this.$el[0];
1408 },
1409
1410 // Set callbacks, where `this.events` is a hash of
1411 //
1412 // *{"event selector": "callback"}*
1413 //
1414 // {
1415 // 'mousedown .title': 'edit',
1416 // 'click .button': 'save',
1417 // 'click .open': function(e) { ... }
1418 // }
1419 //
1420 // pairs. Callbacks will be bound to the view, with `this` set properly.
1421 // Uses event delegation for efficiency.
1422 // Omitting the selector binds the event to `this.el`.
1423 delegateEvents: function(events) {
1424 events || (events = _.result(this, 'events'));
1425 if (!events) return this;
1426 this.undelegateEvents();
1427 for (var key in events) {
1428 var method = events[key];
1429 if (!_.isFunction(method)) method = this[method];
1430 if (!method) continue;
1431 var match = key.match(delegateEventSplitter);
1432 this.delegate(match[1], match[2], method.bind(this));
1433 }
1434 return this;
1435 },
1436
1437 // Add a single event listener to the view's element (or a child element
1438 // using `selector`). This only works for delegate-able events: not `focus`,
1439 // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
1440 delegate: function(eventName, selector, listener) {
1441 this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
1442 return this;
1443 },
1444
1445 // Clears all callbacks previously bound to the view by `delegateEvents`.
1446 // You usually don't need to use this, but may wish to if you have multiple
1447 // Backbone views attached to the same DOM element.
1448 undelegateEvents: function() {
1449 if (this.$el) this.$el.off('.delegateEvents' + this.cid);
1450 return this;
1451 },
1452
1453 // A finer-grained `undelegateEvents` for removing a single delegated event.
1454 // `selector` and `listener` are both optional.
1455 undelegate: function(eventName, selector, listener) {
1456 this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
1457 return this;
1458 },
1459
1460 // Produces a DOM element to be assigned to your view. Exposed for
1461 // subclasses using an alternative DOM manipulation API.
1462 _createElement: function(tagName) {
1463 return document.createElement(tagName);
1464 },
1465
1466 // Ensure that the View has a DOM element to render into.
1467 // If `this.el` is a string, pass it through `$()`, take the first
1468 // matching element, and re-assign it to `el`. Otherwise, create
1469 // an element from the `id`, `className` and `tagName` properties.
1470 _ensureElement: function() {
1471 if (!this.el) {
1472 var attrs = _.extend({}, _.result(this, 'attributes'));
1473 if (this.id) attrs.id = _.result(this, 'id');
1474 if (this.className) attrs['class'] = _.result(this, 'className');
1475 this.setElement(this._createElement(_.result(this, 'tagName')));
1476 this._setAttributes(attrs);
1477 } else {
1478 this.setElement(_.result(this, 'el'));
1479 }
1480 },
1481
1482 // Set attributes from a hash on this view's element. Exposed for
1483 // subclasses using an alternative DOM manipulation API.
1484 _setAttributes: function(attributes) {
1485 this.$el.attr(attributes);
1486 }
1487
1488 });
1489
1490 // Proxy Backbone class methods to Underscore functions, wrapping the model's
1491 // `attributes` object or collection's `models` array behind the scenes.
1492 //
1493 // collection.filter(function(model) { return model.get('age') > 10 });
1494 // collection.each(this.addView);
1495 //
1496 // `Function#apply` can be slow so we use the method's arg count, if we know it.
1497 var addMethod = function(base, length, method, attribute) {
1498 switch (length) {
1499 case 1: return function() {
1500 return base[method](this[attribute]);
1501 };
1502 case 2: return function(value) {
1503 return base[method](this[attribute], value);
1504 };
1505 case 3: return function(iteratee, context) {
1506 return base[method](this[attribute], cb(iteratee, this), context);
1507 };
1508 case 4: return function(iteratee, defaultVal, context) {
1509 return base[method](this[attribute], cb(iteratee, this), defaultVal, context);
1510 };
1511 default: return function() {
1512 var args = slice.call(arguments);
1513 args.unshift(this[attribute]);
1514 return base[method].apply(base, args);
1515 };
1516 }
1517 };
1518
1519 var addUnderscoreMethods = function(Class, base, methods, attribute) {
1520 _.each(methods, function(length, method) {
1521 if (base[method]) Class.prototype[method] = addMethod(base, length, method, attribute);
1522 });
1523 };
1524
1525 // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
1526 var cb = function(iteratee, instance) {
1527 if (_.isFunction(iteratee)) return iteratee;
1528 if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
1529 if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
1530 return iteratee;
1531 };
1532 var modelMatcher = function(attrs) {
1533 var matcher = _.matches(attrs);
1534 return function(model) {
1535 return matcher(model.attributes);
1536 };
1537 };
1538
1539 // Underscore methods that we want to implement on the Collection.
1540 // 90% of the core usefulness of Backbone Collections is actually implemented
1541 // right here:
1542 var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
1543 foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
1544 select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
1545 contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
1546 head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
1547 without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
1548 isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
1549 sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
1550
1551
1552 // Underscore methods that we want to implement on the Model, mapped to the
1553 // number of arguments they take.
1554 var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
1555 omit: 0, chain: 1, isEmpty: 1};
1556
1557 // Mix in each Underscore method as a proxy to `Collection#models`.
1558
1559 _.each([
1560 [Collection, collectionMethods, 'models'],
1561 [Model, modelMethods, 'attributes']
1562 ], function(config) {
1563 var Base = config[0],
1564 methods = config[1],
1565 attribute = config[2];
1566
1567 Base.mixin = function(obj) {
1568 var mappings = _.reduce(_.functions(obj), function(memo, name) {
1569 memo[name] = 0;
1570 return memo;
1571 }, {});
1572 addUnderscoreMethods(Base, obj, mappings, attribute);
1573 };
1574
1575 addUnderscoreMethods(Base, _, methods, attribute);
1576 });
1577
1578 // Backbone.sync
1579 // -------------
1580
1581 // Override this function to change the manner in which Backbone persists
1582 // models to the server. You will be passed the type of request, and the
1583 // model in question. By default, makes a RESTful Ajax request
1584 // to the model's `url()`. Some possible customizations could be:
1585 //
1586 // * Use `setTimeout` to batch rapid-fire updates into a single request.
1587 // * Send up the models as XML instead of JSON.
1588 // * Persist models via WebSockets instead of Ajax.
1589 //
1590 // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
1591 // as `POST`, with a `_method` parameter containing the true HTTP method,
1592 // as well as all requests with the body as `application/x-www-form-urlencoded`
1593 // instead of `application/json` with the model in a param named `model`.
1594 // Useful when interfacing with server-side languages like **PHP** that make
1595 // it difficult to read the body of `PUT` requests.
1596 Backbone.sync = function(method, model, options) {
1597 var type = methodMap[method];
1598
1599 // Default options, unless specified.
1600 _.defaults(options || (options = {}), {
1601 emulateHTTP: Backbone.emulateHTTP,
1602 emulateJSON: Backbone.emulateJSON
1603 });
1604
1605 // Default JSON-request options.
1606 var params = {type: type, dataType: 'json'};
1607
1608 // Ensure that we have a URL.
1609 if (!options.url) {
1610 params.url = _.result(model, 'url') || urlError();
1611 }
1612
1613 // Ensure that we have the appropriate request data.
1614 if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
1615 params.contentType = 'application/json';
1616 params.data = JSON.stringify(options.attrs || model.toJSON(options));
1617 }
1618
1619 // For older servers, emulate JSON by encoding the request into an HTML-form.
1620 if (options.emulateJSON) {
1621 params.contentType = 'application/x-www-form-urlencoded';
1622 params.data = params.data ? {model: params.data} : {};
1623 }
1624
1625 // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
1626 // And an `X-HTTP-Method-Override` header.
1627 if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
1628 params.type = 'POST';
1629 if (options.emulateJSON) params.data._method = type;
1630 var beforeSend = options.beforeSend;
1631 options.beforeSend = function(xhr) {
1632 xhr.setRequestHeader('X-HTTP-Method-Override', type);
1633 if (beforeSend) return beforeSend.apply(this, arguments);
1634 };
1635 }
1636
1637 // Don't process data on a non-GET request.
1638 if (params.type !== 'GET' && !options.emulateJSON) {
1639 params.processData = false;
1640 }
1641
1642 // Pass along `textStatus` and `errorThrown` from jQuery.
1643 var error = options.error;
1644 options.error = function(xhr, textStatus, errorThrown) {
1645 options.textStatus = textStatus;
1646 options.errorThrown = errorThrown;
1647 if (error) error.call(options.context, xhr, textStatus, errorThrown);
1648 };
1649
1650 // Make the request, allowing the user to override any Ajax options.
1651 var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
1652 model.trigger('request', model, xhr, options);
1653 return xhr;
1654 };
1655
1656 // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
1657 var methodMap = {
1658 'create': 'POST',
1659 'update': 'PUT',
1660 'patch': 'PATCH',
1661 'delete': 'DELETE',
1662 'read': 'GET'
1663 };
1664
1665 // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
1666 // Override this if you'd like to use a different library.
1667 Backbone.ajax = function() {
1668 return Backbone.$.ajax.apply(Backbone.$, arguments);
1669 };
1670
1671 // Backbone.Router
1672 // ---------------
1673
1674 // Routers map faux-URLs to actions, and fire events when routes are
1675 // matched. Creating a new one sets its `routes` hash, if not set statically.
1676 var Router = Backbone.Router = function(options) {
1677 options || (options = {});
1678 this.preinitialize.apply(this, arguments);
1679 if (options.routes) this.routes = options.routes;
1680 this._bindRoutes();
1681 this.initialize.apply(this, arguments);
1682 };
1683
1684 // Cached regular expressions for matching named param parts and splatted
1685 // parts of route strings.
1686 var optionalParam = /\((.*?)\)/g;
1687 var namedParam = /(\(\?)?:\w+/g;
1688 var splatParam = /\*\w+/g;
1689 var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
1690
1691 // Set up all inheritable **Backbone.Router** properties and methods.
1692 _.extend(Router.prototype, Events, {
1693
1694 // preinitialize is an empty function by default. You can override it with a function
1695 // or object. preinitialize will run before any instantiation logic is run in the Router.
1696 preinitialize: function(){},
1697
1698 // Initialize is an empty function by default. Override it with your own
1699 // initialization logic.
1700 initialize: function(){},
1701
1702 // Manually bind a single named route to a callback. For example:
1703 //
1704 // this.route('search/:query/p:num', 'search', function(query, num) {
1705 // ...
1706 // });
1707 //
1708 route: function(route, name, callback) {
1709 if (!_.isRegExp(route)) route = this._routeToRegExp(route);
1710 if (_.isFunction(name)) {
1711 callback = name;
1712 name = '';
1713 }
1714 if (!callback) callback = this[name];
1715 var router = this;
1716 Backbone.history.route(route, function(fragment) {
1717 var args = router._extractParameters(route, fragment);
1718 if (router.execute(callback, args, name) !== false) {
1719 router.trigger.apply(router, ['route:' + name].concat(args));
1720 router.trigger('route', name, args);
1721 Backbone.history.trigger('route', router, name, args);
1722 }
1723 });
1724 return this;
1725 },
1726
1727 // Execute a route handler with the provided parameters. This is an
1728 // excellent place to do pre-route setup or post-route cleanup.
1729 execute: function(callback, args, name) {
1730 if (callback) callback.apply(this, args);
1731 },
1732
1733 // Simple proxy to `Backbone.history` to save a fragment into the history.
1734 navigate: function(fragment, options) {
1735 Backbone.history.navigate(fragment, options);
1736 return this;
1737 },
1738
1739 // Bind all defined routes to `Backbone.history`. We have to reverse the
1740 // order of the routes here to support behavior where the most general
1741 // routes can be defined at the bottom of the route map.
1742 _bindRoutes: function() {
1743 if (!this.routes) return;
1744 this.routes = _.result(this, 'routes');
1745 var route, routes = _.keys(this.routes);
1746 while ((route = routes.pop()) != null) {
1747 this.route(route, this.routes[route]);
1748 }
1749 },
1750
1751 // Convert a route string into a regular expression, suitable for matching
1752 // against the current location hash.
1753 _routeToRegExp: function(route) {
1754 route = route.replace(escapeRegExp, '\\$&')
1755 .replace(optionalParam, '(?:$1)?')
1756 .replace(namedParam, function(match, optional) {
1757 return optional ? match : '([^/?]+)';
1758 })
1759 .replace(splatParam, '([^?]*?)');
1760 return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
1761 },
1762
1763 // Given a route, and a URL fragment that it matches, return the array of
1764 // extracted decoded parameters. Empty or unmatched parameters will be
1765 // treated as `null` to normalize cross-browser behavior.
1766 _extractParameters: function(route, fragment) {
1767 var params = route.exec(fragment).slice(1);
1768 return _.map(params, function(param, i) {
1769 // Don't decode the search params.
1770 if (i === params.length - 1) return param || null;
1771 return param ? decodeURIComponent(param) : null;
1772 });
1773 }
1774
1775 });
1776
1777 // Backbone.History
1778 // ----------------
1779
1780 // Handles cross-browser history management, based on either
1781 // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
1782 // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
1783 // and URL fragments. If the browser supports neither (old IE, natch),
1784 // falls back to polling.
1785 var History = Backbone.History = function() {
1786 this.handlers = [];
1787 this.checkUrl = this.checkUrl.bind(this);
1788
1789 // Ensure that `History` can be used outside of the browser.
1790 if (typeof window !== 'undefined') {
1791 this.location = window.location;
1792 this.history = window.history;
1793 }
1794 };
1795
1796 // Cached regex for stripping a leading hash/slash and trailing space.
1797 var routeStripper = /^[#\/]|\s+$/g;
1798
1799 // Cached regex for stripping leading and trailing slashes.
1800 var rootStripper = /^\/+|\/+$/g;
1801
1802 // Cached regex for stripping urls of hash.
1803 var pathStripper = /#.*$/;
1804
1805 // Has the history handling already been started?
1806 History.started = false;
1807
1808 // Set up all inheritable **Backbone.History** properties and methods.
1809 _.extend(History.prototype, Events, {
1810
1811 // The default interval to poll for hash changes, if necessary, is
1812 // twenty times a second.
1813 interval: 50,
1814
1815 // Are we at the app root?
1816 atRoot: function() {
1817 var path = this.location.pathname.replace(/[^\/]$/, '$&/');
1818 return path === this.root && !this.getSearch();
1819 },
1820
1821 // Does the pathname match the root?
1822 matchRoot: function() {
1823 var path = this.decodeFragment(this.location.pathname);
1824 var rootPath = path.slice(0, this.root.length - 1) + '/';
1825 return rootPath === this.root;
1826 },
1827
1828 // Unicode characters in `location.pathname` are percent encoded so they're
1829 // decoded for comparison. `%25` should not be decoded since it may be part
1830 // of an encoded parameter.
1831 decodeFragment: function(fragment) {
1832 return decodeURI(fragment.replace(/%25/g, '%2525'));
1833 },
1834
1835 // In IE6, the hash fragment and search params are incorrect if the
1836 // fragment contains `?`.
1837 getSearch: function() {
1838 var match = this.location.href.replace(/#.*/, '').match(/\?.+/);
1839 return match ? match[0] : '';
1840 },
1841
1842 // Gets the true hash value. Cannot use location.hash directly due to bug
1843 // in Firefox where location.hash will always be decoded.
1844 getHash: function(window) {
1845 var match = (window || this).location.href.match(/#(.*)$/);
1846 return match ? match[1] : '';
1847 },
1848
1849 // Get the pathname and search params, without the root.
1850 getPath: function() {
1851 var path = this.decodeFragment(
1852 this.location.pathname + this.getSearch()
1853 ).slice(this.root.length - 1);
1854 return path.charAt(0) === '/' ? path.slice(1) : path;
1855 },
1856
1857 // Get the cross-browser normalized URL fragment from the path or hash.
1858 getFragment: function(fragment) {
1859 if (fragment == null) {
1860 if (this._usePushState || !this._wantsHashChange) {
1861 fragment = this.getPath();
1862 } else {
1863 fragment = this.getHash();
1864 }
1865 }
1866 return fragment.replace(routeStripper, '');
1867 },
1868
1869 // Start the hash change handling, returning `true` if the current URL matches
1870 // an existing route, and `false` otherwise.
1871 start: function(options) {
1872 if (History.started) throw new Error('Backbone.history has already been started');
1873 History.started = true;
1874
1875 // Figure out the initial configuration. Do we need an iframe?
1876 // Is pushState desired ... is it available?
1877 this.options = _.extend({root: '/'}, this.options, options);
1878 this.root = this.options.root;
1879 this._trailingSlash = this.options.trailingSlash;
1880 this._wantsHashChange = this.options.hashChange !== false;
1881 this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
1882 this._useHashChange = this._wantsHashChange && this._hasHashChange;
1883 this._wantsPushState = !!this.options.pushState;
1884 this._hasPushState = !!(this.history && this.history.pushState);
1885 this._usePushState = this._wantsPushState && this._hasPushState;
1886 this.fragment = this.getFragment();
1887
1888 // Normalize root to always include a leading and trailing slash.
1889 this.root = ('/' + this.root + '/').replace(rootStripper, '/');
1890
1891 // Transition from hashChange to pushState or vice versa if both are
1892 // requested.
1893 if (this._wantsHashChange && this._wantsPushState) {
1894
1895 // If we've started off with a route from a `pushState`-enabled
1896 // browser, but we're currently in a browser that doesn't support it...
1897 if (!this._hasPushState && !this.atRoot()) {
1898 var rootPath = this.root.slice(0, -1) || '/';
1899 this.location.replace(rootPath + '#' + this.getPath());
1900 // Return immediately as browser will do redirect to new url
1901 return true;
1902
1903 // Or if we've started out with a hash-based route, but we're currently
1904 // in a browser where it could be `pushState`-based instead...
1905 } else if (this._hasPushState && this.atRoot()) {
1906 this.navigate(this.getHash(), {replace: true});
1907 }
1908
1909 }
1910
1911 // Proxy an iframe to handle location events if the browser doesn't
1912 // support the `hashchange` event, HTML5 history, or the user wants
1913 // `hashChange` but not `pushState`.
1914 if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
1915 this.iframe = document.createElement('iframe');
1916 this.iframe.src = 'javascript:0';
1917 this.iframe.style.display = 'none';
1918 this.iframe.tabIndex = -1;
1919 var body = document.body;
1920 // Using `appendChild` will throw on IE < 9 if the document is not ready.
1921 var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
1922 iWindow.document.open();
1923 iWindow.document.close();
1924 iWindow.location.hash = '#' + this.fragment;
1925 }
1926
1927 // Add a cross-platform `addEventListener` shim for older browsers.
1928 var addEventListener = window.addEventListener || function(eventName, listener) {
1929 return attachEvent('on' + eventName, listener);
1930 };
1931
1932 // Depending on whether we're using pushState or hashes, and whether
1933 // 'onhashchange' is supported, determine how we check the URL state.
1934 if (this._usePushState) {
1935 addEventListener('popstate', this.checkUrl, false);
1936 } else if (this._useHashChange && !this.iframe) {
1937 addEventListener('hashchange', this.checkUrl, false);
1938 } else if (this._wantsHashChange) {
1939 this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
1940 }
1941
1942 if (!this.options.silent) return this.loadUrl();
1943 },
1944
1945 // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
1946 // but possibly useful for unit testing Routers.
1947 stop: function() {
1948 // Add a cross-platform `removeEventListener` shim for older browsers.
1949 var removeEventListener = window.removeEventListener || function(eventName, listener) {
1950 return detachEvent('on' + eventName, listener);
1951 };
1952
1953 // Remove window listeners.
1954 if (this._usePushState) {
1955 removeEventListener('popstate', this.checkUrl, false);
1956 } else if (this._useHashChange && !this.iframe) {
1957 removeEventListener('hashchange', this.checkUrl, false);
1958 }
1959
1960 // Clean up the iframe if necessary.
1961 if (this.iframe) {
1962 document.body.removeChild(this.iframe);
1963 this.iframe = null;
1964 }
1965
1966 // Some environments will throw when clearing an undefined interval.
1967 if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
1968 History.started = false;
1969 },
1970
1971 // Add a route to be tested when the fragment changes. Routes added later
1972 // may override previous routes.
1973 route: function(route, callback) {
1974 this.handlers.unshift({route: route, callback: callback});
1975 },
1976
1977 // Checks the current URL to see if it has changed, and if it has,
1978 // calls `loadUrl`, normalizing across the hidden iframe.
1979 checkUrl: function(e) {
1980 var current = this.getFragment();
1981
1982 // If the user pressed the back button, the iframe's hash will have
1983 // changed and we should use that for comparison.
1984 if (current === this.fragment && this.iframe) {
1985 current = this.getHash(this.iframe.contentWindow);
1986 }
1987
1988 if (current === this.fragment) {
1989 if (!this.matchRoot()) return this.notfound();
1990 return false;
1991 }
1992 if (this.iframe) this.navigate(current);
1993 this.loadUrl();
1994 },
1995
1996 // Attempt to load the current URL fragment. If a route succeeds with a
1997 // match, returns `true`. If no defined routes matches the fragment,
1998 // returns `false`.
1999 loadUrl: function(fragment) {
2000 // If the root doesn't match, no routes can match either.
2001 if (!this.matchRoot()) return this.notfound();
2002 fragment = this.fragment = this.getFragment(fragment);
2003 return _.some(this.handlers, function(handler) {
2004 if (handler.route.test(fragment)) {
2005 handler.callback(fragment);
2006 return true;
2007 }
2008 }) || this.notfound();
2009 },
2010
2011 // When no route could be matched, this method is called internally to
2012 // trigger the `'notfound'` event. It returns `false` so that it can be used
2013 // in tail position.
2014 notfound: function() {
2015 this.trigger('notfound');
2016 return false;
2017 },
2018
2019 // Save a fragment into the hash history, or replace the URL state if the
2020 // 'replace' option is passed. You are responsible for properly URL-encoding
2021 // the fragment in advance.
2022 //
2023 // The options object can contain `trigger: true` if you wish to have the
2024 // route callback be fired (not usually desirable), or `replace: true`, if
2025 // you wish to modify the current URL without adding an entry to the history.
2026 navigate: function(fragment, options) {
2027 if (!History.started) return false;
2028 if (!options || options === true) options = {trigger: !!options};
2029
2030 // Normalize the fragment.
2031 fragment = this.getFragment(fragment || '');
2032
2033 // Strip trailing slash on the root unless _trailingSlash is true
2034 var rootPath = this.root;
2035 if (!this._trailingSlash && (fragment === '' || fragment.charAt(0) === '?')) {
2036 rootPath = rootPath.slice(0, -1) || '/';
2037 }
2038 var url = rootPath + fragment;
2039
2040 // Strip the fragment of the query and hash for matching.
2041 fragment = fragment.replace(pathStripper, '');
2042
2043 // Decode for matching.
2044 var decodedFragment = this.decodeFragment(fragment);
2045
2046 if (this.fragment === decodedFragment) return;
2047 this.fragment = decodedFragment;
2048
2049 // If pushState is available, we use it to set the fragment as a real URL.
2050 if (this._usePushState) {
2051 this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
2052
2053 // If hash changes haven't been explicitly disabled, update the hash
2054 // fragment to store history.
2055 } else if (this._wantsHashChange) {
2056 this._updateHash(this.location, fragment, options.replace);
2057 if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) {
2058 var iWindow = this.iframe.contentWindow;
2059
2060 // Opening and closing the iframe tricks IE7 and earlier to push a
2061 // history entry on hash-tag change. When replace is true, we don't
2062 // want this.
2063 if (!options.replace) {
2064 iWindow.document.open();
2065 iWindow.document.close();
2066 }
2067
2068 this._updateHash(iWindow.location, fragment, options.replace);
2069 }
2070
2071 // If you've told us that you explicitly don't want fallback hashchange-
2072 // based history, then `navigate` becomes a page refresh.
2073 } else {
2074 return this.location.assign(url);
2075 }
2076 if (options.trigger) return this.loadUrl(fragment);
2077 },
2078
2079 // Update the hash location, either replacing the current entry, or adding
2080 // a new one to the browser history.
2081 _updateHash: function(location, fragment, replace) {
2082 if (replace) {
2083 var href = location.href.replace(/(javascript:|#).*$/, '');
2084 location.replace(href + '#' + fragment);
2085 } else {
2086 // Some browsers require that `hash` contains a leading #.
2087 location.hash = '#' + fragment;
2088 }
2089 }
2090
2091 });
2092
2093 // Create the default Backbone.history.
2094 Backbone.history = new History;
2095
2096 // Helpers
2097 // -------
2098
2099 // Helper function to correctly set up the prototype chain for subclasses.
2100 // Similar to `goog.inherits`, but uses a hash of prototype properties and
2101 // class properties to be extended.
2102 var extend = function(protoProps, staticProps) {
2103 var parent = this;
2104 var child;
2105
2106 // The constructor function for the new subclass is either defined by you
2107 // (the "constructor" property in your `extend` definition), or defaulted
2108 // by us to simply call the parent constructor.
2109 if (protoProps && _.has(protoProps, 'constructor')) {
2110 child = protoProps.constructor;
2111 } else {
2112 child = function(){ return parent.apply(this, arguments); };
2113 }
2114
2115 // Add static properties to the constructor function, if supplied.
2116 _.extend(child, parent, staticProps);
2117
2118 // Set the prototype chain to inherit from `parent`, without calling
2119 // `parent`'s constructor function and add the prototype properties.
2120 child.prototype = _.create(parent.prototype, protoProps);
2121 child.prototype.constructor = child;
2122
2123 // Set a convenience property in case the parent's prototype is needed
2124 // later.
2125 child.__super__ = parent.prototype;
2126
2127 return child;
2128 };
2129
2130 // Set up inheritance for the model, collection, router, view and history.
2131 Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
2132
2133 // Throw an error when a URL is needed, and none is supplied.
2134 var urlError = function() {
2135 throw new Error('A "url" property or function must be specified');
2136 };
2137
2138 // Wrap an optional error callback with a fallback error event.
2139 var wrapError = function(model, options) {
2140 var error = options.error;
2141 options.error = function(resp) {
2142 if (error) error.call(options.context, model, resp, options);
2143 model.trigger('error', model, resp, options);
2144 };
2145 };
2146
2147 // Provide useful information when things go wrong. This method is not meant
2148 // to be used directly; it merely provides the necessary introspection for the
2149 // external `debugInfo` function.
2150 Backbone._debug = function() {
2151 return {root: root, _: _};
2152 };
2153
2154 return Backbone;
2155});
2156
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1