swc/ecmascript/minifier/tests/projects/refs/backbone-1.1.0.js

2273 lines
68 KiB
JavaScript
Raw Normal View History

(function (
) {
var Backbone,
root = this,
previousBackbone = root.Backbone,
array = [],
slice = array.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)
: 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 (
options || (options = {
}),
i = 0,
l = (models = singular
? [models,]
: _.clone(
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 new 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
) {
var method = options.reset ? "reset" : "set";
collection[method](
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 (
(route = route
.replace(
escapeRegExp,
"\\$&"
)
.replace(
optionalParam,
"(?:$1)?"
)
.replace(
namedParam,
function (
match, optional
) {
return optional ? match : "([^/]+)";
}
)
.replace(
splatParam,
"(.*?)"
)),
new RegExp(
"^" + route + "$"
)
);
},
_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 (
window
) {
var match = (window || 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 new 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,
));
}
return options.trigger
? this.loadUrl(
fragment
)
: void 0;
}
},
_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 new 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
));