mirror of
https://github.com/swc-project/swc.git
synced 2024-12-22 13:11:31 +03:00
667 lines
34 KiB
JavaScript
667 lines
34 KiB
JavaScript
(function() {
|
|
var Backbone, root = this, previousBackbone = root.Backbone, slice = [].slice;
|
|
(Backbone = "undefined" != typeof exports ? exports : root.Backbone = {}).VERSION = "1.1.0";
|
|
var _ = root._;
|
|
_ || "undefined" == typeof require || (_ = require("underscore")), Backbone.$ = root.jQuery || root.Zepto || root.ender || root.$, Backbone.noConflict = function() {
|
|
return root.Backbone = previousBackbone, this;
|
|
}, Backbone.emulateHTTP = !1, Backbone.emulateJSON = !1;
|
|
var Events = Backbone.Events = {
|
|
on: function(name, callback, context) {
|
|
return eventsApi(this, "on", name, [
|
|
callback,
|
|
context
|
|
]) && callback && (this._events || (this._events = {}), (this._events[name] || (this._events[name] = [])).push({
|
|
callback: callback,
|
|
context: context,
|
|
ctx: context || this
|
|
})), this;
|
|
},
|
|
once: function(name, callback, context) {
|
|
if (!eventsApi(this, "once", name, [
|
|
callback,
|
|
context
|
|
]) || !callback) return this;
|
|
var self = this, once = _.once(function() {
|
|
self.off(name, once), callback.apply(this, arguments);
|
|
});
|
|
return once._callback = callback, this.on(name, once, context);
|
|
},
|
|
off: function(name, callback, context) {
|
|
var retain, ev, events, names, i, l, j, k;
|
|
if (!this._events || !eventsApi(this, "off", name, [
|
|
callback,
|
|
context
|
|
])) return this;
|
|
if (!name && !callback && !context) return this._events = {}, this;
|
|
for(i = 0, l = (names = name ? [
|
|
name
|
|
] : _.keys(this._events)).length; i < l; i++)if (name = names[i], events = this._events[name]) {
|
|
if (this._events[name] = retain = [], callback || context) for(j = 0, k = events.length; j < k; j++)ev = events[j], (callback && callback !== ev.callback && callback !== ev.callback._callback || context && context !== ev.context) && retain.push(ev);
|
|
retain.length || delete this._events[name];
|
|
}
|
|
return this;
|
|
},
|
|
trigger: function(name) {
|
|
if (!this._events) return this;
|
|
var args = slice.call(arguments, 1);
|
|
if (!eventsApi(this, "trigger", name, args)) return this;
|
|
var events = this._events[name], allEvents = this._events.all;
|
|
return events && triggerEvents(events, args), allEvents && triggerEvents(allEvents, arguments), this;
|
|
},
|
|
stopListening: function(obj, name, callback) {
|
|
var listeningTo = this._listeningTo;
|
|
if (!listeningTo) return this;
|
|
var remove = !name && !callback;
|
|
for(var id in callback || "object" != typeof name || (callback = this), obj && ((listeningTo = {})[obj._listenId] = obj), listeningTo)(obj = listeningTo[id]).off(name, callback, this), (remove || _.isEmpty(obj._events)) && delete this._listeningTo[id];
|
|
return this;
|
|
}
|
|
}, eventSplitter = /\s+/, eventsApi = function(obj, action, name, rest) {
|
|
if (!name) return !0;
|
|
if ("object" == typeof name) {
|
|
for(var key in name)obj[action].apply(obj, [
|
|
key,
|
|
name[key]
|
|
].concat(rest));
|
|
return !1;
|
|
}
|
|
if (eventSplitter.test(name)) {
|
|
for(var names = name.split(eventSplitter), i = 0, l = names.length; i < l; i++)obj[action].apply(obj, [
|
|
names[i]
|
|
].concat(rest));
|
|
return !1;
|
|
}
|
|
return !0;
|
|
}, triggerEvents = function(events, args) {
|
|
var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
|
|
switch(args.length){
|
|
case 0:
|
|
for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx);
|
|
return;
|
|
case 1:
|
|
for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx, a1);
|
|
return;
|
|
case 2:
|
|
for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx, a1, a2);
|
|
return;
|
|
case 3:
|
|
for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
|
|
return;
|
|
default:
|
|
for(; ++i < l;)(ev = events[i]).callback.apply(ev.ctx, args);
|
|
}
|
|
};
|
|
_.each({
|
|
listenTo: "on",
|
|
listenToOnce: "once"
|
|
}, function(implementation, method) {
|
|
Events[method] = function(obj, name, callback) {
|
|
return (this._listeningTo || (this._listeningTo = {}))[obj._listenId || (obj._listenId = _.uniqueId("l"))] = obj, callback || "object" != typeof name || (callback = this), obj[implementation](name, callback, this), this;
|
|
};
|
|
}), Events.bind = Events.on, Events.unbind = Events.off, _.extend(Backbone, Events);
|
|
var Model = Backbone.Model = function(attributes, options) {
|
|
var attrs = attributes || {};
|
|
options || (options = {}), this.cid = _.uniqueId("c"), this.attributes = {}, options.collection && (this.collection = options.collection), options.parse && (attrs = this.parse(attrs, options) || {}), attrs = _.defaults({}, attrs, _.result(this, "defaults")), this.set(attrs, options), this.changed = {}, this.initialize.apply(this, arguments);
|
|
};
|
|
_.extend(Model.prototype, Events, {
|
|
changed: null,
|
|
validationError: null,
|
|
idAttribute: "id",
|
|
initialize: function() {},
|
|
toJSON: function(options) {
|
|
return _.clone(this.attributes);
|
|
},
|
|
sync: function() {
|
|
return Backbone.sync.apply(this, arguments);
|
|
},
|
|
get: function(attr) {
|
|
return this.attributes[attr];
|
|
},
|
|
escape: function(attr) {
|
|
return _.escape(this.get(attr));
|
|
},
|
|
has: function(attr) {
|
|
return null != this.get(attr);
|
|
},
|
|
set: function(key, val, options) {
|
|
var attr, attrs, unset, changes, silent, changing, prev, current;
|
|
if (null == key) return this;
|
|
if ("object" == typeof key ? (attrs = key, options = val) : (attrs = {})[key] = val, options || (options = {}), !this._validate(attrs, options)) return !1;
|
|
for(attr in unset = options.unset, silent = options.silent, changes = [], changing = this._changing, this._changing = !0, changing || (this._previousAttributes = _.clone(this.attributes), this.changed = {}), current = this.attributes, prev = this._previousAttributes, this.idAttribute in attrs && (this.id = attrs[this.idAttribute]), attrs)val = attrs[attr], _.isEqual(current[attr], val) || changes.push(attr), _.isEqual(prev[attr], val) ? delete this.changed[attr] : this.changed[attr] = val, unset ? delete current[attr] : current[attr] = val;
|
|
if (!silent) {
|
|
changes.length && (this._pending = !0);
|
|
for(var i = 0, l = changes.length; i < l; i++)this.trigger("change:" + changes[i], this, current[changes[i]], options);
|
|
}
|
|
if (changing) return this;
|
|
if (!silent) for(; this._pending;)this._pending = !1, this.trigger("change", this, options);
|
|
return this._pending = !1, this._changing = !1, this;
|
|
},
|
|
unset: function(attr, options) {
|
|
return this.set(attr, void 0, _.extend({}, options, {
|
|
unset: !0
|
|
}));
|
|
},
|
|
clear: function(options) {
|
|
var attrs = {};
|
|
for(var key in this.attributes)attrs[key] = void 0;
|
|
return this.set(attrs, _.extend({}, options, {
|
|
unset: !0
|
|
}));
|
|
},
|
|
hasChanged: function(attr) {
|
|
return null == attr ? !_.isEmpty(this.changed) : _.has(this.changed, attr);
|
|
},
|
|
changedAttributes: function(diff) {
|
|
if (!diff) return !!this.hasChanged() && _.clone(this.changed);
|
|
var val, changed = !1, old = this._changing ? this._previousAttributes : this.attributes;
|
|
for(var attr in diff)_.isEqual(old[attr], val = diff[attr]) || ((changed || (changed = {}))[attr] = val);
|
|
return changed;
|
|
},
|
|
previous: function(attr) {
|
|
return null != attr && this._previousAttributes ? this._previousAttributes[attr] : null;
|
|
},
|
|
previousAttributes: function() {
|
|
return _.clone(this._previousAttributes);
|
|
},
|
|
fetch: function(options) {
|
|
void 0 === (options = options ? _.clone(options) : {}).parse && (options.parse = !0);
|
|
var model = this, success = options.success;
|
|
return options.success = function(resp) {
|
|
if (!model.set(model.parse(resp, options), options)) return !1;
|
|
success && success(model, resp, options), model.trigger("sync", model, resp, options);
|
|
}, wrapError(this, options), this.sync("read", this, options);
|
|
},
|
|
save: function(key, val, options) {
|
|
var attrs, method, xhr, attributes = this.attributes;
|
|
if (null == key || "object" == typeof key ? (attrs = key, options = val) : (attrs = {})[key] = val, options = _.extend({
|
|
validate: !0
|
|
}, options), attrs && !options.wait) {
|
|
if (!this.set(attrs, options)) return !1;
|
|
} else if (!this._validate(attrs, options)) return !1;
|
|
attrs && options.wait && (this.attributes = _.extend({}, attributes, attrs)), void 0 === options.parse && (options.parse = !0);
|
|
var model = this, success = options.success;
|
|
return options.success = function(resp) {
|
|
model.attributes = attributes;
|
|
var serverAttrs = model.parse(resp, options);
|
|
if (options.wait && (serverAttrs = _.extend(attrs || {}, serverAttrs)), _.isObject(serverAttrs) && !model.set(serverAttrs, options)) return !1;
|
|
success && success(model, resp, options), model.trigger("sync", model, resp, options);
|
|
}, wrapError(this, options), "patch" == (method = this.isNew() ? "create" : options.patch ? "patch" : "update") && (options.attrs = attrs), xhr = this.sync(method, this, options), attrs && options.wait && (this.attributes = attributes), xhr;
|
|
},
|
|
destroy: function(options) {
|
|
options = options ? _.clone(options) : {};
|
|
var model = this, success = options.success, destroy = function() {
|
|
model.trigger("destroy", model, model.collection, options);
|
|
};
|
|
if (options.success = function(resp) {
|
|
(options.wait || model.isNew()) && destroy(), success && success(model, resp, options), model.isNew() || model.trigger("sync", model, resp, options);
|
|
}, this.isNew()) return options.success(), !1;
|
|
wrapError(this, options);
|
|
var xhr = this.sync("delete", this, options);
|
|
return options.wait || destroy(), xhr;
|
|
},
|
|
url: function() {
|
|
var base = _.result(this, "urlRoot") || _.result(this.collection, "url") || urlError();
|
|
return this.isNew() ? base : base + ("/" === base.charAt(base.length - 1) ? "" : "/") + encodeURIComponent(this.id);
|
|
},
|
|
parse: function(resp, options) {
|
|
return resp;
|
|
},
|
|
clone: function() {
|
|
return new this.constructor(this.attributes);
|
|
},
|
|
isNew: function() {
|
|
return null == this.id;
|
|
},
|
|
isValid: function(options) {
|
|
return this._validate({}, _.extend(options || {}, {
|
|
validate: !0
|
|
}));
|
|
},
|
|
_validate: function(attrs, options) {
|
|
if (!options.validate || !this.validate) return !0;
|
|
attrs = _.extend({}, this.attributes, attrs);
|
|
var error = this.validationError = this.validate(attrs, options) || null;
|
|
return !error || (this.trigger("invalid", this, error, _.extend(options, {
|
|
validationError: error
|
|
})), !1);
|
|
}
|
|
}), _.each([
|
|
"keys",
|
|
"values",
|
|
"pairs",
|
|
"invert",
|
|
"pick",
|
|
"omit"
|
|
], function(method) {
|
|
Model.prototype[method] = function() {
|
|
var args = slice.call(arguments);
|
|
return args.unshift(this.attributes), _[method].apply(_, args);
|
|
};
|
|
});
|
|
var Collection = Backbone.Collection = function(models, options) {
|
|
options || (options = {}), options.model && (this.model = options.model), void 0 !== options.comparator && (this.comparator = options.comparator), this._reset(), this.initialize.apply(this, arguments), models && this.reset(models, _.extend({
|
|
silent: !0
|
|
}, options));
|
|
}, setOptions = {
|
|
add: !0,
|
|
remove: !0,
|
|
merge: !0
|
|
}, addOptions = {
|
|
add: !0,
|
|
remove: !1
|
|
};
|
|
_.extend(Collection.prototype, Events, {
|
|
model: Model,
|
|
initialize: function() {},
|
|
toJSON: function(options) {
|
|
return this.map(function(model) {
|
|
return model.toJSON(options);
|
|
});
|
|
},
|
|
sync: function() {
|
|
return Backbone.sync.apply(this, arguments);
|
|
},
|
|
add: function(models, options) {
|
|
return this.set(models, _.extend({
|
|
merge: !1
|
|
}, options, addOptions));
|
|
},
|
|
remove: function(models, options) {
|
|
var i, l, index, model, singular = !_.isArray(models);
|
|
for(models = singular ? [
|
|
models
|
|
] : _.clone(models), options || (options = {}), i = 0, l = models.length; i < l; i++)(model = models[i] = this.get(models[i])) && (delete this._byId[model.id], delete this._byId[model.cid], index = this.indexOf(model), this.models.splice(index, 1), this.length--, options.silent || (options.index = index, model.trigger("remove", model, this, options)), this._removeReference(model));
|
|
return singular ? models[0] : models;
|
|
},
|
|
set: function(models, options) {
|
|
(options = _.defaults({}, options, setOptions)).parse && (models = this.parse(models, options));
|
|
var i, l, id, model, attrs, existing, sort, singular = !_.isArray(models);
|
|
models = singular ? models ? [
|
|
models
|
|
] : [] : _.clone(models);
|
|
var at = options.at, targetModel = this.model, sortable = this.comparator && null == at && !1 !== options.sort, sortAttr = _.isString(this.comparator) ? this.comparator : null, toAdd = [], toRemove = [], modelMap = {}, add = options.add, merge = options.merge, remove = options.remove, order = !sortable && !!add && !!remove && [];
|
|
for(i = 0, l = models.length; i < l; i++){
|
|
if (id = (attrs = models[i]) instanceof Model ? model = attrs : attrs[targetModel.prototype.idAttribute], existing = this.get(id)) remove && (modelMap[existing.cid] = !0), merge && (attrs = attrs === model ? model.attributes : attrs, options.parse && (attrs = existing.parse(attrs, options)), existing.set(attrs, options), sortable && !sort && existing.hasChanged(sortAttr) && (sort = !0)), models[i] = existing;
|
|
else if (add) {
|
|
if (!(model = models[i] = this._prepareModel(attrs, options))) continue;
|
|
toAdd.push(model), model.on("all", this._onModelEvent, this), this._byId[model.cid] = model, null != model.id && (this._byId[model.id] = model);
|
|
}
|
|
order && order.push(existing || model);
|
|
}
|
|
if (remove) {
|
|
for(i = 0, l = this.length; i < l; ++i)modelMap[(model = this.models[i]).cid] || toRemove.push(model);
|
|
toRemove.length && this.remove(toRemove, options);
|
|
}
|
|
if (toAdd.length || order && order.length) {
|
|
if (sortable && (sort = !0), this.length += toAdd.length, null != at) for(i = 0, l = toAdd.length; i < l; i++)this.models.splice(at + i, 0, toAdd[i]);
|
|
else {
|
|
order && (this.models.length = 0);
|
|
var orderedModels = order || toAdd;
|
|
for(i = 0, l = orderedModels.length; i < l; i++)this.models.push(orderedModels[i]);
|
|
}
|
|
}
|
|
if (sort && this.sort({
|
|
silent: !0
|
|
}), !options.silent) {
|
|
for(i = 0, l = toAdd.length; i < l; i++)(model = toAdd[i]).trigger("add", model, this, options);
|
|
(sort || order && order.length) && this.trigger("sort", this, options);
|
|
}
|
|
return singular ? models[0] : models;
|
|
},
|
|
reset: function(models, options) {
|
|
options || (options = {});
|
|
for(var i = 0, l = this.models.length; i < l; i++)this._removeReference(this.models[i]);
|
|
return options.previousModels = this.models, this._reset(), models = this.add(models, _.extend({
|
|
silent: !0
|
|
}, options)), options.silent || this.trigger("reset", this, options), models;
|
|
},
|
|
push: function(model, options) {
|
|
return this.add(model, _.extend({
|
|
at: this.length
|
|
}, options));
|
|
},
|
|
pop: function(options) {
|
|
var model = this.at(this.length - 1);
|
|
return this.remove(model, options), model;
|
|
},
|
|
unshift: function(model, options) {
|
|
return this.add(model, _.extend({
|
|
at: 0
|
|
}, options));
|
|
},
|
|
shift: function(options) {
|
|
var model = this.at(0);
|
|
return this.remove(model, options), model;
|
|
},
|
|
slice: function() {
|
|
return slice.apply(this.models, arguments);
|
|
},
|
|
get: function(obj) {
|
|
if (null != obj) return this._byId[obj.id] || this._byId[obj.cid] || this._byId[obj];
|
|
},
|
|
at: function(index) {
|
|
return this.models[index];
|
|
},
|
|
where: function(attrs, first) {
|
|
return _.isEmpty(attrs) ? first ? void 0 : [] : this[first ? "find" : "filter"](function(model) {
|
|
for(var key in attrs)if (attrs[key] !== model.get(key)) return !1;
|
|
return !0;
|
|
});
|
|
},
|
|
findWhere: function(attrs) {
|
|
return this.where(attrs, !0);
|
|
},
|
|
sort: function(options) {
|
|
if (!this.comparator) throw Error("Cannot sort a set without a comparator");
|
|
return options || (options = {}), _.isString(this.comparator) || 1 === this.comparator.length ? this.models = this.sortBy(this.comparator, this) : this.models.sort(_.bind(this.comparator, this)), options.silent || this.trigger("sort", this, options), this;
|
|
},
|
|
pluck: function(attr) {
|
|
return _.invoke(this.models, "get", attr);
|
|
},
|
|
fetch: function(options) {
|
|
void 0 === (options = options ? _.clone(options) : {}).parse && (options.parse = !0);
|
|
var success = options.success, collection = this;
|
|
return options.success = function(resp) {
|
|
collection[options.reset ? "reset" : "set"](resp, options), success && success(collection, resp, options), collection.trigger("sync", collection, resp, options);
|
|
}, wrapError(this, options), this.sync("read", this, options);
|
|
},
|
|
create: function(model, options) {
|
|
if (options = options ? _.clone(options) : {}, !(model = this._prepareModel(model, options))) return !1;
|
|
options.wait || this.add(model, options);
|
|
var collection = this, success = options.success;
|
|
return options.success = function(model, resp, options) {
|
|
options.wait && collection.add(model, options), success && success(model, resp, options);
|
|
}, model.save(null, options), model;
|
|
},
|
|
parse: function(resp, options) {
|
|
return resp;
|
|
},
|
|
clone: function() {
|
|
return new this.constructor(this.models);
|
|
},
|
|
_reset: function() {
|
|
this.length = 0, this.models = [], this._byId = {};
|
|
},
|
|
_prepareModel: function(attrs, options) {
|
|
if (attrs instanceof Model) return attrs.collection || (attrs.collection = this), attrs;
|
|
(options = options ? _.clone(options) : {}).collection = this;
|
|
var model = new this.model(attrs, options);
|
|
return model.validationError ? (this.trigger("invalid", this, model.validationError, options), !1) : model;
|
|
},
|
|
_removeReference: function(model) {
|
|
this === model.collection && delete model.collection, model.off("all", this._onModelEvent, this);
|
|
},
|
|
_onModelEvent: function(event, model, collection, options) {
|
|
("add" !== event && "remove" !== event || collection === this) && ("destroy" === event && this.remove(model, options), model && event === "change:" + model.idAttribute && (delete this._byId[model.previous(model.idAttribute)], null != model.id && (this._byId[model.id] = model)), this.trigger.apply(this, arguments));
|
|
}
|
|
}), _.each([
|
|
"forEach",
|
|
"each",
|
|
"map",
|
|
"collect",
|
|
"reduce",
|
|
"foldl",
|
|
"inject",
|
|
"reduceRight",
|
|
"foldr",
|
|
"find",
|
|
"detect",
|
|
"filter",
|
|
"select",
|
|
"reject",
|
|
"every",
|
|
"all",
|
|
"some",
|
|
"any",
|
|
"include",
|
|
"contains",
|
|
"invoke",
|
|
"max",
|
|
"min",
|
|
"toArray",
|
|
"size",
|
|
"first",
|
|
"head",
|
|
"take",
|
|
"initial",
|
|
"rest",
|
|
"tail",
|
|
"drop",
|
|
"last",
|
|
"without",
|
|
"difference",
|
|
"indexOf",
|
|
"shuffle",
|
|
"lastIndexOf",
|
|
"isEmpty",
|
|
"chain"
|
|
], function(method) {
|
|
Collection.prototype[method] = function() {
|
|
var args = slice.call(arguments);
|
|
return args.unshift(this.models), _[method].apply(_, args);
|
|
};
|
|
}), _.each([
|
|
"groupBy",
|
|
"countBy",
|
|
"sortBy"
|
|
], function(method) {
|
|
Collection.prototype[method] = function(value, context) {
|
|
var iterator = _.isFunction(value) ? value : function(model) {
|
|
return model.get(value);
|
|
};
|
|
return _[method](this.models, iterator, context);
|
|
};
|
|
});
|
|
var View = Backbone.View = function(options) {
|
|
this.cid = _.uniqueId("view"), options || (options = {}), _.extend(this, _.pick(options, viewOptions)), this._ensureElement(), this.initialize.apply(this, arguments), this.delegateEvents();
|
|
}, delegateEventSplitter = /^(\S+)\s*(.*)$/, viewOptions = [
|
|
"model",
|
|
"collection",
|
|
"el",
|
|
"id",
|
|
"attributes",
|
|
"className",
|
|
"tagName",
|
|
"events"
|
|
];
|
|
_.extend(View.prototype, Events, {
|
|
tagName: "div",
|
|
$: function(selector) {
|
|
return this.$el.find(selector);
|
|
},
|
|
initialize: function() {},
|
|
render: function() {
|
|
return this;
|
|
},
|
|
remove: function() {
|
|
return this.$el.remove(), this.stopListening(), this;
|
|
},
|
|
setElement: function(element, delegate) {
|
|
return this.$el && this.undelegateEvents(), this.$el = element instanceof Backbone.$ ? element : Backbone.$(element), this.el = this.$el[0], !1 !== delegate && this.delegateEvents(), this;
|
|
},
|
|
delegateEvents: function(events) {
|
|
if (!(events || (events = _.result(this, "events")))) return this;
|
|
for(var key in this.undelegateEvents(), events){
|
|
var method = events[key];
|
|
if (_.isFunction(method) || (method = this[events[key]]), method) {
|
|
var match = key.match(delegateEventSplitter), eventName = match[1], selector = match[2];
|
|
method = _.bind(method, this), eventName += ".delegateEvents" + this.cid, "" === selector ? this.$el.on(eventName, method) : this.$el.on(eventName, selector, method);
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
undelegateEvents: function() {
|
|
return this.$el.off(".delegateEvents" + this.cid), this;
|
|
},
|
|
_ensureElement: function() {
|
|
if (this.el) this.setElement(_.result(this, "el"), !1);
|
|
else {
|
|
var attrs = _.extend({}, _.result(this, "attributes"));
|
|
this.id && (attrs.id = _.result(this, "id")), this.className && (attrs.class = _.result(this, "className"));
|
|
var $el = Backbone.$("<" + _.result(this, "tagName") + ">").attr(attrs);
|
|
this.setElement($el, !1);
|
|
}
|
|
}
|
|
}), Backbone.sync = function(method, model, options) {
|
|
var type = methodMap[method];
|
|
_.defaults(options || (options = {}), {
|
|
emulateHTTP: Backbone.emulateHTTP,
|
|
emulateJSON: Backbone.emulateJSON
|
|
});
|
|
var params = {
|
|
type: type,
|
|
dataType: "json"
|
|
};
|
|
if (options.url || (params.url = _.result(model, "url") || urlError()), null == options.data && model && ("create" === method || "update" === method || "patch" === method) && (params.contentType = "application/json", params.data = JSON.stringify(options.attrs || model.toJSON(options))), options.emulateJSON && (params.contentType = "application/x-www-form-urlencoded", params.data = params.data ? {
|
|
model: params.data
|
|
} : {}), options.emulateHTTP && ("PUT" === type || "DELETE" === type || "PATCH" === type)) {
|
|
params.type = "POST", options.emulateJSON && (params.data._method = type);
|
|
var beforeSend = options.beforeSend;
|
|
options.beforeSend = function(xhr) {
|
|
if (xhr.setRequestHeader("X-HTTP-Method-Override", type), beforeSend) return beforeSend.apply(this, arguments);
|
|
};
|
|
}
|
|
"GET" === params.type || options.emulateJSON || (params.processData = !1), "PATCH" === params.type && noXhrPatch && (params.xhr = function() {
|
|
return new ActiveXObject("Microsoft.XMLHTTP");
|
|
});
|
|
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
|
|
return model.trigger("request", model, xhr, options), xhr;
|
|
};
|
|
var noXhrPatch = "undefined" != typeof window && !!window.ActiveXObject && !(window.XMLHttpRequest && new XMLHttpRequest().dispatchEvent), methodMap = {
|
|
create: "POST",
|
|
update: "PUT",
|
|
patch: "PATCH",
|
|
delete: "DELETE",
|
|
read: "GET"
|
|
};
|
|
Backbone.ajax = function() {
|
|
return Backbone.$.ajax.apply(Backbone.$, arguments);
|
|
};
|
|
var Router = Backbone.Router = function(options) {
|
|
options || (options = {}), options.routes && (this.routes = options.routes), this._bindRoutes(), this.initialize.apply(this, arguments);
|
|
}, optionalParam = /\((.*?)\)/g, namedParam = /(\(\?)?:\w+/g, splatParam = /\*\w+/g, escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
|
|
_.extend(Router.prototype, Events, {
|
|
initialize: function() {},
|
|
route: function(route, name, callback) {
|
|
_.isRegExp(route) || (route = this._routeToRegExp(route)), _.isFunction(name) && (callback = name, name = ""), callback || (callback = this[name]);
|
|
var router = this;
|
|
return Backbone.history.route(route, function(fragment) {
|
|
var args = router._extractParameters(route, fragment);
|
|
callback && callback.apply(router, args), router.trigger.apply(router, [
|
|
"route:" + name
|
|
].concat(args)), router.trigger("route", name, args), Backbone.history.trigger("route", router, name, args);
|
|
}), this;
|
|
},
|
|
navigate: function(fragment, options) {
|
|
return Backbone.history.navigate(fragment, options), this;
|
|
},
|
|
_bindRoutes: function() {
|
|
if (this.routes) {
|
|
this.routes = _.result(this, "routes");
|
|
for(var route, routes = _.keys(this.routes); null != (route = routes.pop());)this.route(route, this.routes[route]);
|
|
}
|
|
},
|
|
_routeToRegExp: function(route) {
|
|
return RegExp("^" + (route = route.replace(escapeRegExp, "\\$&").replace(optionalParam, "(?:$1)?").replace(namedParam, function(match, optional) {
|
|
return optional ? match : "([^/]+)";
|
|
}).replace(splatParam, "(.*?)")) + "$");
|
|
},
|
|
_extractParameters: function(route, fragment) {
|
|
var params = route.exec(fragment).slice(1);
|
|
return _.map(params, function(param) {
|
|
return param ? decodeURIComponent(param) : null;
|
|
});
|
|
}
|
|
});
|
|
var History = Backbone.History = function() {
|
|
this.handlers = [], _.bindAll(this, "checkUrl"), "undefined" != typeof window && (this.location = window.location, this.history = window.history);
|
|
}, routeStripper = /^[#\/]|\s+$/g, rootStripper = /^\/+|\/+$/g, isExplorer = /msie [\w.]+/, trailingSlash = /\/$/, pathStripper = /[?#].*$/;
|
|
History.started = !1, _.extend(History.prototype, Events, {
|
|
interval: 50,
|
|
getHash: function(window1) {
|
|
var match = (window1 || this).location.href.match(/#(.*)$/);
|
|
return match ? match[1] : "";
|
|
},
|
|
getFragment: function(fragment, forcePushState) {
|
|
if (null == fragment) {
|
|
if (this._hasPushState || !this._wantsHashChange || forcePushState) {
|
|
fragment = this.location.pathname;
|
|
var root = this.root.replace(trailingSlash, "");
|
|
fragment.indexOf(root) || (fragment = fragment.slice(root.length));
|
|
} else fragment = this.getHash();
|
|
}
|
|
return fragment.replace(routeStripper, "");
|
|
},
|
|
start: function(options) {
|
|
if (History.started) throw Error("Backbone.history has already been started");
|
|
History.started = !0, this.options = _.extend({
|
|
root: "/"
|
|
}, this.options, options), this.root = this.options.root, this._wantsHashChange = !1 !== this.options.hashChange, this._wantsPushState = !!this.options.pushState, this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
|
|
var fragment = this.getFragment(), docMode = document.documentMode, oldIE = isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7);
|
|
this.root = ("/" + this.root + "/").replace(rootStripper, "/"), oldIE && this._wantsHashChange && (this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow, this.navigate(fragment)), this._hasPushState ? Backbone.$(window).on("popstate", this.checkUrl) : this._wantsHashChange && "onhashchange" in window && !oldIE ? Backbone.$(window).on("hashchange", this.checkUrl) : this._wantsHashChange && (this._checkUrlInterval = setInterval(this.checkUrl, this.interval)), this.fragment = fragment;
|
|
var loc = this.location, atRoot = loc.pathname.replace(/[^\/]$/, "$&/") === this.root;
|
|
if (this._wantsHashChange && this._wantsPushState) {
|
|
if (!this._hasPushState && !atRoot) return this.fragment = this.getFragment(null, !0), this.location.replace(this.root + this.location.search + "#" + this.fragment), !0;
|
|
this._hasPushState && atRoot && loc.hash && (this.fragment = this.getHash().replace(routeStripper, ""), this.history.replaceState({}, document.title, this.root + this.fragment + loc.search));
|
|
}
|
|
if (!this.options.silent) return this.loadUrl();
|
|
},
|
|
stop: function() {
|
|
Backbone.$(window).off("popstate", this.checkUrl).off("hashchange", this.checkUrl), clearInterval(this._checkUrlInterval), History.started = !1;
|
|
},
|
|
route: function(route, callback) {
|
|
this.handlers.unshift({
|
|
route: route,
|
|
callback: callback
|
|
});
|
|
},
|
|
checkUrl: function(e) {
|
|
var current = this.getFragment();
|
|
if (current === this.fragment && this.iframe && (current = this.getFragment(this.getHash(this.iframe))), current === this.fragment) return !1;
|
|
this.iframe && this.navigate(current), this.loadUrl();
|
|
},
|
|
loadUrl: function(fragment) {
|
|
return fragment = this.fragment = this.getFragment(fragment), _.any(this.handlers, function(handler) {
|
|
if (handler.route.test(fragment)) return handler.callback(fragment), !0;
|
|
});
|
|
},
|
|
navigate: function(fragment, options) {
|
|
if (!History.started) return !1;
|
|
options && !0 !== options || (options = {
|
|
trigger: !!options
|
|
});
|
|
var url = this.root + (fragment = this.getFragment(fragment || ""));
|
|
if (fragment = fragment.replace(pathStripper, ""), this.fragment !== fragment) {
|
|
if (this.fragment = fragment, "" === fragment && "/" !== url && (url = url.slice(0, -1)), this._hasPushState) this.history[options.replace ? "replaceState" : "pushState"]({}, document.title, url);
|
|
else {
|
|
if (!this._wantsHashChange) return this.location.assign(url);
|
|
this._updateHash(this.location, fragment, options.replace), this.iframe && fragment !== this.getFragment(this.getHash(this.iframe)) && (options.replace || this.iframe.document.open().close(), this._updateHash(this.iframe.location, fragment, options.replace));
|
|
}
|
|
if (options.trigger) return this.loadUrl(fragment);
|
|
}
|
|
},
|
|
_updateHash: function(location, fragment, replace) {
|
|
if (replace) {
|
|
var href = location.href.replace(/(javascript:|#).*$/, "");
|
|
location.replace(href + "#" + fragment);
|
|
} else location.hash = "#" + fragment;
|
|
}
|
|
}), Backbone.history = new History(), Model.extend = Collection.extend = Router.extend = View.extend = History.extend = function(protoProps, staticProps) {
|
|
var child, parent = this;
|
|
child = protoProps && _.has(protoProps, "constructor") ? protoProps.constructor : function() {
|
|
return parent.apply(this, arguments);
|
|
}, _.extend(child, parent, staticProps);
|
|
var Surrogate = function() {
|
|
this.constructor = child;
|
|
};
|
|
return Surrogate.prototype = parent.prototype, child.prototype = new Surrogate(), protoProps && _.extend(child.prototype, protoProps), child.__super__ = parent.prototype, child;
|
|
};
|
|
var urlError = function() {
|
|
throw Error('A "url" property or function must be specified');
|
|
}, wrapError = function(model, options) {
|
|
var error = options.error;
|
|
options.error = function(resp) {
|
|
error && error(model, resp, options), model.trigger("error", model, resp, options);
|
|
};
|
|
};
|
|
}).call(this);
|