PageSourceSearch

https://www.kmu.edu.tw/plugins/system/easyslider/assets/js/lib/model.js

js kmu.edu.tw collected 2026-09-24 09:16:24 UTC 21,730 bytes, 715 lines download raw bytes

1var optimizeCb = function(func, context, argCount) {
2	if (context === void 0) return func;
3	switch (argCount == null ? 3 : argCount) {
4		case 1: return function(value) {
5			return func.call(context, value);
6		};
7		case 2: return function(value, other) {
8			return func.call(context, value, other);
9		};
10		case 3: return function(value, index, collection) {
11			return func.call(context, value, index, collection);
12		};
13		case 4: return function(accumulator, value, index, collection) {
14			return func.call(context, accumulator, value, index, collection);
15		};
16	}
17	return function() {
18		return func.apply(context, arguments);
19	};
20};
21var cb = function(value, context, argCount) {
22	if (value == null) return _.identity;
23	if (_.isFunction(value)) return optimizeCb(value, context, argCount);
24	if (_.isObject(value)) return _.matcher(value);
25	return _.property(value);
26};
27
28_.mixin({
29	mapObject: function(obj, iteratee, context) {
30		iteratee = cb(iteratee, context);
31		var keys = _.keys(obj),
32			length = keys.length,
33			results = {},
34			currentKey;
35		for (var index = 0; index < length; index++) {
36			currentKey = keys[index];
37			results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
38		}
39		return results;
40	}
41})
42
43_.noop = function(){}
44void function ( root, factory ) {
45
46	// Set up Backbone appropriately for the environment. Start with AMD.
47	if ( typeof define === 'function' && define.amd ) {
48		define([ 'underscore', 'backbone', 'exports' ], function ( _, Backbone, exports ) {
49			// Export global even in AMD case in case this script is loaded with
50			// others that may still expect a global Backbone.
51			root.B = factory(root, exports, _, Backbone);
52		});
53
54		// Next for Node.js or CommonJS.
55	} else if ( typeof exports !== 'undefined' ) {
56		var _ = require('underscore');
57		var Backbone = require('backbone');
58		factory(root, exports, _, Backbone);
59
60		// Finally, as a browser global.
61	} else {
62		root.B = factory(root, root.B || {}, root._, root.Backbone);
63	}
64
65}(this, function ( root, B, _, Backbone ) {
66
67	var Model, Collection, Compute;
68	var BackboneModel = Backbone.Model;
69	var BackboneCollection = Backbone.Collection;
70
71	/* --- Compute --- */
72	Compute = B.Compute = function () {
73
74		function Compute ( deps, options ) {
75			if ( !(this instanceof Compute) )
76				return new Compute(deps, options);
77			if ( _.isArray(deps) && _.isFunction(options) )
78				options = { deps: deps, get: options }
79			else if ( _.isFunction(deps) )
80				options = _.defaults({ get: deps }, options)
81			else
82				options = (deps || {});
83
84			_.defaults(this, options, {
85				deps: [],
86				init: function () {
87					return null;
88				},
89				get: function ( value ) {
90					return value;
91				},
92				set: function ( value ) {
93					return value;
94				}
95			})
96		}
97
98		return Compute
99	}()
100
101	/* --- Model --- */
102	Model = B.Model = function () {
103
104		function Model ( attrs, options ) {
105			if ( !(this instanceof Model) ) {
106				if (isPrototypeOf(attrs, Model))
107					return attrs.create.apply(attrs, _(arguments).slice(1));
108				return Model.create.apply(Model, arguments);
109			}
110			_.extend(this, _.pick(options, '_parent', '_relatedKey'))
111			_.each(this.computes, this._registerComputeValue, this)
112			Object.defineProperties(this, {
113				'root': {
114					get: function () {
115						var root = this;
116						var parent = this.collection || this._parent;
117						while ( parent ) {
118							root = parent;
119							parent = parent.collection || parent._parent;
120						}
121						return root;
122					}
123				}
124			});
125			BackboneModel.apply(this, arguments);
126		}
127
128		Model.prototype.__proto__ = BackboneModel.prototype;
129
130		var useMatchAll = typeof String.prototype.matchAll == 'function'
131
132		// prototypes
133		_.extend(Model.prototype, {
134
135			relations: {},
136			computes: {},
137			defaults: {},
138
139			/*
140			* Use String.matchAll() on newer browsers
141			* otherwise use Regex.exec() for older browsers
142			* */
143			get: useMatchAll ? 
144				function( key ) {
145					var value = this;
146					var regex = /(\w+)(?:\[([0-9]+)\])?/g;
147					var m = key.matchAll(regex)
148					var done = false
149					while (!done) {
150						var result = m.next()
151						if (result.done == false) {
152							var match = result.value
153							value = value instanceof BackboneModel ? getComputedValue(value, match[ 1 ]) :
154								typeof value == 'object' ? value[ match[ 1 ] ] : undefined;
155							if ( match[ 2 ] )
156								value = value instanceof BackboneCollection ? value.at(match[ 2 ]) : value[ match[ 2 ] ];
157						} else {
158							done = true
159						}
160					}
161					return value;
162				} : 
163				function ( key ) {
164					var value = this;
165					var regex = /(\w+)(?:\[([0-9]+)\])?/g;
166					var match;
167					while (match = regex.exec(key)) {
168						value = value instanceof BackboneModel ? getComputedValue(value, match[ 1 ]) :
169							typeof value == 'object' ? value[ match[ 1 ] ] : undefined;
170						if ( match[ 2 ] ) {
171							value = value instanceof BackboneCollection ? value.at(match[ 2 ]) : value[ match[ 2 ] ];
172						}
173					}
174					return value;
175				},
176
177			//
178			// Borrowed from "Backbone Nested Models" by "Bret Little"
179			//
180			setRelation: function ( attr, val, options ) {
181				var relation = this.attributes[ attr ],
182					id = this.idAttribute || "id",
183					modelToSet, modelsToAdd = [], modelsToRemove = [];
184
185				if ( options.unset && relation ) delete relation.parent;
186
187				if ( this.relations && _.has(this.relations, attr) ) {
188
189					// If the relation already exists, we don't want to replace it, rather
190					// update the data within it whether it is a collection or model
191					if ( relation && relation instanceof Collection ) {
192
193						// If the val that is being set is already a collection, use the models
194						// within the collection.
195						if ( val instanceof Collection || val instanceof Array ) {
196							val = val.models || val;
197							modelsToAdd = _.clone(val);
198
199							relation.each(function ( model, i ) {
200
201								// If the model does not have an "id" skip logic to detect if it already
202								// exists and simply add it to the collection
203								if ( typeof model[ id ] == 'undefined' ) return;
204
205								// If the incoming model also exists within the existing collection,
206								// call set on that model. If it doesn't exist in the incoming array,
207								// then add it to a list that will be removed.
208								var rModel = _.find(val, function ( _model ) {
209									return _model[ id ] === model[ id ];
210								});
211
212								if ( rModel ) {
213									model.set(rModel.toJSON ? rModel.toJSON() : rModel);
214
215									// Remove the model from the incoming list because all remaining models
216									// will be added to the relation
217									modelsToAdd.splice(i, 1);
218								} else {
219									modelsToRemove.push(model);
220								}
221
222							});
223
224							_.each(modelsToRemove, function ( model ) {
225								relation.remove(model);
226							});
227
228							relation.add(modelsToAdd);
229
230						} else {
231
232							// The incoming val that is being set is not an array or collection, then it represents
233							// a single model.  Go through each of the models in the existing relation and remove
234							// all models that aren't the same as this one (by id). If it is the same, call set on that
235							// model.
236
237							relation.each(function ( model ) {
238								if ( val[ id ] === model[ id ] ) {
239									model.set(val);
240								} else {
241									relation.remove(model);
242								}
243							});
244						}
245
246						return relation;
247					}
248
249					if ( val instanceof Model ) {
250						val = val.toJSON()
251					}
252
253					if ( relation && relation instanceof Model ) {
254						relation.set(val);
255						return relation;
256					}
257
258					options._parent = this;
259					options._relatedKey = attr;
260
261					val = new this.relations[ attr ](val, options);
262					val.parent = this;
263				}
264
265				return val;
266			},
267			set: function ( key, val, options ) {
268				if ( typeof key == 'object' ) {
269					options = val;
270					return this._set(key, options)
271				}
272				if ( typeof key == 'string' ) {
273					if ( !key.match(/[.\[]/) )
274						return this._set(key, val, options)
275					var regex = /(\w+)(?:\[([0-9]+)\])?/;
276					var keys = key.split('.');
277					var setAttr = keys.pop().match(regex);
278					var getAttr = keys.join('.');
279					if ( !setAttr[ 2 ] ) {
280						var setter = this.get(getAttr);
281						if ( setter instanceof BackboneModel )
282							setter.set(setAttr[ 1 ], val, options);
283						else if ( typeof setter === 'object' )
284							setter[ setAttr[ 1 ] ] = val;
285						return this;
286					}
287					var collection = this.get(getAttr + '.' + setAttr[ 1 ]);
288					if ( collection instanceof BackboneCollection )
289						collection.at(parseInt(setAttr[ 2 ])).set(val, options)
290					else if ( typeof setter === 'object' )
291						collection[ parseInt(setAttr[ 2 ]) ] = val;
292					return this;
293				}
294			},
295			_set: function ( key, val, options ) {
296				var attr, attrs, unset, changes, silent, changing, prev, current;
297				if ( key == null ) return this;
298
299				// Handle both `"key", value` and `{key: value}` -style arguments.
300				if ( typeof key === 'object' ) {
301					attrs = key;
302					options = val;
303				} else {
304					(attrs = {})[ key ] = val;
305				}
306
307				options || (options = {});
308
309				// Run validation.
310				if ( !this._validate(attrs, options) ) return false;
311
312				// Extract attributes and options.
313				unset = options.unset;
314				silent = options.silent;
315				changes = [];
316				changing = this._changing;
317				this._changing = true;
318
319				if ( !changing ) {
320					this._previousAttributes = _.clone(this.attributes);
321					this.changed = {};
322				}
323				current = this.attributes, prev = this._previousAttributes;
324
325				// Check for changes of `id`.
326				if ( this.idAttribute in attrs ) this.id = attrs[ this.idAttribute ];
327
328				// For each `set` attribute, update or delete the current value.
329				for ( attr in attrs ) {
330					if ( this.computes[ attr ] ) {
331						val = attrs[ attr ]
332						val = this.computes[ attr ].set.call(this, val, options);
333					}
334					else {
335						val = attrs[ attr ];
336						// Inject in the relational lookup
337						val = this.setRelation(attr, val, options);
338					}
339
340					if ( !_.isEqual(current[ attr ], val) ) changes.push(attr);
341					if ( !_.isEqual(prev[ attr ], val) ) {
342						this.changed[ attr ] = val;
343					} else {
344						delete this.changed[ attr ];
345					}
346					unset ? delete current[ attr ] : current[ attr ] = val;
347				}
348
349				// Trigger all relevant attribute changes.
350				if ( !silent ) {
351					if ( changes.length ) this._pending = true;
352					for ( var i = 0, l = changes.length; i < l; i++ ) {
353						this.trigger('change:' + changes[ i ], this, current[ changes[ i ] ], options);
354					}
355				}
356
357				if ( changing ) return this;
358				if ( !silent ) {
359					while ( this._pending ) {
360						this._pending = false;
361						this.trigger('change', this, options);
362						this._triggerParentChange(options);
363					}
364				}
365				this._pending = false;
366				this._changing = false;
367				return this;
368			},
369			clone: function ( options ) {
370				return new this.constructor(this.toJSON());
371			},
372			clear: function ( options ) {
373				var attrs = {};
374				for ( var key in this.attributes ) {
375					if ( this.attributes[ key ] instanceof BackboneModel )
376						this.attributes[ key ].clear(options);
377					else if ( this.attributes[ key ] instanceof BackboneCollection )
378						this.attributes[ key ].invoke('clear', options),
379							this.attributes[ key ].reset([]);
380					else
381						attrs[ key ] = void 0;
382				}
383				return this.set(attrs, _.extend({}, options, { unset: true }));
384			},
385			toJSON2: function ( options ) {
386				var attrs = _.clone(this.attributes);
387				attrs.__proto__ = null;
388
389				_.each(this.relations, function ( rel, key ) {
390					if ( _.has(attrs, key) ) {
391						attrs[ key ] = attrs[ key ].toJSON();
392					} else {
393						attrs[ key ] = (new rel()).toJSON();
394					}
395				});
396
397				return attrs;
398			},
399			toJSON: function () {
400				var attr, obj = Object.create(null, {});
401				for ( var key in this.attributes ) {
402					attr = this.attributes[ key ];
403					if ( attr instanceof BackboneModel || attr instanceof BackboneCollection )
404						attr = attr.toJSON();
405					if (this.computes.hasOwnProperty(key))
406						continue;
407					if ( _.isObject(attr) )
408						attr = _.clone(attr);
409					obj[ key ] = attr;
410				}
411				return obj;
412			},
413			toCompactJSON: function () {
414				var attr, obj = Object.create(null, {});
415				for ( var key in this.attributes ) {
416					if ( this.attributes.hasOwnProperty(key) ) {
417						attr = this.attributes[ key ];
418						if ( attr instanceof Model || attr instanceof Collection )
419							attr = attr.toCompactJSON();
420						else if ( attr instanceof BackboneModel || attr instanceof BackboneCollection )
421							attr = attr.toJSON();
422
423						//if (attr instanceof Compute)
424						if (this.computes.hasOwnProperty(key))
425							continue;
426						if ( _.isEqual(attr, this.defaults[ key ]))
427							continue;
428
429						obj[ key ] = attr;
430					}
431				}
432				return obj;
433			},
434			_triggerParentChange: function ( options ) {
435				var parent = this._parent;
436				if ( !parent ) return;
437
438				parent.changed = {};
439				_.extend(options, { chained: true })
440
441				// Loop through every changed attribute
442				for ( var key in this.changed ) {
443
444					// Trigger "change:this.attr"
445					parent.changed[ this._relatedKey + '.' + key ] = this.changed[ key ];
446					parent.trigger('change:' + this._relatedKey + '.' + key, parent, this.changed[ key ], options);
447				}
448				//parent.changed[ this._relatedKey ] = this;
449				parent.changed[ this._relatedKey ] = undefined;
450
451				parent.trigger('change:' + this._relatedKey, parent, this, options);
452				parent.trigger('change', parent, options);
453				parent._triggerParentChange(options);
454			},
455			_registerComputeValue: function ( compute, attr ) {
456				_.each(compute.deps, function ( depAttr ) {
457					this.on('change:' + depAttr, function ( model, value, options ) {
458						var value = model.get(depAttr);
459						if ( value instanceof BackboneModel || value instanceof Collection ) {
460							model.changed[ attr ] = undefined;
461							_.each(value.changed, function ( subValue, subAttr ) {
462								model.changed[ subAttr ] = subValue;
463								model.trigger('change:' + attr + '.' + subAttr, model, subValue, options)
464							})
465						}
466						else {
467							model.changed[ attr ] = value;
468						}
469						model.trigger('change:' + attr, model, value, options)
470						model.trigger('change', model, options)
471					})
472				}, this)
473			},
474			alias: function ( model ) {
475				for ( var attr in model.attributes ) {
476					if ( model.attributes[ attr ] instanceof BackboneModel ) {
477						if ( !this.attributes.hasOwnProperty(attr) || !(this.attributes[ attr ] instanceof BackboneModel) || !(this.attributes[ attr ] instanceof BackboneCollection) ) {
478							this.attributes[ attr ] = new Model(this.attributes[ attr ], { _parent: this, _relatedKey: attr });
479						}
480						this.attributes[ attr ].alias(model.attributes[ attr ])
481					}
482				}
483				this._alias = model;
484				this.attributes.__proto__ = model.attributes;
485				this.defaults = _.extend({}, model.defaults, this.defaults);
486				this.relations = _.extend({}, model.relations, this.relations);
487				this.computes = _.extend({}, model.computes, this.computes);
488				//this.on('change', function () {
489				//	_.each(this.changed, function ( val, attr ) {
490				//		console.log('check target attr',model.has(attr))
491				//		model.has(attr) || model.set(attr, val)
492				//	})
493				//})
494				return this;
495			}
496		})
497		// statics
498		_.extend(Model, {
499			create: function ( attrs, protos, statics ) {
500
501				var protos = protos || {};
502				var statics = statics || {};
503				var defaults = _.extend( {}, this.prototype.defaults, protos.defaults, attrs );
504				var relations = _.extend( {}, this.prototype.relations, protos.relations );
505				var computes = _.extend( {}, this.prototype.computes, protos.computes );
506				for ( var attr in attrs ) {
507					if ( isPrototypeOf(attrs[ attr ], BackboneModel) ) {
508						relations[ attr ] = attrs[ attr ];
509						defaults[ attr ] = {};
510					}
511					else if ( isPrototypeOf(attrs[ attr ], BackboneCollection) ) {
512						relations[ attr ] = attrs[ attr ];
513						defaults[ attr ] = [];
514					}
515					else if ( attrs[ attr ] instanceof Compute ) {
516						computes[ attr ] = attrs[ attr ];
517						delete defaults[ attr ];
518					}
519				}
520				return this.extend(
521					_.extend({}, protos, {
522						defaults: defaults,
523						relations: relations,
524						computes: computes
525					}),
526					_.extend({}, statics, {
527						create: Model.create,
528						extend: Model.extend,
529						define: Model.define,
530					})
531				);
532			},
533			extend: function () {
534				return BackboneModel.extend.apply(this, arguments)
535			}
536		})
537
538		Model.define = Model.create;
539
540		return Model
541	}();
542
543	/* --- Collection --- */
544	Collection = B.Collection = function () {
545
546		function Collection ( models, options ) {
547			if ( !(this instanceof Collection) ) {
548				if (isPrototypeOf(models, Collection))
549					return models.create.apply(models, _(arguments).slice(1));
550				return Collection.create.apply(Collection, arguments);
551			}
552			_.extend(this, _.pick(options, '_parent', '_relatedKey'))
553			this.on('change', this._triggerParentChange)
554			this.on('add', this._triggerParentChange)
555			this.on('remove', this._triggerParentChange)
556			BackboneCollection.apply(this, arguments)
557		}
558
559		Collection.prototype.__proto__ = BackboneCollection.prototype;
560
561		// prototypes
562		_.extend(Collection.prototype, {
563
564			model: Model,
565
566			_triggerParentChange: function ( model, options ) {
567				var parent = this._parent;
568				if ( !parent ) return;
569
570				// If this change event is triggered by one of its child model
571				if ( model && model.collection ) {
572
573					var modelIndex = model.collection.indexOf(model);
574
575					parent.changed = {};
576					_.extend(options, { chained: true })
577
578					// Loop through every changed attributes of this model
579					for ( var key in model.changed ) {
580
581						// Trigger "change:collection[n].child"
582						parent.changed[ this._relatedKey + '[' + modelIndex + '].' + key ] = model.changed[ key ];
583						parent.trigger('change:' + this._relatedKey + '[' + modelIndex + '].' + key, parent, model.changed[ key ], options);
584
585						// Trigger "change:collection.child"
586						parent.changed[ this._relatedKey + '.' + key ] = model.changed[ key ];
587						parent.trigger('change:' + this._relatedKey + '.' + key, parent, model.changed[ key ], options);
588					}
589
590					// Trigger "change:collection"
591					//parent.changed[ this._relatedKey ] = this;
592					parent.changed[ this._relatedKey ] = undefined;
593					parent.trigger('change:' + this._relatedKey, parent, options);
594					parent._triggerParentChange(options);
595				}
596
597				// Finally trigger "change"
598				parent.trigger('change', parent, options);
599			},
600			resetRelations: function ( options ) {
601				_.each(this.models, function ( model ) {
602					_.each(model.relations, function ( rel, key ) {
603						if ( model.get(key) instanceof BackboneCollection ) {
604							model.get(key).trigger('reset', model, options);
605						}
606					});
607				})
608			},
609			reset: function ( models, options ) {
610				options || (options = {});
611				for ( var i = 0, l = this.models.length; i < l; i++ ) {
612					this._removeReference(this.models[ i ]);
613				}
614				options.previousModels = this.models;
615				this._reset();
616				this.add(models, _.extend({ silent: true }, options));
617				if ( !options.silent ) {
618					this.trigger('reset', this, options);
619					this.resetRelations(options);
620				}
621				return this;
622			},
623			comparator: function ( model ) {
624				return model.get('index');
625			},
626			toCompactJSON: function () {
627				var models = _(this.models).map(function ( model ) {
628					return model instanceof BackboneModel ? model.toCompactJSON() : model.toJSON();
629				});
630				models.__proto__ = null;
631				return models;
632			},
633			_prepareModel: function( attrs, options ) {
634				if (attrs instanceof Model)
635					return attrs;
636				options = options ? _.clone(options) : {};
637				options.collection = this;
638
639				var modelClass = this.model;
640				if (attrs._rel && this.relations[attrs._rel])
641					modelClass = this.relations[attrs._rel];
642				var model = new modelClass(attrs, options);
643				if (!model.validationError) return model;
644				this.trigger('invalid', this, model.validationError, options);
645				return false;
646			}
647		})
648		// statics
649		_.extend(Collection, {
650			create: function ( models, protos, statics ) {
651				var statics = _.extend({}, statics, {
652					create: Collection.create,
653					extend: Collection.extend,
654					define: Collection.define,
655				});
656				if ( _.isArray(models) || isPrototypeOf(models, Model))
657					return Collection.extend(_.extend({}, protos, {
658						model: _.isArray(models) ? models[ 0 ] : models
659					}), statics);
660
661				else if ( _.isObject(models))
662					return Collection.extend(_.extend({}, protos, {
663						relations: models
664					}), statics);
665				else
666					return Collection.extend( protos, statics );
667			},
668			extend: function () {
669				return BackboneCollection.extend.apply(this, arguments)
670			}
671		})
672
673		Collection.define = Collection.create;
674
675		return Collection
676	}()
677
678	/* --- Utils --- */
679	function getComputedValue ( model, key ) {
680		if ( model.computes && model.computes[ key ] ) {
681			var compute = model.computes[ key ];
682			var deps = _(compute.deps).map(function ( dep ) {
683				return getComputedValue(model, dep);
684			})
685			// console.log('computed value', compute.get.apply(model, deps))
686			return compute.get.apply(model, deps);
687		}
688		return model.attributes[ key ];
689	}
690
691	function setPrototypeOf( child, prototype ) {
692		if (_.isFunction(Object.setPrototypeOf))
693			Object.setPrototypeOf(child.prototype || child, prototype);
694		else
695			(child.prototype || child).__proto__ = prototype;
696		return child
697	}
698
699	function isPrototypeOf( child, parent ) {
700		if ( !child || !parent )
701			return false;
702		var result = false;
703		var proto = child.prototype;
704		while ( proto ) {
705			if ( proto == parent.prototype ) {
706				result = true;
707				break;
708			}
709			proto = proto.__proto__;
710		}
711		return result;
712	}
713
714	return B;
715});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.