Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
todomvc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Eugene Shen
todomvc
Commits
eec62c61
Commit
eec62c61
authored
Aug 24, 2015
by
Sam Saccone
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update vendor dependencies
* backbone * jquery * underscore * todomvc common js
parent
daaa9a40
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1171 additions
and
743 deletions
+1171
-743
examples/backbone/node_modules/backbone/backbone.js
examples/backbone/node_modules/backbone/backbone.js
+716
-431
examples/backbone/node_modules/jquery/dist/jquery.js
examples/backbone/node_modules/jquery/dist/jquery.js
+9
-4
examples/backbone/node_modules/todomvc-common/base.js
examples/backbone/node_modules/todomvc-common/base.js
+7
-2
examples/backbone/node_modules/underscore/underscore.js
examples/backbone/node_modules/underscore/underscore.js
+439
-306
No files found.
examples/backbone/node_modules/backbone/backbone.js
View file @
eec62c61
// Backbone.js 1.
1
.2
// Backbone.js 1.
2
.2
// (c) 2010-201
4
Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// (c) 2010-201
5
Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Backbone may be freely distributed under the MIT license.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// For all details and documentation:
// http://backbonejs.org
// http://backbonejs.org
(
function
(
root
,
factory
)
{
(
function
(
factory
)
{
// Establish the root object, `window` (`self`) in the browser, or `global` on the server.
// We use `self` instead of `window` for `WebWorker` support.
var
root
=
(
typeof
self
==
'
object
'
&&
self
.
self
==
self
&&
self
)
||
(
typeof
global
==
'
object
'
&&
global
.
global
==
global
&&
global
);
// Set up Backbone appropriately for the environment. Start with AMD.
// Set up Backbone appropriately for the environment. Start with AMD.
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
...
@@ -17,15 +22,16 @@
...
@@ -17,15 +22,16 @@
// Next for Node.js or CommonJS. jQuery may not be needed as a module.
// Next for Node.js or CommonJS. jQuery may not be needed as a module.
}
else
if
(
typeof
exports
!==
'
undefined
'
)
{
}
else
if
(
typeof
exports
!==
'
undefined
'
)
{
var
_
=
require
(
'
underscore
'
);
var
_
=
require
(
'
underscore
'
),
$
;
factory
(
root
,
exports
,
_
);
try
{
$
=
require
(
'
jquery
'
);
}
catch
(
e
)
{}
factory
(
root
,
exports
,
_
,
$
);
// Finally, as a browser global.
// Finally, as a browser global.
}
else
{
}
else
{
root
.
Backbone
=
factory
(
root
,
{},
root
.
_
,
(
root
.
jQuery
||
root
.
Zepto
||
root
.
ender
||
root
.
$
));
root
.
Backbone
=
factory
(
root
,
{},
root
.
_
,
(
root
.
jQuery
||
root
.
Zepto
||
root
.
ender
||
root
.
$
));
}
}
}(
this
,
function
(
root
,
Backbone
,
_
,
$
)
{
}(
function
(
root
,
Backbone
,
_
,
$
)
{
// Initial Setup
// Initial Setup
// -------------
// -------------
...
@@ -34,14 +40,11 @@
...
@@ -34,14 +40,11 @@
// restored later on, if `noConflict` is used.
// restored later on, if `noConflict` is used.
var
previousBackbone
=
root
.
Backbone
;
var
previousBackbone
=
root
.
Backbone
;
// Create local references to array methods we'll want to use later.
// Create a local reference to a common array method we'll want to use later.
var
array
=
[];
var
slice
=
Array
.
prototype
.
slice
;
var
push
=
array
.
push
;
var
slice
=
array
.
slice
;
var
splice
=
array
.
splice
;
// Current version of the library. Keep in sync with `package.json`.
// Current version of the library. Keep in sync with `package.json`.
Backbone
.
VERSION
=
'
1.
1
.2
'
;
Backbone
.
VERSION
=
'
1.
2
.2
'
;
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
// the `$` variable.
// the `$` variable.
...
@@ -60,17 +63,65 @@
...
@@ -60,17 +63,65 @@
Backbone
.
emulateHTTP
=
false
;
Backbone
.
emulateHTTP
=
false
;
// Turn on `emulateJSON` to support legacy servers that can't deal with direct
// Turn on `emulateJSON` to support legacy servers that can't deal with direct
// `application/json` requests ... will encode the body as
// `application/json` requests ...
this
will encode the body as
// `application/x-www-form-urlencoded` instead and will send the model in a
// `application/x-www-form-urlencoded` instead and will send the model in a
// form param named `model`.
// form param named `model`.
Backbone
.
emulateJSON
=
false
;
Backbone
.
emulateJSON
=
false
;
// Proxy Backbone class methods to Underscore functions, wrapping the model's
// `attributes` object or collection's `models` array behind the scenes.
//
// collection.filter(function(model) { return model.get('age') > 10 });
// collection.each(this.addView);
//
// `Function#apply` can be slow so we use the method's arg count, if we know it.
var
addMethod
=
function
(
length
,
method
,
attribute
)
{
switch
(
length
)
{
case
1
:
return
function
()
{
return
_
[
method
](
this
[
attribute
]);
};
case
2
:
return
function
(
value
)
{
return
_
[
method
](
this
[
attribute
],
value
);
};
case
3
:
return
function
(
iteratee
,
context
)
{
return
_
[
method
](
this
[
attribute
],
cb
(
iteratee
,
this
),
context
);
};
case
4
:
return
function
(
iteratee
,
defaultVal
,
context
)
{
return
_
[
method
](
this
[
attribute
],
cb
(
iteratee
,
this
),
defaultVal
,
context
);
};
default
:
return
function
()
{
var
args
=
slice
.
call
(
arguments
);
args
.
unshift
(
this
[
attribute
]);
return
_
[
method
].
apply
(
_
,
args
);
};
}
};
var
addUnderscoreMethods
=
function
(
Class
,
methods
,
attribute
)
{
_
.
each
(
methods
,
function
(
length
,
method
)
{
if
(
_
[
method
])
Class
.
prototype
[
method
]
=
addMethod
(
length
,
method
,
attribute
);
});
};
// Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
var
cb
=
function
(
iteratee
,
instance
)
{
if
(
_
.
isFunction
(
iteratee
))
return
iteratee
;
if
(
_
.
isObject
(
iteratee
)
&&
!
instance
.
_isModel
(
iteratee
))
return
modelMatcher
(
iteratee
);
if
(
_
.
isString
(
iteratee
))
return
function
(
model
)
{
return
model
.
get
(
iteratee
);
};
return
iteratee
;
};
var
modelMatcher
=
function
(
attrs
)
{
var
matcher
=
_
.
matches
(
attrs
);
return
function
(
model
)
{
return
matcher
(
model
.
attributes
);
};
};
// Backbone.Events
// Backbone.Events
// ---------------
// ---------------
// A module that can be mixed in to *any object* in order to provide it with
// A module that can be mixed in to *any object* in order to provide it with
//
custom events. You may bind with `on` or remove with `off` callback
//
a custom event channel. You may bind a callback to an event with `on` or
//
functions to an event
; `trigger`-ing an event fires all callbacks in
//
remove with `off`
; `trigger`-ing an event fires all callbacks in
// succession.
// succession.
//
//
// var object = {};
// var object = {};
...
@@ -78,123 +129,234 @@
...
@@ -78,123 +129,234 @@
// object.on('expand', function(){ alert('expanded'); });
// object.on('expand', function(){ alert('expanded'); });
// object.trigger('expand');
// object.trigger('expand');
//
//
var
Events
=
Backbone
.
Events
=
{
var
Events
=
Backbone
.
Events
=
{};
// Bind an event to a `callback` function. Passing `"all"` will bind
// the callback to all events fired.
on
:
function
(
name
,
callback
,
context
)
{
if
(
!
eventsApi
(
this
,
'
on
'
,
name
,
[
callback
,
context
])
||
!
callback
)
return
this
;
this
.
_events
||
(
this
.
_events
=
{});
var
events
=
this
.
_events
[
name
]
||
(
this
.
_events
[
name
]
=
[]);
events
.
push
({
callback
:
callback
,
context
:
context
,
ctx
:
context
||
this
});
return
this
;
},
// Bind an event to only be triggered a single time. After the first time
// Regular expression used to split event strings.
// the callback is invoked, it will be removed.
var
eventSplitter
=
/
\s
+/
;
once
:
function
(
name
,
callback
,
context
)
{
if
(
!
eventsApi
(
this
,
'
once
'
,
name
,
[
callback
,
context
])
||
!
callback
)
return
this
;
// Iterates over the standard `event, callback` (as well as the fancy multiple
var
self
=
this
;
// space-separated events `"change blur", callback` and jQuery-style event
var
once
=
_
.
once
(
function
()
{
// maps `{event: callback}`).
self
.
off
(
name
,
once
);
var
eventsApi
=
function
(
iteratee
,
events
,
name
,
callback
,
opts
)
{
callback
.
apply
(
this
,
arguments
);
var
i
=
0
,
names
;
});
if
(
name
&&
typeof
name
===
'
object
'
)
{
once
.
_callback
=
callback
;
// Handle event maps.
return
this
.
on
(
name
,
once
,
context
);
if
(
callback
!==
void
0
&&
'
context
'
in
opts
&&
opts
.
context
===
void
0
)
opts
.
context
=
callback
;
},
for
(
names
=
_
.
keys
(
name
);
i
<
names
.
length
;
i
++
)
{
events
=
eventsApi
(
iteratee
,
events
,
names
[
i
],
name
[
names
[
i
]],
opts
);
// Remove one or many callbacks. If `context` is null, removes all
// callbacks with that function. If `callback` is null, removes all
// callbacks for the event. If `name` is null, removes all bound
// callbacks for all events.
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
)
{
this
.
_events
=
void
0
;
return
this
;
}
}
names
=
name
?
[
name
]
:
_
.
keys
(
this
.
_events
);
}
else
if
(
name
&&
eventSplitter
.
test
(
name
))
{
for
(
i
=
0
,
l
=
names
.
length
;
i
<
l
;
i
++
)
{
// Handle space separated event names by delegating them individually.
name
=
names
[
i
];
for
(
names
=
name
.
split
(
eventSplitter
);
i
<
names
.
length
;
i
++
)
{
if
(
events
=
this
.
_events
[
name
])
{
events
=
iteratee
(
events
,
names
[
i
],
callback
,
opts
);
this
.
_events
[
name
]
=
retain
=
[];
if
(
callback
||
context
)
{
for
(
j
=
0
,
k
=
events
.
length
;
j
<
k
;
j
++
)
{
ev
=
events
[
j
];
if
((
callback
&&
callback
!==
ev
.
callback
&&
callback
!==
ev
.
callback
.
_callback
)
||
(
context
&&
context
!==
ev
.
context
))
{
retain
.
push
(
ev
);
}
}
}
if
(
!
retain
.
length
)
delete
this
.
_events
[
name
];
}
}
}
}
else
{
// Finally, standard events.
events
=
iteratee
(
events
,
name
,
callback
,
opts
);
}
return
events
;
};
return
this
;
// Bind an event to a `callback` function. Passing `"all"` will bind
},
// the callback to all events fired.
Events
.
on
=
function
(
name
,
callback
,
context
)
{
return
internalOn
(
this
,
name
,
callback
,
context
);
};
// Trigger one or many events, firing all bound callbacks. Callbacks are
// Guard the `listening` argument from the public API.
// passed the same arguments as `trigger` is, apart from the event name
var
internalOn
=
function
(
obj
,
name
,
callback
,
context
,
listening
)
{
// (unless you're listening on `"all"`, which will cause your callback to
obj
.
_events
=
eventsApi
(
onApi
,
obj
.
_events
||
{},
name
,
callback
,
{
// receive the true name of the event as the first argument).
context
:
context
,
trigger
:
function
(
name
)
{
ctx
:
obj
,
if
(
!
this
.
_events
)
return
this
;
listening
:
listening
var
args
=
slice
.
call
(
arguments
,
1
);
});
if
(
!
eventsApi
(
this
,
'
trigger
'
,
name
,
args
))
return
this
;
var
events
=
this
.
_events
[
name
];
var
allEvents
=
this
.
_events
.
all
;
if
(
events
)
triggerEvents
(
events
,
args
);
if
(
allEvents
)
triggerEvents
(
allEvents
,
arguments
);
return
this
;
},
// Tell this object to stop listening to either specific events ... or
if
(
listening
)
{
// to every object it's currently listening to.
var
listeners
=
obj
.
_listeners
||
(
obj
.
_listeners
=
{});
stopListening
:
function
(
obj
,
name
,
callback
)
{
listeners
[
listening
.
id
]
=
listening
;
var
listeningTo
=
this
.
_listeningTo
;
if
(
!
listeningTo
)
return
this
;
var
remove
=
!
name
&&
!
callback
;
if
(
!
callback
&&
typeof
name
===
'
object
'
)
callback
=
this
;
if
(
obj
)
(
listeningTo
=
{})[
obj
.
_listenId
]
=
obj
;
for
(
var
id
in
listeningTo
)
{
obj
=
listeningTo
[
id
];
obj
.
off
(
name
,
callback
,
this
);
if
(
remove
||
_
.
isEmpty
(
obj
.
_events
))
delete
this
.
_listeningTo
[
id
];
}
return
this
;
}
}
return
obj
;
};
};
// Regular expression used to split event strings.
// Inversion-of-control versions of `on`. Tell *this* object to listen to
var
eventSplitter
=
/
\s
+/
;
// an event in another object... keeping track of what it's listening to
// for easier unbinding later.
Events
.
listenTo
=
function
(
obj
,
name
,
callback
)
{
if
(
!
obj
)
return
this
;
var
id
=
obj
.
_listenId
||
(
obj
.
_listenId
=
_
.
uniqueId
(
'
l
'
));
var
listeningTo
=
this
.
_listeningTo
||
(
this
.
_listeningTo
=
{});
var
listening
=
listeningTo
[
id
];
// This object is not listening to any other events on `obj` yet.
// Setup the necessary references to track the listening callbacks.
if
(
!
listening
)
{
var
thisId
=
this
.
_listenId
||
(
this
.
_listenId
=
_
.
uniqueId
(
'
l
'
));
listening
=
listeningTo
[
id
]
=
{
obj
:
obj
,
objId
:
id
,
id
:
thisId
,
listeningTo
:
listeningTo
,
count
:
0
};
}
// Bind callbacks on obj, and keep track of them on listening.
internalOn
(
obj
,
name
,
callback
,
this
,
listening
);
return
this
;
};
// The reducing API that adds a callback to the `events` object.
var
onApi
=
function
(
events
,
name
,
callback
,
options
)
{
if
(
callback
)
{
var
handlers
=
events
[
name
]
||
(
events
[
name
]
=
[]);
var
context
=
options
.
context
,
ctx
=
options
.
ctx
,
listening
=
options
.
listening
;
if
(
listening
)
listening
.
count
++
;
handlers
.
push
({
callback
:
callback
,
context
:
context
,
ctx
:
context
||
ctx
,
listening
:
listening
});
}
return
events
;
};
// Remove one or many callbacks. If `context` is null, removes all
// callbacks with that function. If `callback` is null, removes all
// callbacks for the event. If `name` is null, removes all bound
// callbacks for all events.
Events
.
off
=
function
(
name
,
callback
,
context
)
{
if
(
!
this
.
_events
)
return
this
;
this
.
_events
=
eventsApi
(
offApi
,
this
.
_events
,
name
,
callback
,
{
context
:
context
,
listeners
:
this
.
_listeners
});
return
this
;
};
// Tell this object to stop listening to either specific events ... or
// to every object it's currently listening to.
Events
.
stopListening
=
function
(
obj
,
name
,
callback
)
{
var
listeningTo
=
this
.
_listeningTo
;
if
(
!
listeningTo
)
return
this
;
var
ids
=
obj
?
[
obj
.
_listenId
]
:
_
.
keys
(
listeningTo
);
for
(
var
i
=
0
;
i
<
ids
.
length
;
i
++
)
{
var
listening
=
listeningTo
[
ids
[
i
]];
// If listening doesn't exist, this object is not currently
// listening to obj. Break out early.
if
(
!
listening
)
break
;
listening
.
obj
.
off
(
name
,
callback
,
this
);
}
if
(
_
.
isEmpty
(
listeningTo
))
this
.
_listeningTo
=
void
0
;
return
this
;
};
// Implement fancy features of the Events API such as multiple event
// The reducing API that removes a callback from the `events` object.
// names `"change blur"` and jQuery-style event maps `{change: action}`
var
offApi
=
function
(
events
,
name
,
callback
,
options
)
{
// in terms of the existing API.
if
(
!
events
)
return
;
var
eventsApi
=
function
(
obj
,
action
,
name
,
rest
)
{
if
(
!
name
)
return
true
;
// Handle event maps.
var
i
=
0
,
listening
;
if
(
typeof
name
===
'
object
'
)
{
var
context
=
options
.
context
,
listeners
=
options
.
listeners
;
for
(
var
key
in
name
)
{
obj
[
action
].
apply
(
obj
,
[
key
,
name
[
key
]].
concat
(
rest
));
// Delete all events listeners and "drop" events.
if
(
!
name
&&
!
callback
&&
!
context
)
{
var
ids
=
_
.
keys
(
listeners
);
for
(;
i
<
ids
.
length
;
i
++
)
{
listening
=
listeners
[
ids
[
i
]];
delete
listeners
[
listening
.
id
];
delete
listening
.
listeningTo
[
listening
.
objId
];
}
}
return
false
;
return
;
}
}
// Handle space separated event names.
var
names
=
name
?
[
name
]
:
_
.
keys
(
events
);
if
(
eventSplitter
.
test
(
name
))
{
for
(;
i
<
names
.
length
;
i
++
)
{
var
names
=
name
.
split
(
eventSplitter
);
name
=
names
[
i
];
for
(
var
i
=
0
,
l
=
names
.
length
;
i
<
l
;
i
++
)
{
var
handlers
=
events
[
name
];
obj
[
action
].
apply
(
obj
,
[
names
[
i
]].
concat
(
rest
));
// Bail out if there are no events stored.
if
(
!
handlers
)
break
;
// Replace events if there are any remaining. Otherwise, clean up.
var
remaining
=
[];
for
(
var
j
=
0
;
j
<
handlers
.
length
;
j
++
)
{
var
handler
=
handlers
[
j
];
if
(
callback
&&
callback
!==
handler
.
callback
&&
callback
!==
handler
.
callback
.
_callback
||
context
&&
context
!==
handler
.
context
)
{
remaining
.
push
(
handler
);
}
else
{
listening
=
handler
.
listening
;
if
(
listening
&&
--
listening
.
count
===
0
)
{
delete
listeners
[
listening
.
id
];
delete
listening
.
listeningTo
[
listening
.
objId
];
}
}
}
// Update tail event if the list has any events. Otherwise, clean up.
if
(
remaining
.
length
)
{
events
[
name
]
=
remaining
;
}
else
{
delete
events
[
name
];
}
}
return
false
;
}
}
if
(
_
.
size
(
events
))
return
events
;
};
// Bind an event to only be triggered a single time. After the first time
// the callback is invoked, its listener will be removed. If multiple events
// are passed in using the space-separated syntax, the handler will fire
// once for each event, not once for a combination of all events.
Events
.
once
=
function
(
name
,
callback
,
context
)
{
// Map the event into a `{event: once}` object.
var
events
=
eventsApi
(
onceMap
,
{},
name
,
callback
,
_
.
bind
(
this
.
off
,
this
));
return
this
.
on
(
events
,
void
0
,
context
);
};
return
true
;
// Inversion-of-control versions of `once`.
Events
.
listenToOnce
=
function
(
obj
,
name
,
callback
)
{
// Map the event into a `{event: once}` object.
var
events
=
eventsApi
(
onceMap
,
{},
name
,
callback
,
_
.
bind
(
this
.
stopListening
,
this
,
obj
));
return
this
.
listenTo
(
obj
,
events
);
};
// Reduces the event callbacks into a map of `{event: onceWrapper}`.
// `offer` unbinds the `onceWrapper` after it has been called.
var
onceMap
=
function
(
map
,
name
,
callback
,
offer
)
{
if
(
callback
)
{
var
once
=
map
[
name
]
=
_
.
once
(
function
()
{
offer
(
name
,
once
);
callback
.
apply
(
this
,
arguments
);
});
once
.
_callback
=
callback
;
}
return
map
;
};
// Trigger one or many events, firing all bound callbacks. Callbacks are
// passed the same arguments as `trigger` is, apart from the event name
// (unless you're listening on `"all"`, which will cause your callback to
// receive the true name of the event as the first argument).
Events
.
trigger
=
function
(
name
)
{
if
(
!
this
.
_events
)
return
this
;
var
length
=
Math
.
max
(
0
,
arguments
.
length
-
1
);
var
args
=
Array
(
length
);
for
(
var
i
=
0
;
i
<
length
;
i
++
)
args
[
i
]
=
arguments
[
i
+
1
];
eventsApi
(
triggerApi
,
this
.
_events
,
name
,
void
0
,
args
);
return
this
;
};
// Handles triggering the appropriate event callbacks.
var
triggerApi
=
function
(
objEvents
,
name
,
cb
,
args
)
{
if
(
objEvents
)
{
var
events
=
objEvents
[
name
];
var
allEvents
=
objEvents
.
all
;
if
(
events
&&
allEvents
)
allEvents
=
allEvents
.
slice
();
if
(
events
)
triggerEvents
(
events
,
args
);
if
(
allEvents
)
triggerEvents
(
allEvents
,
[
name
].
concat
(
args
));
}
return
objEvents
;
};
};
// A difficult-to-believe, but optimized internal dispatch function for
// A difficult-to-believe, but optimized internal dispatch function for
...
@@ -211,22 +373,6 @@
...
@@ -211,22 +373,6 @@
}
}
};
};
var
listenMethods
=
{
listenTo
:
'
on
'
,
listenToOnce
:
'
once
'
};
// Inversion-of-control versions of `on` and `once`. Tell *this* object to
// listen to an event in another object ... keeping track of what it's
// listening to.
_
.
each
(
listenMethods
,
function
(
implementation
,
method
)
{
Events
[
method
]
=
function
(
obj
,
name
,
callback
)
{
var
listeningTo
=
this
.
_listeningTo
||
(
this
.
_listeningTo
=
{});
var
id
=
obj
.
_listenId
||
(
obj
.
_listenId
=
_
.
uniqueId
(
'
l
'
));
listeningTo
[
id
]
=
obj
;
if
(
!
callback
&&
typeof
name
===
'
object
'
)
callback
=
this
;
obj
[
implementation
](
name
,
callback
,
this
);
return
this
;
};
});
// Aliases for backwards compatibility.
// Aliases for backwards compatibility.
Events
.
bind
=
Events
.
on
;
Events
.
bind
=
Events
.
on
;
Events
.
unbind
=
Events
.
off
;
Events
.
unbind
=
Events
.
off
;
...
@@ -248,7 +394,7 @@
...
@@ -248,7 +394,7 @@
var
Model
=
Backbone
.
Model
=
function
(
attributes
,
options
)
{
var
Model
=
Backbone
.
Model
=
function
(
attributes
,
options
)
{
var
attrs
=
attributes
||
{};
var
attrs
=
attributes
||
{};
options
||
(
options
=
{});
options
||
(
options
=
{});
this
.
cid
=
_
.
uniqueId
(
'
c
'
);
this
.
cid
=
_
.
uniqueId
(
this
.
cidPrefix
);
this
.
attributes
=
{};
this
.
attributes
=
{};
if
(
options
.
collection
)
this
.
collection
=
options
.
collection
;
if
(
options
.
collection
)
this
.
collection
=
options
.
collection
;
if
(
options
.
parse
)
attrs
=
this
.
parse
(
attrs
,
options
)
||
{};
if
(
options
.
parse
)
attrs
=
this
.
parse
(
attrs
,
options
)
||
{};
...
@@ -271,6 +417,10 @@
...
@@ -271,6 +417,10 @@
// CouchDB users may want to set this to `"_id"`.
// CouchDB users may want to set this to `"_id"`.
idAttribute
:
'
id
'
,
idAttribute
:
'
id
'
,
// The prefix is used to create the client id which is used to identify models locally.
// You may want to override this if you're experiencing name clashes with model ids.
cidPrefix
:
'
c
'
,
// Initialize is an empty function by default. Override it with your own
// Initialize is an empty function by default. Override it with your own
// initialization logic.
// initialization logic.
initialize
:
function
(){},
initialize
:
function
(){},
...
@@ -302,14 +452,19 @@
...
@@ -302,14 +452,19 @@
return
this
.
get
(
attr
)
!=
null
;
return
this
.
get
(
attr
)
!=
null
;
},
},
// Special-cased proxy to underscore's `_.matches` method.
matches
:
function
(
attrs
)
{
return
!!
_
.
iteratee
(
attrs
,
this
)(
this
.
attributes
);
},
// Set a hash of model attributes on the object, firing `"change"`. This is
// Set a hash of model attributes on the object, firing `"change"`. This is
// the core primitive operation of a model, updating the data and notifying
// the core primitive operation of a model, updating the data and notifying
// anyone who needs to know about the change in state. The heart of the beast.
// anyone who needs to know about the change in state. The heart of the beast.
set
:
function
(
key
,
val
,
options
)
{
set
:
function
(
key
,
val
,
options
)
{
var
attr
,
attrs
,
unset
,
changes
,
silent
,
changing
,
prev
,
current
;
if
(
key
==
null
)
return
this
;
if
(
key
==
null
)
return
this
;
// Handle both `"key", value` and `{key: value}` -style arguments.
// Handle both `"key", value` and `{key: value}` -style arguments.
var
attrs
;
if
(
typeof
key
===
'
object
'
)
{
if
(
typeof
key
===
'
object
'
)
{
attrs
=
key
;
attrs
=
key
;
options
=
val
;
options
=
val
;
...
@@ -323,37 +478,40 @@
...
@@ -323,37 +478,40 @@
if
(
!
this
.
_validate
(
attrs
,
options
))
return
false
;
if
(
!
this
.
_validate
(
attrs
,
options
))
return
false
;
// Extract attributes and options.
// Extract attributes and options.
unset
=
options
.
unset
;
var
unset
=
options
.
unset
;
silent
=
options
.
silent
;
var
silent
=
options
.
silent
;
changes
=
[];
var
changes
=
[];
changing
=
this
.
_changing
;
var
changing
=
this
.
_changing
;
this
.
_changing
=
true
;
this
.
_changing
=
true
;
if
(
!
changing
)
{
if
(
!
changing
)
{
this
.
_previousAttributes
=
_
.
clone
(
this
.
attributes
);
this
.
_previousAttributes
=
_
.
clone
(
this
.
attributes
);
this
.
changed
=
{};
this
.
changed
=
{};
}
}
current
=
this
.
attributes
,
prev
=
this
.
_previousAttributes
;
// Check for changes of `id`.
var
current
=
this
.
attributes
;
if
(
this
.
idAttribute
in
attrs
)
this
.
id
=
attrs
[
this
.
idAttribute
];
var
changed
=
this
.
changed
;
var
prev
=
this
.
_previousAttributes
;
// For each `set` attribute, update or delete the current value.
// For each `set` attribute, update or delete the current value.
for
(
attr
in
attrs
)
{
for
(
var
attr
in
attrs
)
{
val
=
attrs
[
attr
];
val
=
attrs
[
attr
];
if
(
!
_
.
isEqual
(
current
[
attr
],
val
))
changes
.
push
(
attr
);
if
(
!
_
.
isEqual
(
current
[
attr
],
val
))
changes
.
push
(
attr
);
if
(
!
_
.
isEqual
(
prev
[
attr
],
val
))
{
if
(
!
_
.
isEqual
(
prev
[
attr
],
val
))
{
this
.
changed
[
attr
]
=
val
;
changed
[
attr
]
=
val
;
}
else
{
}
else
{
delete
this
.
changed
[
attr
];
delete
changed
[
attr
];
}
}
unset
?
delete
current
[
attr
]
:
current
[
attr
]
=
val
;
unset
?
delete
current
[
attr
]
:
current
[
attr
]
=
val
;
}
}
// Update the `id`.
this
.
id
=
this
.
get
(
this
.
idAttribute
);
// Trigger all relevant attribute changes.
// Trigger all relevant attribute changes.
if
(
!
silent
)
{
if
(
!
silent
)
{
if
(
changes
.
length
)
this
.
_pending
=
options
;
if
(
changes
.
length
)
this
.
_pending
=
options
;
for
(
var
i
=
0
,
l
=
changes
.
length
;
i
<
l
;
i
++
)
{
for
(
var
i
=
0
;
i
<
changes
.
length
;
i
++
)
{
this
.
trigger
(
'
change:
'
+
changes
[
i
],
this
,
current
[
changes
[
i
]],
options
);
this
.
trigger
(
'
change:
'
+
changes
[
i
],
this
,
current
[
changes
[
i
]],
options
);
}
}
}
}
...
@@ -401,13 +559,14 @@
...
@@ -401,13 +559,14 @@
// determining if there *would be* a change.
// determining if there *would be* a change.
changedAttributes
:
function
(
diff
)
{
changedAttributes
:
function
(
diff
)
{
if
(
!
diff
)
return
this
.
hasChanged
()
?
_
.
clone
(
this
.
changed
)
:
false
;
if
(
!
diff
)
return
this
.
hasChanged
()
?
_
.
clone
(
this
.
changed
)
:
false
;
var
val
,
changed
=
false
;
var
old
=
this
.
_changing
?
this
.
_previousAttributes
:
this
.
attributes
;
var
old
=
this
.
_changing
?
this
.
_previousAttributes
:
this
.
attributes
;
var
changed
=
{};
for
(
var
attr
in
diff
)
{
for
(
var
attr
in
diff
)
{
if
(
_
.
isEqual
(
old
[
attr
],
(
val
=
diff
[
attr
])))
continue
;
var
val
=
diff
[
attr
];
(
changed
||
(
changed
=
{}))[
attr
]
=
val
;
if
(
_
.
isEqual
(
old
[
attr
],
val
))
continue
;
changed
[
attr
]
=
val
;
}
}
return
changed
;
return
_
.
size
(
changed
)
?
changed
:
false
;
},
},
// Get the previous value of an attribute, recorded at the time the last
// Get the previous value of an attribute, recorded at the time the last
...
@@ -423,17 +582,16 @@
...
@@ -423,17 +582,16 @@
return
_
.
clone
(
this
.
_previousAttributes
);
return
_
.
clone
(
this
.
_previousAttributes
);
},
},
// Fetch the model from the server. If the server's representation of the
// Fetch the model from the server, merging the response with the model's
// model differs from its current attributes, they will be overridden,
// local attributes. Any changed attributes will trigger a "change" event.
// triggering a `"change"` event.
fetch
:
function
(
options
)
{
fetch
:
function
(
options
)
{
options
=
options
?
_
.
clone
(
options
)
:
{};
options
=
_
.
extend
({
parse
:
true
},
options
);
if
(
options
.
parse
===
void
0
)
options
.
parse
=
true
;
var
model
=
this
;
var
model
=
this
;
var
success
=
options
.
success
;
var
success
=
options
.
success
;
options
.
success
=
function
(
resp
)
{
options
.
success
=
function
(
resp
)
{
if
(
!
model
.
set
(
model
.
parse
(
resp
,
options
),
options
))
return
false
;
var
serverAttrs
=
options
.
parse
?
model
.
parse
(
resp
,
options
)
:
resp
;
if
(
success
)
success
(
model
,
resp
,
options
);
if
(
!
model
.
set
(
serverAttrs
,
options
))
return
false
;
if
(
success
)
success
.
call
(
options
.
context
,
model
,
resp
,
options
);
model
.
trigger
(
'
sync
'
,
model
,
resp
,
options
);
model
.
trigger
(
'
sync
'
,
model
,
resp
,
options
);
};
};
wrapError
(
this
,
options
);
wrapError
(
this
,
options
);
...
@@ -444,9 +602,8 @@
...
@@ -444,9 +602,8 @@
// If the server returns an attributes hash that differs, the model's
// If the server returns an attributes hash that differs, the model's
// state will be `set` again.
// state will be `set` again.
save
:
function
(
key
,
val
,
options
)
{
save
:
function
(
key
,
val
,
options
)
{
var
attrs
,
method
,
xhr
,
attributes
=
this
.
attributes
;
// Handle both `"key", value` and `{key: value}` -style arguments.
// Handle both `"key", value` and `{key: value}` -style arguments.
var
attrs
;
if
(
key
==
null
||
typeof
key
===
'
object
'
)
{
if
(
key
==
null
||
typeof
key
===
'
object
'
)
{
attrs
=
key
;
attrs
=
key
;
options
=
val
;
options
=
val
;
...
@@ -454,46 +611,43 @@
...
@@ -454,46 +611,43 @@
(
attrs
=
{})[
key
]
=
val
;
(
attrs
=
{})[
key
]
=
val
;
}
}
options
=
_
.
extend
({
validate
:
true
},
options
);
options
=
_
.
extend
({
validate
:
true
,
parse
:
true
},
options
);
var
wait
=
options
.
wait
;
// If we're not waiting and attributes exist, save acts as
// If we're not waiting and attributes exist, save acts as
// `set(attr).save(null, opts)` with validation. Otherwise, check if
// `set(attr).save(null, opts)` with validation. Otherwise, check if
// the model will be valid when the attributes, if any, are set.
// the model will be valid when the attributes, if any, are set.
if
(
attrs
&&
!
options
.
wait
)
{
if
(
attrs
&&
!
wait
)
{
if
(
!
this
.
set
(
attrs
,
options
))
return
false
;
if
(
!
this
.
set
(
attrs
,
options
))
return
false
;
}
else
{
}
else
{
if
(
!
this
.
_validate
(
attrs
,
options
))
return
false
;
if
(
!
this
.
_validate
(
attrs
,
options
))
return
false
;
}
}
// Set temporary attributes if `{wait: true}`.
if
(
attrs
&&
options
.
wait
)
{
this
.
attributes
=
_
.
extend
({},
attributes
,
attrs
);
}
// After a successful server-side save, the client is (optionally)
// After a successful server-side save, the client is (optionally)
// updated with the server-side state.
// updated with the server-side state.
if
(
options
.
parse
===
void
0
)
options
.
parse
=
true
;
var
model
=
this
;
var
model
=
this
;
var
success
=
options
.
success
;
var
success
=
options
.
success
;
var
attributes
=
this
.
attributes
;
options
.
success
=
function
(
resp
)
{
options
.
success
=
function
(
resp
)
{
// Ensure attributes are restored during synchronous saves.
// Ensure attributes are restored during synchronous saves.
model
.
attributes
=
attributes
;
model
.
attributes
=
attributes
;
var
serverAttrs
=
model
.
parse
(
resp
,
options
);
var
serverAttrs
=
options
.
parse
?
model
.
parse
(
resp
,
options
)
:
resp
;
if
(
options
.
wait
)
serverAttrs
=
_
.
extend
(
attrs
||
{},
serverAttrs
);
if
(
wait
)
serverAttrs
=
_
.
extend
({},
attrs
,
serverAttrs
);
if
(
_
.
isObject
(
serverAttrs
)
&&
!
model
.
set
(
serverAttrs
,
options
))
{
if
(
serverAttrs
&&
!
model
.
set
(
serverAttrs
,
options
))
return
false
;
return
false
;
if
(
success
)
success
.
call
(
options
.
context
,
model
,
resp
,
options
);
}
if
(
success
)
success
(
model
,
resp
,
options
);
model
.
trigger
(
'
sync
'
,
model
,
resp
,
options
);
model
.
trigger
(
'
sync
'
,
model
,
resp
,
options
);
};
};
wrapError
(
this
,
options
);
wrapError
(
this
,
options
);
method
=
this
.
isNew
()
?
'
create
'
:
(
options
.
patch
?
'
patch
'
:
'
update
'
);
// Set temporary attributes if `{wait: true}` to properly find new ids.
if
(
method
===
'
patch
'
)
options
.
attrs
=
attrs
;
if
(
attrs
&&
wait
)
this
.
attributes
=
_
.
extend
({},
attributes
,
attrs
);
xhr
=
this
.
sync
(
method
,
this
,
options
);
var
method
=
this
.
isNew
()
?
'
create
'
:
(
options
.
patch
?
'
patch
'
:
'
update
'
);
if
(
method
===
'
patch
'
&&
!
options
.
attrs
)
options
.
attrs
=
attrs
;
var
xhr
=
this
.
sync
(
method
,
this
,
options
);
// Restore attributes.
// Restore attributes.
if
(
attrs
&&
options
.
wait
)
this
.
attributes
=
attributes
;
this
.
attributes
=
attributes
;
return
xhr
;
return
xhr
;
},
},
...
@@ -505,25 +659,27 @@
...
@@ -505,25 +659,27 @@
options
=
options
?
_
.
clone
(
options
)
:
{};
options
=
options
?
_
.
clone
(
options
)
:
{};
var
model
=
this
;
var
model
=
this
;
var
success
=
options
.
success
;
var
success
=
options
.
success
;
var
wait
=
options
.
wait
;
var
destroy
=
function
()
{
var
destroy
=
function
()
{
model
.
stopListening
();
model
.
trigger
(
'
destroy
'
,
model
,
model
.
collection
,
options
);
model
.
trigger
(
'
destroy
'
,
model
,
model
.
collection
,
options
);
};
};
options
.
success
=
function
(
resp
)
{
options
.
success
=
function
(
resp
)
{
if
(
options
.
wait
||
model
.
isNew
()
)
destroy
();
if
(
wait
)
destroy
();
if
(
success
)
success
(
model
,
resp
,
options
);
if
(
success
)
success
.
call
(
options
.
context
,
model
,
resp
,
options
);
if
(
!
model
.
isNew
())
model
.
trigger
(
'
sync
'
,
model
,
resp
,
options
);
if
(
!
model
.
isNew
())
model
.
trigger
(
'
sync
'
,
model
,
resp
,
options
);
};
};
var
xhr
=
false
;
if
(
this
.
isNew
())
{
if
(
this
.
isNew
())
{
options
.
success
();
_
.
defer
(
options
.
success
);
return
false
;
}
else
{
wrapError
(
this
,
options
);
xhr
=
this
.
sync
(
'
delete
'
,
this
,
options
);
}
}
wrapError
(
this
,
options
);
if
(
!
wait
)
destroy
();
var
xhr
=
this
.
sync
(
'
delete
'
,
this
,
options
);
if
(
!
options
.
wait
)
destroy
();
return
xhr
;
return
xhr
;
},
},
...
@@ -536,7 +692,8 @@
...
@@ -536,7 +692,8 @@
_
.
result
(
this
.
collection
,
'
url
'
)
||
_
.
result
(
this
.
collection
,
'
url
'
)
||
urlError
();
urlError
();
if
(
this
.
isNew
())
return
base
;
if
(
this
.
isNew
())
return
base
;
return
base
.
replace
(
/
([^\/])
$/
,
'
$1/
'
)
+
encodeURIComponent
(
this
.
id
);
var
id
=
this
.
get
(
this
.
idAttribute
);
return
base
.
replace
(
/
[^\/]
$/
,
'
$&/
'
)
+
encodeURIComponent
(
id
);
},
},
// **parse** converts a response into the hash of attributes to be `set` on
// **parse** converts a response into the hash of attributes to be `set` on
...
@@ -557,7 +714,7 @@
...
@@ -557,7 +714,7 @@
// Check if the model is currently in a valid state.
// Check if the model is currently in a valid state.
isValid
:
function
(
options
)
{
isValid
:
function
(
options
)
{
return
this
.
_validate
({},
_
.
extend
(
options
||
{},
{
validate
:
true
}
));
return
this
.
_validate
({},
_
.
defaults
({
validate
:
true
},
options
));
},
},
// Run validation against the next complete set of model attributes,
// Run validation against the next complete set of model attributes,
...
@@ -573,23 +730,19 @@
...
@@ -573,23 +730,19 @@
});
});
// Underscore methods that we want to implement on the Model.
// Underscore methods that we want to implement on the Model, mapped to the
var
modelMethods
=
[
'
keys
'
,
'
values
'
,
'
pairs
'
,
'
invert
'
,
'
pick
'
,
'
omit
'
];
// number of arguments they take.
var
modelMethods
=
{
keys
:
1
,
values
:
1
,
pairs
:
1
,
invert
:
1
,
pick
:
0
,
omit
:
0
,
chain
:
1
,
isEmpty
:
1
};
// Mix in each Underscore method as a proxy to `Model#attributes`.
// Mix in each Underscore method as a proxy to `Model#attributes`.
_
.
each
(
modelMethods
,
function
(
method
)
{
addUnderscoreMethods
(
Model
,
modelMethods
,
'
attributes
'
);
Model
.
prototype
[
method
]
=
function
()
{
var
args
=
slice
.
call
(
arguments
);
args
.
unshift
(
this
.
attributes
);
return
_
[
method
].
apply
(
_
,
args
);
};
});
// Backbone.Collection
// Backbone.Collection
// -------------------
// -------------------
// If models tend to represent a single row of data, a Backbone Collection is
// If models tend to represent a single row of data, a Backbone Collection is
// more anal
a
gous to a table full of data ... or a small slice or page of that
// more anal
o
gous to a table full of data ... or a small slice or page of that
// table, or a collection of rows that belong together for a particular reason
// table, or a collection of rows that belong together for a particular reason
// -- all of the messages in this particular folder, all of the documents
// -- all of the messages in this particular folder, all of the documents
// belonging to this particular author, and so on. Collections maintain
// belonging to this particular author, and so on. Collections maintain
...
@@ -611,6 +764,15 @@
...
@@ -611,6 +764,15 @@
var
setOptions
=
{
add
:
true
,
remove
:
true
,
merge
:
true
};
var
setOptions
=
{
add
:
true
,
remove
:
true
,
merge
:
true
};
var
addOptions
=
{
add
:
true
,
remove
:
false
};
var
addOptions
=
{
add
:
true
,
remove
:
false
};
// Splices `insert` into `array` at index `at`.
var
splice
=
function
(
array
,
insert
,
at
)
{
var
tail
=
Array
(
array
.
length
-
at
);
var
length
=
insert
.
length
;
for
(
var
i
=
0
;
i
<
tail
.
length
;
i
++
)
tail
[
i
]
=
array
[
i
+
at
];
for
(
i
=
0
;
i
<
length
;
i
++
)
array
[
i
+
at
]
=
insert
[
i
];
for
(
i
=
0
;
i
<
tail
.
length
;
i
++
)
array
[
i
+
length
+
at
]
=
tail
[
i
];
};
// Define the Collection's inheritable methods.
// Define the Collection's inheritable methods.
_
.
extend
(
Collection
.
prototype
,
Events
,
{
_
.
extend
(
Collection
.
prototype
,
Events
,
{
...
@@ -625,7 +787,7 @@
...
@@ -625,7 +787,7 @@
// The JSON representation of a Collection is an array of the
// The JSON representation of a Collection is an array of the
// models' attributes.
// models' attributes.
toJSON
:
function
(
options
)
{
toJSON
:
function
(
options
)
{
return
this
.
map
(
function
(
model
){
return
model
.
toJSON
(
options
);
});
return
this
.
map
(
function
(
model
)
{
return
model
.
toJSON
(
options
);
});
},
},
// Proxy `Backbone.sync` by default.
// Proxy `Backbone.sync` by default.
...
@@ -633,32 +795,21 @@
...
@@ -633,32 +795,21 @@
return
Backbone
.
sync
.
apply
(
this
,
arguments
);
return
Backbone
.
sync
.
apply
(
this
,
arguments
);
},
},
// Add a model, or list of models to the set.
// Add a model, or list of models to the set. `models` may be Backbone
// Models or raw JavaScript objects to be converted to Models, or any
// combination of the two.
add
:
function
(
models
,
options
)
{
add
:
function
(
models
,
options
)
{
return
this
.
set
(
models
,
_
.
extend
({
merge
:
false
},
options
,
addOptions
));
return
this
.
set
(
models
,
_
.
extend
({
merge
:
false
},
options
,
addOptions
));
},
},
// Remove a model, or a list of models from the set.
// Remove a model, or a list of models from the set.
remove
:
function
(
models
,
options
)
{
remove
:
function
(
models
,
options
)
{
options
=
_
.
extend
({},
options
);
var
singular
=
!
_
.
isArray
(
models
);
var
singular
=
!
_
.
isArray
(
models
);
models
=
singular
?
[
models
]
:
_
.
clone
(
models
);
models
=
singular
?
[
models
]
:
_
.
clone
(
models
);
options
||
(
options
=
{});
var
removed
=
this
.
_removeModels
(
models
,
options
);
var
i
,
l
,
index
,
model
;
if
(
!
options
.
silent
&&
removed
)
this
.
trigger
(
'
update
'
,
this
,
options
);
for
(
i
=
0
,
l
=
models
.
length
;
i
<
l
;
i
++
)
{
return
singular
?
removed
[
0
]
:
removed
;
model
=
models
[
i
]
=
this
.
get
(
models
[
i
]);
if
(
!
model
)
continue
;
delete
this
.
_byId
[
model
.
id
];
delete
this
.
_byId
[
model
.
cid
];
index
=
this
.
indexOf
(
model
);
this
.
models
.
splice
(
index
,
1
);
this
.
length
--
;
if
(
!
options
.
silent
)
{
options
.
index
=
index
;
model
.
trigger
(
'
remove
'
,
model
,
this
,
options
);
}
this
.
_removeReference
(
model
,
options
);
}
return
singular
?
models
[
0
]
:
models
;
},
},
// Update a collection by `set`-ing a new list of models, adding new ones,
// Update a collection by `set`-ing a new list of models, adding new ones,
...
@@ -666,78 +817,88 @@
...
@@ -666,78 +817,88 @@
// already exist in the collection, as necessary. Similar to **Model#set**,
// already exist in the collection, as necessary. Similar to **Model#set**,
// the core operation for updating the data contained by the collection.
// the core operation for updating the data contained by the collection.
set
:
function
(
models
,
options
)
{
set
:
function
(
models
,
options
)
{
if
(
models
==
null
)
return
;
options
=
_
.
defaults
({},
options
,
setOptions
);
options
=
_
.
defaults
({},
options
,
setOptions
);
if
(
options
.
parse
)
models
=
this
.
parse
(
models
,
options
);
if
(
options
.
parse
&&
!
this
.
_isModel
(
models
))
models
=
this
.
parse
(
models
,
options
);
var
singular
=
!
_
.
isArray
(
models
);
var
singular
=
!
_
.
isArray
(
models
);
models
=
singular
?
(
models
?
[
models
]
:
[])
:
_
.
clone
(
models
);
models
=
singular
?
[
models
]
:
models
.
slice
(
);
var
i
,
l
,
id
,
model
,
attrs
,
existing
,
sort
;
var
at
=
options
.
at
;
var
at
=
options
.
at
;
var
targetModel
=
this
.
model
;
if
(
at
!=
null
)
at
=
+
at
;
if
(
at
<
0
)
at
+=
this
.
length
+
1
;
var
set
=
[];
var
toAdd
=
[];
var
toRemove
=
[];
var
modelMap
=
{};
var
add
=
options
.
add
;
var
merge
=
options
.
merge
;
var
remove
=
options
.
remove
;
var
sort
=
false
;
var
sortable
=
this
.
comparator
&&
(
at
==
null
)
&&
options
.
sort
!==
false
;
var
sortable
=
this
.
comparator
&&
(
at
==
null
)
&&
options
.
sort
!==
false
;
var
sortAttr
=
_
.
isString
(
this
.
comparator
)
?
this
.
comparator
:
null
;
var
sortAttr
=
_
.
isString
(
this
.
comparator
)
?
this
.
comparator
:
null
;
var
toAdd
=
[],
toRemove
=
[],
modelMap
=
{};
var
add
=
options
.
add
,
merge
=
options
.
merge
,
remove
=
options
.
remove
;
var
order
=
!
sortable
&&
add
&&
remove
?
[]
:
false
;
// Turn bare objects into model references, and prevent invalid models
// Turn bare objects into model references, and prevent invalid models
// from being added.
// from being added.
for
(
i
=
0
,
l
=
models
.
length
;
i
<
l
;
i
++
)
{
var
model
;
attrs
=
models
[
i
]
||
{};
for
(
var
i
=
0
;
i
<
models
.
length
;
i
++
)
{
if
(
attrs
instanceof
Model
)
{
model
=
models
[
i
];
id
=
model
=
attrs
;
}
else
{
id
=
attrs
[
targetModel
.
prototype
.
idAttribute
||
'
id
'
];
}
// If a duplicate is found, prevent it from being added and
// If a duplicate is found, prevent it from being added and
// optionally merge it into the existing model.
// optionally merge it into the existing model.
if
(
existing
=
this
.
get
(
id
))
{
var
existing
=
this
.
get
(
model
);
if
(
remove
)
modelMap
[
existing
.
cid
]
=
true
;
if
(
existing
)
{
if
(
merge
)
{
if
(
merge
&&
model
!==
existing
)
{
attrs
=
attrs
===
model
?
model
.
attributes
:
attrs
;
var
attrs
=
this
.
_isModel
(
model
)
?
model
.
attributes
:
model
;
if
(
options
.
parse
)
attrs
=
existing
.
parse
(
attrs
,
options
);
if
(
options
.
parse
)
attrs
=
existing
.
parse
(
attrs
,
options
);
existing
.
set
(
attrs
,
options
);
existing
.
set
(
attrs
,
options
);
if
(
sortable
&&
!
sort
&&
existing
.
hasChanged
(
sortAttr
))
sort
=
true
;
if
(
sortable
&&
!
sort
)
sort
=
existing
.
hasChanged
(
sortAttr
);
}
if
(
!
modelMap
[
existing
.
cid
])
{
modelMap
[
existing
.
cid
]
=
true
;
set
.
push
(
existing
);
}
}
models
[
i
]
=
existing
;
models
[
i
]
=
existing
;
// If this is a new, valid model, push it to the `toAdd` list.
// If this is a new, valid model, push it to the `toAdd` list.
}
else
if
(
add
)
{
}
else
if
(
add
)
{
model
=
models
[
i
]
=
this
.
_prepareModel
(
attrs
,
options
);
model
=
models
[
i
]
=
this
.
_prepareModel
(
model
,
options
);
if
(
!
model
)
continue
;
if
(
model
)
{
toAdd
.
push
(
model
);
toAdd
.
push
(
model
);
this
.
_addReference
(
model
,
options
);
this
.
_addReference
(
model
,
options
);
modelMap
[
model
.
cid
]
=
true
;
set
.
push
(
model
);
}
}
}
// Do not add multiple models with the same `id`.
model
=
existing
||
model
;
if
(
order
&&
(
model
.
isNew
()
||
!
modelMap
[
model
.
id
]))
order
.
push
(
model
);
modelMap
[
model
.
id
]
=
true
;
}
}
// Remove
nonexistent models if appropriate
.
// Remove
stale models
.
if
(
remove
)
{
if
(
remove
)
{
for
(
i
=
0
,
l
=
this
.
length
;
i
<
l
;
++
i
)
{
for
(
i
=
0
;
i
<
this
.
length
;
i
++
)
{
if
(
!
modelMap
[(
model
=
this
.
models
[
i
]).
cid
])
toRemove
.
push
(
model
);
model
=
this
.
models
[
i
];
if
(
!
modelMap
[
model
.
cid
])
toRemove
.
push
(
model
);
}
}
if
(
toRemove
.
length
)
this
.
remove
(
toRemove
,
options
);
if
(
toRemove
.
length
)
this
.
_removeModels
(
toRemove
,
options
);
}
}
// See if sorting is needed, update `length` and splice in new models.
// See if sorting is needed, update `length` and splice in new models.
if
(
toAdd
.
length
||
(
order
&&
order
.
length
))
{
var
orderChanged
=
false
;
var
replace
=
!
sortable
&&
add
&&
remove
;
if
(
set
.
length
&&
replace
)
{
orderChanged
=
this
.
length
!=
set
.
length
||
_
.
some
(
this
.
models
,
function
(
model
,
index
)
{
return
model
!==
set
[
index
];
});
this
.
models
.
length
=
0
;
splice
(
this
.
models
,
set
,
0
);
this
.
length
=
this
.
models
.
length
;
}
else
if
(
toAdd
.
length
)
{
if
(
sortable
)
sort
=
true
;
if
(
sortable
)
sort
=
true
;
this
.
length
+=
toAdd
.
length
;
splice
(
this
.
models
,
toAdd
,
at
==
null
?
this
.
length
:
at
);
if
(
at
!=
null
)
{
this
.
length
=
this
.
models
.
length
;
for
(
i
=
0
,
l
=
toAdd
.
length
;
i
<
l
;
i
++
)
{
this
.
models
.
splice
(
at
+
i
,
0
,
toAdd
[
i
]);
}
}
else
{
if
(
order
)
this
.
models
.
length
=
0
;
var
orderedModels
=
order
||
toAdd
;
for
(
i
=
0
,
l
=
orderedModels
.
length
;
i
<
l
;
i
++
)
{
this
.
models
.
push
(
orderedModels
[
i
]);
}
}
}
}
// Silently sort the collection if appropriate.
// Silently sort the collection if appropriate.
...
@@ -745,10 +906,13 @@
...
@@ -745,10 +906,13 @@
// Unless silenced, it's time to fire all appropriate add/sort events.
// Unless silenced, it's time to fire all appropriate add/sort events.
if
(
!
options
.
silent
)
{
if
(
!
options
.
silent
)
{
for
(
i
=
0
,
l
=
toAdd
.
length
;
i
<
l
;
i
++
)
{
for
(
i
=
0
;
i
<
toAdd
.
length
;
i
++
)
{
(
model
=
toAdd
[
i
]).
trigger
(
'
add
'
,
model
,
this
,
options
);
if
(
at
!=
null
)
options
.
index
=
at
+
i
;
model
=
toAdd
[
i
];
model
.
trigger
(
'
add
'
,
model
,
this
,
options
);
}
}
if
(
sort
||
(
order
&&
order
.
length
))
this
.
trigger
(
'
sort
'
,
this
,
options
);
if
(
sort
||
orderChanged
)
this
.
trigger
(
'
sort
'
,
this
,
options
);
if
(
toAdd
.
length
||
toRemove
.
length
)
this
.
trigger
(
'
update
'
,
this
,
options
);
}
}
// Return the added (or merged) model (or models).
// Return the added (or merged) model (or models).
...
@@ -760,8 +924,8 @@
...
@@ -760,8 +924,8 @@
// any granular `add` or `remove` events. Fires `reset` when finished.
// any granular `add` or `remove` events. Fires `reset` when finished.
// Useful for bulk operations and optimizations.
// Useful for bulk operations and optimizations.
reset
:
function
(
models
,
options
)
{
reset
:
function
(
models
,
options
)
{
options
||
(
options
=
{})
;
options
=
options
?
_
.
clone
(
options
)
:
{}
;
for
(
var
i
=
0
,
l
=
this
.
models
.
length
;
i
<
l
;
i
++
)
{
for
(
var
i
=
0
;
i
<
this
.
models
.
length
;
i
++
)
{
this
.
_removeReference
(
this
.
models
[
i
],
options
);
this
.
_removeReference
(
this
.
models
[
i
],
options
);
}
}
options
.
previousModels
=
this
.
models
;
options
.
previousModels
=
this
.
models
;
...
@@ -779,8 +943,7 @@
...
@@ -779,8 +943,7 @@
// Remove a model from the end of the collection.
// Remove a model from the end of the collection.
pop
:
function
(
options
)
{
pop
:
function
(
options
)
{
var
model
=
this
.
at
(
this
.
length
-
1
);
var
model
=
this
.
at
(
this
.
length
-
1
);
this
.
remove
(
model
,
options
);
return
this
.
remove
(
model
,
options
);
return
model
;
},
},
// Add a model to the beginning of the collection.
// Add a model to the beginning of the collection.
...
@@ -791,8 +954,7 @@
...
@@ -791,8 +954,7 @@
// Remove a model from the beginning of the collection.
// Remove a model from the beginning of the collection.
shift
:
function
(
options
)
{
shift
:
function
(
options
)
{
var
model
=
this
.
at
(
0
);
var
model
=
this
.
at
(
0
);
this
.
remove
(
model
,
options
);
return
this
.
remove
(
model
,
options
);
return
model
;
},
},
// Slice out a sub-array of models from the collection.
// Slice out a sub-array of models from the collection.
...
@@ -803,24 +965,20 @@
...
@@ -803,24 +965,20 @@
// Get a model from the set by id.
// Get a model from the set by id.
get
:
function
(
obj
)
{
get
:
function
(
obj
)
{
if
(
obj
==
null
)
return
void
0
;
if
(
obj
==
null
)
return
void
0
;
return
this
.
_byId
[
obj
]
||
this
.
_byId
[
obj
.
id
]
||
this
.
_byId
[
obj
.
cid
];
var
id
=
this
.
modelId
(
this
.
_isModel
(
obj
)
?
obj
.
attributes
:
obj
);
return
this
.
_byId
[
obj
]
||
this
.
_byId
[
id
]
||
this
.
_byId
[
obj
.
cid
];
},
},
// Get the model at the given index.
// Get the model at the given index.
at
:
function
(
index
)
{
at
:
function
(
index
)
{
if
(
index
<
0
)
index
+=
this
.
length
;
return
this
.
models
[
index
];
return
this
.
models
[
index
];
},
},
// Return models with matching attributes. Useful for simple cases of
// Return models with matching attributes. Useful for simple cases of
// `filter`.
// `filter`.
where
:
function
(
attrs
,
first
)
{
where
:
function
(
attrs
,
first
)
{
if
(
_
.
isEmpty
(
attrs
))
return
first
?
void
0
:
[];
return
this
[
first
?
'
find
'
:
'
filter
'
](
attrs
);
return
this
[
first
?
'
find
'
:
'
filter
'
](
function
(
model
)
{
for
(
var
key
in
attrs
)
{
if
(
attrs
[
key
]
!==
model
.
get
(
key
))
return
false
;
}
return
true
;
});
},
},
// Return the first model with matching attributes. Useful for simple cases
// Return the first model with matching attributes. Useful for simple cases
...
@@ -833,16 +991,19 @@
...
@@ -833,16 +991,19 @@
// normal circumstances, as the set will maintain sort order as each item
// normal circumstances, as the set will maintain sort order as each item
// is added.
// is added.
sort
:
function
(
options
)
{
sort
:
function
(
options
)
{
if
(
!
this
.
comparator
)
throw
new
Error
(
'
Cannot sort a set without a comparator
'
);
var
comparator
=
this
.
comparator
;
if
(
!
comparator
)
throw
new
Error
(
'
Cannot sort a set without a comparator
'
);
options
||
(
options
=
{});
options
||
(
options
=
{});
var
length
=
comparator
.
length
;
if
(
_
.
isFunction
(
comparator
))
comparator
=
_
.
bind
(
comparator
,
this
);
// Run sort based on type of `comparator`.
// Run sort based on type of `comparator`.
if
(
_
.
isString
(
this
.
comparator
)
||
this
.
comparator
.
length
===
1
)
{
if
(
length
===
1
||
_
.
isString
(
comparator
)
)
{
this
.
models
=
this
.
sortBy
(
this
.
comparator
,
this
);
this
.
models
=
this
.
sortBy
(
comparator
);
}
else
{
}
else
{
this
.
models
.
sort
(
_
.
bind
(
this
.
comparator
,
this
)
);
this
.
models
.
sort
(
comparator
);
}
}
if
(
!
options
.
silent
)
this
.
trigger
(
'
sort
'
,
this
,
options
);
if
(
!
options
.
silent
)
this
.
trigger
(
'
sort
'
,
this
,
options
);
return
this
;
return
this
;
},
},
...
@@ -856,14 +1017,13 @@
...
@@ -856,14 +1017,13 @@
// collection when they arrive. If `reset: true` is passed, the response
// collection when they arrive. If `reset: true` is passed, the response
// data will be passed through the `reset` method instead of `set`.
// data will be passed through the `reset` method instead of `set`.
fetch
:
function
(
options
)
{
fetch
:
function
(
options
)
{
options
=
options
?
_
.
clone
(
options
)
:
{};
options
=
_
.
extend
({
parse
:
true
},
options
);
if
(
options
.
parse
===
void
0
)
options
.
parse
=
true
;
var
success
=
options
.
success
;
var
success
=
options
.
success
;
var
collection
=
this
;
var
collection
=
this
;
options
.
success
=
function
(
resp
)
{
options
.
success
=
function
(
resp
)
{
var
method
=
options
.
reset
?
'
reset
'
:
'
set
'
;
var
method
=
options
.
reset
?
'
reset
'
:
'
set
'
;
collection
[
method
](
resp
,
options
);
collection
[
method
](
resp
,
options
);
if
(
success
)
success
(
collection
,
resp
,
options
);
if
(
success
)
success
.
call
(
options
.
context
,
collection
,
resp
,
options
);
collection
.
trigger
(
'
sync
'
,
collection
,
resp
,
options
);
collection
.
trigger
(
'
sync
'
,
collection
,
resp
,
options
);
};
};
wrapError
(
this
,
options
);
wrapError
(
this
,
options
);
...
@@ -875,13 +1035,15 @@
...
@@ -875,13 +1035,15 @@
// wait for the server to agree.
// wait for the server to agree.
create
:
function
(
model
,
options
)
{
create
:
function
(
model
,
options
)
{
options
=
options
?
_
.
clone
(
options
)
:
{};
options
=
options
?
_
.
clone
(
options
)
:
{};
if
(
!
(
model
=
this
.
_prepareModel
(
model
,
options
)))
return
false
;
var
wait
=
options
.
wait
;
if
(
!
options
.
wait
)
this
.
add
(
model
,
options
);
model
=
this
.
_prepareModel
(
model
,
options
);
if
(
!
model
)
return
false
;
if
(
!
wait
)
this
.
add
(
model
,
options
);
var
collection
=
this
;
var
collection
=
this
;
var
success
=
options
.
success
;
var
success
=
options
.
success
;
options
.
success
=
function
(
model
,
resp
)
{
options
.
success
=
function
(
model
,
resp
,
callbackOpts
)
{
if
(
options
.
wait
)
collection
.
add
(
model
,
option
s
);
if
(
wait
)
collection
.
add
(
model
,
callbackOpt
s
);
if
(
success
)
success
(
model
,
resp
,
option
s
);
if
(
success
)
success
.
call
(
callbackOpts
.
context
,
model
,
resp
,
callbackOpt
s
);
};
};
model
.
save
(
null
,
options
);
model
.
save
(
null
,
options
);
return
model
;
return
model
;
...
@@ -895,7 +1057,15 @@
...
@@ -895,7 +1057,15 @@
// Create a new collection with an identical list of models as this one.
// Create a new collection with an identical list of models as this one.
clone
:
function
()
{
clone
:
function
()
{
return
new
this
.
constructor
(
this
.
models
);
return
new
this
.
constructor
(
this
.
models
,
{
model
:
this
.
model
,
comparator
:
this
.
comparator
});
},
// Define how to uniquely identify models in the collection.
modelId
:
function
(
attrs
)
{
return
attrs
[
this
.
model
.
prototype
.
idAttribute
||
'
id
'
];
},
},
// Private method to reset all internal state. Called when the collection
// Private method to reset all internal state. Called when the collection
...
@@ -909,7 +1079,10 @@
...
@@ -909,7 +1079,10 @@
// Prepare a hash of attributes (or other model) to be added to this
// Prepare a hash of attributes (or other model) to be added to this
// collection.
// collection.
_prepareModel
:
function
(
attrs
,
options
)
{
_prepareModel
:
function
(
attrs
,
options
)
{
if
(
attrs
instanceof
Model
)
return
attrs
;
if
(
this
.
_isModel
(
attrs
))
{
if
(
!
attrs
.
collection
)
attrs
.
collection
=
this
;
return
attrs
;
}
options
=
options
?
_
.
clone
(
options
)
:
{};
options
=
options
?
_
.
clone
(
options
)
:
{};
options
.
collection
=
this
;
options
.
collection
=
this
;
var
model
=
new
this
.
model
(
attrs
,
options
);
var
model
=
new
this
.
model
(
attrs
,
options
);
...
@@ -918,16 +1091,47 @@
...
@@ -918,16 +1091,47 @@
return
false
;
return
false
;
},
},
// Internal method called by both remove and set.
_removeModels
:
function
(
models
,
options
)
{
var
removed
=
[];
for
(
var
i
=
0
;
i
<
models
.
length
;
i
++
)
{
var
model
=
this
.
get
(
models
[
i
]);
if
(
!
model
)
continue
;
var
index
=
this
.
indexOf
(
model
);
this
.
models
.
splice
(
index
,
1
);
this
.
length
--
;
if
(
!
options
.
silent
)
{
options
.
index
=
index
;
model
.
trigger
(
'
remove
'
,
model
,
this
,
options
);
}
removed
.
push
(
model
);
this
.
_removeReference
(
model
,
options
);
}
return
removed
.
length
?
removed
:
false
;
},
// Method for checking whether an object should be considered a model for
// the purposes of adding to the collection.
_isModel
:
function
(
model
)
{
return
model
instanceof
Model
;
},
// Internal method to create a model's ties to a collection.
// Internal method to create a model's ties to a collection.
_addReference
:
function
(
model
,
options
)
{
_addReference
:
function
(
model
,
options
)
{
this
.
_byId
[
model
.
cid
]
=
model
;
this
.
_byId
[
model
.
cid
]
=
model
;
if
(
model
.
id
!=
null
)
this
.
_byId
[
model
.
id
]
=
model
;
var
id
=
this
.
modelId
(
model
.
attributes
)
;
if
(
!
model
.
collection
)
model
.
collection
=
this
;
if
(
id
!=
null
)
this
.
_byId
[
id
]
=
model
;
model
.
on
(
'
all
'
,
this
.
_onModelEvent
,
this
);
model
.
on
(
'
all
'
,
this
.
_onModelEvent
,
this
);
},
},
// Internal method to sever a model's ties to a collection.
// Internal method to sever a model's ties to a collection.
_removeReference
:
function
(
model
,
options
)
{
_removeReference
:
function
(
model
,
options
)
{
delete
this
.
_byId
[
model
.
cid
];
var
id
=
this
.
modelId
(
model
.
attributes
);
if
(
id
!=
null
)
delete
this
.
_byId
[
id
];
if
(
this
===
model
.
collection
)
delete
model
.
collection
;
if
(
this
===
model
.
collection
)
delete
model
.
collection
;
model
.
off
(
'
all
'
,
this
.
_onModelEvent
,
this
);
model
.
off
(
'
all
'
,
this
.
_onModelEvent
,
this
);
},
},
...
@@ -939,9 +1143,13 @@
...
@@ -939,9 +1143,13 @@
_onModelEvent
:
function
(
event
,
model
,
collection
,
options
)
{
_onModelEvent
:
function
(
event
,
model
,
collection
,
options
)
{
if
((
event
===
'
add
'
||
event
===
'
remove
'
)
&&
collection
!==
this
)
return
;
if
((
event
===
'
add
'
||
event
===
'
remove
'
)
&&
collection
!==
this
)
return
;
if
(
event
===
'
destroy
'
)
this
.
remove
(
model
,
options
);
if
(
event
===
'
destroy
'
)
this
.
remove
(
model
,
options
);
if
(
model
&&
event
===
'
change:
'
+
model
.
idAttribute
)
{
if
(
event
===
'
change
'
)
{
delete
this
.
_byId
[
model
.
previous
(
model
.
idAttribute
)];
var
prevId
=
this
.
modelId
(
model
.
previousAttributes
());
if
(
model
.
id
!=
null
)
this
.
_byId
[
model
.
id
]
=
model
;
var
id
=
this
.
modelId
(
model
.
attributes
);
if
(
prevId
!==
id
)
{
if
(
prevId
!=
null
)
delete
this
.
_byId
[
prevId
];
if
(
id
!=
null
)
this
.
_byId
[
id
]
=
model
;
}
}
}
this
.
trigger
.
apply
(
this
,
arguments
);
this
.
trigger
.
apply
(
this
,
arguments
);
}
}
...
@@ -951,34 +1159,17 @@
...
@@ -951,34 +1159,17 @@
// Underscore methods that we want to implement on the Collection.
// Underscore methods that we want to implement on the Collection.
// 90% of the core usefulness of Backbone Collections is actually implemented
// 90% of the core usefulness of Backbone Collections is actually implemented
// right here:
// right here:
var
methods
=
[
'
forEach
'
,
'
each
'
,
'
map
'
,
'
collect
'
,
'
reduce
'
,
'
foldl
'
,
var
collectionMethods
=
{
forEach
:
3
,
each
:
3
,
map
:
3
,
collect
:
3
,
reduce
:
4
,
'
inject
'
,
'
reduceRight
'
,
'
foldr
'
,
'
find
'
,
'
detect
'
,
'
filter
'
,
'
select
'
,
foldl
:
4
,
inject
:
4
,
reduceRight
:
4
,
foldr
:
4
,
find
:
3
,
detect
:
3
,
filter
:
3
,
'
reject
'
,
'
every
'
,
'
all
'
,
'
some
'
,
'
any
'
,
'
include
'
,
'
contains
'
,
'
invoke
'
,
select
:
3
,
reject
:
3
,
every
:
3
,
all
:
3
,
some
:
3
,
any
:
3
,
include
:
3
,
includes
:
3
,
'
max
'
,
'
min
'
,
'
toArray
'
,
'
size
'
,
'
first
'
,
'
head
'
,
'
take
'
,
'
initial
'
,
'
rest
'
,
contains
:
3
,
invoke
:
0
,
max
:
3
,
min
:
3
,
toArray
:
1
,
size
:
1
,
first
:
3
,
'
tail
'
,
'
drop
'
,
'
last
'
,
'
without
'
,
'
difference
'
,
'
indexOf
'
,
'
shuffle
'
,
head
:
3
,
take
:
3
,
initial
:
3
,
rest
:
3
,
tail
:
3
,
drop
:
3
,
last
:
3
,
'
lastIndexOf
'
,
'
isEmpty
'
,
'
chain
'
,
'
sample
'
];
without
:
0
,
difference
:
0
,
indexOf
:
3
,
shuffle
:
1
,
lastIndexOf
:
3
,
isEmpty
:
1
,
chain
:
1
,
sample
:
3
,
partition
:
3
,
groupBy
:
3
,
countBy
:
3
,
sortBy
:
3
,
indexBy
:
3
};
// Mix in each Underscore method as a proxy to `Collection#models`.
// Mix in each Underscore method as a proxy to `Collection#models`.
_
.
each
(
methods
,
function
(
method
)
{
addUnderscoreMethods
(
Collection
,
collectionMethods
,
'
models
'
);
Collection
.
prototype
[
method
]
=
function
()
{
var
args
=
slice
.
call
(
arguments
);
args
.
unshift
(
this
.
models
);
return
_
[
method
].
apply
(
_
,
args
);
};
});
// Underscore methods that take a property name as an argument.
var
attributeMethods
=
[
'
groupBy
'
,
'
countBy
'
,
'
sortBy
'
,
'
indexBy
'
];
// Use attributes instead of properties.
_
.
each
(
attributeMethods
,
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
);
};
});
// Backbone.View
// Backbone.View
// -------------
// -------------
...
@@ -995,17 +1186,15 @@
...
@@ -995,17 +1186,15 @@
// if an existing element is not provided...
// if an existing element is not provided...
var
View
=
Backbone
.
View
=
function
(
options
)
{
var
View
=
Backbone
.
View
=
function
(
options
)
{
this
.
cid
=
_
.
uniqueId
(
'
view
'
);
this
.
cid
=
_
.
uniqueId
(
'
view
'
);
options
||
(
options
=
{});
_
.
extend
(
this
,
_
.
pick
(
options
,
viewOptions
));
_
.
extend
(
this
,
_
.
pick
(
options
,
viewOptions
));
this
.
_ensureElement
();
this
.
_ensureElement
();
this
.
initialize
.
apply
(
this
,
arguments
);
this
.
initialize
.
apply
(
this
,
arguments
);
this
.
delegateEvents
();
};
};
// Cached regex to split keys for `delegate`.
// Cached regex to split keys for `delegate`.
var
delegateEventSplitter
=
/^
(\S
+
)\s
*
(
.*
)
$/
;
var
delegateEventSplitter
=
/^
(\S
+
)\s
*
(
.*
)
$/
;
// List of view options to be
merged
as properties.
// List of view options to be
set
as properties.
var
viewOptions
=
[
'
model
'
,
'
collection
'
,
'
el
'
,
'
id
'
,
'
attributes
'
,
'
className
'
,
'
tagName
'
,
'
events
'
];
var
viewOptions
=
[
'
model
'
,
'
collection
'
,
'
el
'
,
'
id
'
,
'
attributes
'
,
'
className
'
,
'
tagName
'
,
'
events
'
];
// Set up all inheritable **Backbone.View** properties and methods.
// Set up all inheritable **Backbone.View** properties and methods.
...
@@ -1034,21 +1223,37 @@
...
@@ -1034,21 +1223,37 @@
// Remove this view by taking the element out of the DOM, and removing any
// Remove this view by taking the element out of the DOM, and removing any
// applicable Backbone.Events listeners.
// applicable Backbone.Events listeners.
remove
:
function
()
{
remove
:
function
()
{
this
.
$el
.
remove
();
this
.
_removeElement
();
this
.
stopListening
();
this
.
stopListening
();
return
this
;
return
this
;
},
},
// Change the view's element (`this.el` property), including event
// Remove this view's element from the document and all event listeners
// re-delegation.
// attached to it. Exposed for subclasses using an alternative DOM
setElement
:
function
(
element
,
delegate
)
{
// manipulation API.
if
(
this
.
$el
)
this
.
undelegateEvents
();
_removeElement
:
function
()
{
this
.
$el
=
element
instanceof
Backbone
.
$
?
element
:
Backbone
.
$
(
element
);
this
.
$el
.
remove
();
this
.
el
=
this
.
$el
[
0
];
},
if
(
delegate
!==
false
)
this
.
delegateEvents
();
// Change the view's element (`this.el` property) and re-delegate the
// view's events on the new element.
setElement
:
function
(
element
)
{
this
.
undelegateEvents
();
this
.
_setElement
(
element
);
this
.
delegateEvents
();
return
this
;
return
this
;
},
},
// Creates the `this.el` and `this.$el` references for this view using the
// given `el`. `el` can be a CSS selector or an HTML string, a jQuery
// context or an element. Subclasses can override this to utilize an
// alternative DOM manipulation API and are only required to set the
// `this.el` property.
_setElement
:
function
(
el
)
{
this
.
$el
=
el
instanceof
Backbone
.
$
?
el
:
Backbone
.
$
(
el
);
this
.
el
=
this
.
$el
[
0
];
},
// Set callbacks, where `this.events` is a hash of
// Set callbacks, where `this.events` is a hash of
//
//
// *{"event selector": "callback"}*
// *{"event selector": "callback"}*
...
@@ -1062,37 +1267,49 @@
...
@@ -1062,37 +1267,49 @@
// pairs. Callbacks will be bound to the view, with `this` set properly.
// pairs. Callbacks will be bound to the view, with `this` set properly.
// Uses event delegation for efficiency.
// Uses event delegation for efficiency.
// Omitting the selector binds the event to `this.el`.
// Omitting the selector binds the event to `this.el`.
// This only works for delegate-able events: not `focus`, `blur`, and
// not `change`, `submit`, and `reset` in Internet Explorer.
delegateEvents
:
function
(
events
)
{
delegateEvents
:
function
(
events
)
{
if
(
!
(
events
||
(
events
=
_
.
result
(
this
,
'
events
'
))))
return
this
;
events
||
(
events
=
_
.
result
(
this
,
'
events
'
));
if
(
!
events
)
return
this
;
this
.
undelegateEvents
();
this
.
undelegateEvents
();
for
(
var
key
in
events
)
{
for
(
var
key
in
events
)
{
var
method
=
events
[
key
];
var
method
=
events
[
key
];
if
(
!
_
.
isFunction
(
method
))
method
=
this
[
events
[
key
]
];
if
(
!
_
.
isFunction
(
method
))
method
=
this
[
method
];
if
(
!
method
)
continue
;
if
(
!
method
)
continue
;
var
match
=
key
.
match
(
delegateEventSplitter
);
var
match
=
key
.
match
(
delegateEventSplitter
);
var
eventName
=
match
[
1
],
selector
=
match
[
2
];
this
.
delegate
(
match
[
1
],
match
[
2
],
_
.
bind
(
method
,
this
));
method
=
_
.
bind
(
method
,
this
);
eventName
+=
'
.delegateEvents
'
+
this
.
cid
;
if
(
selector
===
''
)
{
this
.
$el
.
on
(
eventName
,
method
);
}
else
{
this
.
$el
.
on
(
eventName
,
selector
,
method
);
}
}
}
return
this
;
return
this
;
},
},
// Clears all callbacks previously bound to the view with `delegateEvents`.
// Add a single event listener to the view's element (or a child element
// using `selector`). This only works for delegate-able events: not `focus`,
// `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
delegate
:
function
(
eventName
,
selector
,
listener
)
{
this
.
$el
.
on
(
eventName
+
'
.delegateEvents
'
+
this
.
cid
,
selector
,
listener
);
return
this
;
},
// Clears all callbacks previously bound to the view by `delegateEvents`.
// You usually don't need to use this, but may wish to if you have multiple
// You usually don't need to use this, but may wish to if you have multiple
// Backbone views attached to the same DOM element.
// Backbone views attached to the same DOM element.
undelegateEvents
:
function
()
{
undelegateEvents
:
function
()
{
this
.
$el
.
off
(
'
.delegateEvents
'
+
this
.
cid
);
if
(
this
.
$el
)
this
.
$el
.
off
(
'
.delegateEvents
'
+
this
.
cid
);
return
this
;
},
// A finer-grained `undelegateEvents` for removing a single delegated event.
// `selector` and `listener` are both optional.
undelegate
:
function
(
eventName
,
selector
,
listener
)
{
this
.
$el
.
off
(
eventName
+
'
.delegateEvents
'
+
this
.
cid
,
selector
,
listener
);
return
this
;
return
this
;
},
},
// Produces a DOM element to be assigned to your view. Exposed for
// subclasses using an alternative DOM manipulation API.
_createElement
:
function
(
tagName
)
{
return
document
.
createElement
(
tagName
);
},
// Ensure that the View has a DOM element to render into.
// Ensure that the View has a DOM element to render into.
// If `this.el` is a string, pass it through `$()`, take the first
// If `this.el` is a string, pass it through `$()`, take the first
// matching element, and re-assign it to `el`. Otherwise, create
// matching element, and re-assign it to `el`. Otherwise, create
...
@@ -1102,11 +1319,17 @@
...
@@ -1102,11 +1319,17 @@
var
attrs
=
_
.
extend
({},
_
.
result
(
this
,
'
attributes
'
));
var
attrs
=
_
.
extend
({},
_
.
result
(
this
,
'
attributes
'
));
if
(
this
.
id
)
attrs
.
id
=
_
.
result
(
this
,
'
id
'
);
if
(
this
.
id
)
attrs
.
id
=
_
.
result
(
this
,
'
id
'
);
if
(
this
.
className
)
attrs
[
'
class
'
]
=
_
.
result
(
this
,
'
className
'
);
if
(
this
.
className
)
attrs
[
'
class
'
]
=
_
.
result
(
this
,
'
className
'
);
var
$el
=
Backbone
.
$
(
'
<
'
+
_
.
result
(
this
,
'
tagName
'
)
+
'
>
'
).
attr
(
attrs
);
this
.
setElement
(
this
.
_createElement
(
_
.
result
(
this
,
'
tagName
'
))
);
this
.
setElement
(
$el
,
false
);
this
.
_setAttributes
(
attrs
);
}
else
{
}
else
{
this
.
setElement
(
_
.
result
(
this
,
'
el
'
)
,
false
);
this
.
setElement
(
_
.
result
(
this
,
'
el
'
));
}
}
},
// Set attributes from a hash on this view's element. Exposed for
// subclasses using an alternative DOM manipulation API.
_setAttributes
:
function
(
attributes
)
{
this
.
$el
.
attr
(
attributes
);
}
}
});
});
...
@@ -1175,14 +1398,13 @@
...
@@ -1175,14 +1398,13 @@
params
.
processData
=
false
;
params
.
processData
=
false
;
}
}
// If we're sending a `PATCH` request, and we're in an old Internet Explorer
// Pass along `textStatus` and `errorThrown` from jQuery.
// that still has ActiveX enabled by default, override jQuery to use that
var
error
=
options
.
error
;
// for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
options
.
error
=
function
(
xhr
,
textStatus
,
errorThrown
)
{
if
(
params
.
type
===
'
PATCH
'
&&
noXhrPatch
)
{
options
.
textStatus
=
textStatus
;
params
.
xhr
=
function
()
{
options
.
errorThrown
=
errorThrown
;
return
new
ActiveXObject
(
"
Microsoft.XMLHTTP
"
);
if
(
error
)
error
.
call
(
options
.
context
,
xhr
,
textStatus
,
errorThrown
);
};
};
}
// Make the request, allowing the user to override any Ajax options.
// Make the request, allowing the user to override any Ajax options.
var
xhr
=
options
.
xhr
=
Backbone
.
ajax
(
_
.
extend
(
params
,
options
));
var
xhr
=
options
.
xhr
=
Backbone
.
ajax
(
_
.
extend
(
params
,
options
));
...
@@ -1190,10 +1412,6 @@
...
@@ -1190,10 +1412,6 @@
return
xhr
;
return
xhr
;
};
};
var
noXhrPatch
=
typeof
window
!==
'
undefined
'
&&
!!
window
.
ActiveXObject
&&
!
(
window
.
XMLHttpRequest
&&
(
new
XMLHttpRequest
).
dispatchEvent
);
// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
var
methodMap
=
{
var
methodMap
=
{
'
create
'
:
'
POST
'
,
'
create
'
:
'
POST
'
,
...
@@ -1251,17 +1469,18 @@
...
@@ -1251,17 +1469,18 @@
var
router
=
this
;
var
router
=
this
;
Backbone
.
history
.
route
(
route
,
function
(
fragment
)
{
Backbone
.
history
.
route
(
route
,
function
(
fragment
)
{
var
args
=
router
.
_extractParameters
(
route
,
fragment
);
var
args
=
router
.
_extractParameters
(
route
,
fragment
);
router
.
execute
(
callback
,
args
);
if
(
router
.
execute
(
callback
,
args
,
name
)
!==
false
)
{
router
.
trigger
.
apply
(
router
,
[
'
route:
'
+
name
].
concat
(
args
));
router
.
trigger
.
apply
(
router
,
[
'
route:
'
+
name
].
concat
(
args
));
router
.
trigger
(
'
route
'
,
name
,
args
);
router
.
trigger
(
'
route
'
,
name
,
args
);
Backbone
.
history
.
trigger
(
'
route
'
,
router
,
name
,
args
);
Backbone
.
history
.
trigger
(
'
route
'
,
router
,
name
,
args
);
}
});
});
return
this
;
return
this
;
},
},
// Execute a route handler with the provided parameters. This is an
// Execute a route handler with the provided parameters. This is an
// excellent place to do pre-route setup or post-route cleanup.
// excellent place to do pre-route setup or post-route cleanup.
execute
:
function
(
callback
,
args
)
{
execute
:
function
(
callback
,
args
,
name
)
{
if
(
callback
)
callback
.
apply
(
this
,
args
);
if
(
callback
)
callback
.
apply
(
this
,
args
);
},
},
...
@@ -1319,7 +1538,7 @@
...
@@ -1319,7 +1538,7 @@
// falls back to polling.
// falls back to polling.
var
History
=
Backbone
.
History
=
function
()
{
var
History
=
Backbone
.
History
=
function
()
{
this
.
handlers
=
[];
this
.
handlers
=
[];
_
.
bindAll
(
this
,
'
checkUrl
'
);
this
.
checkUrl
=
_
.
bind
(
this
.
checkUrl
,
this
);
// Ensure that `History` can be used outside of the browser.
// Ensure that `History` can be used outside of the browser.
if
(
typeof
window
!==
'
undefined
'
)
{
if
(
typeof
window
!==
'
undefined
'
)
{
...
@@ -1334,12 +1553,6 @@
...
@@ -1334,12 +1553,6 @@
// Cached regex for stripping leading and trailing slashes.
// Cached regex for stripping leading and trailing slashes.
var
rootStripper
=
/^
\/
+|
\/
+$/g
;
var
rootStripper
=
/^
\/
+|
\/
+$/g
;
// Cached regex for detecting MSIE.
var
isExplorer
=
/msie
[\w
.
]
+/
;
// Cached regex for removing a trailing slash.
var
trailingSlash
=
/
\/
$/
;
// Cached regex for stripping urls of hash.
// Cached regex for stripping urls of hash.
var
pathStripper
=
/#.*$/
;
var
pathStripper
=
/#.*$/
;
...
@@ -1355,7 +1568,29 @@
...
@@ -1355,7 +1568,29 @@
// Are we at the app root?
// Are we at the app root?
atRoot
:
function
()
{
atRoot
:
function
()
{
return
this
.
location
.
pathname
.
replace
(
/
[^\/]
$/
,
'
$&/
'
)
===
this
.
root
;
var
path
=
this
.
location
.
pathname
.
replace
(
/
[^\/]
$/
,
'
$&/
'
);
return
path
===
this
.
root
&&
!
this
.
getSearch
();
},
// Does the pathname match the root?
matchRoot
:
function
()
{
var
path
=
this
.
decodeFragment
(
this
.
location
.
pathname
);
var
root
=
path
.
slice
(
0
,
this
.
root
.
length
-
1
)
+
'
/
'
;
return
root
===
this
.
root
;
},
// Unicode characters in `location.pathname` are percent encoded so they're
// decoded for comparison. `%25` should not be decoded since it may be part
// of an encoded parameter.
decodeFragment
:
function
(
fragment
)
{
return
decodeURI
(
fragment
.
replace
(
/%25/g
,
'
%2525
'
));
},
// In IE6, the hash fragment and search params are incorrect if the
// fragment contains `?`.
getSearch
:
function
()
{
var
match
=
this
.
location
.
href
.
replace
(
/#.*/
,
''
).
match
(
/
\?
.+/
);
return
match
?
match
[
0
]
:
''
;
},
},
// Gets the true hash value. Cannot use location.hash directly due to bug
// Gets the true hash value. Cannot use location.hash directly due to bug
...
@@ -1365,14 +1600,19 @@
...
@@ -1365,14 +1600,19 @@
return
match
?
match
[
1
]
:
''
;
return
match
?
match
[
1
]
:
''
;
},
},
// Get the cross-browser normalized URL fragment, either from the URL,
// Get the pathname and search params, without the root.
// the hash, or the override.
getPath
:
function
()
{
getFragment
:
function
(
fragment
,
forcePushState
)
{
var
path
=
this
.
decodeFragment
(
this
.
location
.
pathname
+
this
.
getSearch
()
).
slice
(
this
.
root
.
length
-
1
);
return
path
.
charAt
(
0
)
===
'
/
'
?
path
.
slice
(
1
)
:
path
;
},
// Get the cross-browser normalized URL fragment from the path or hash.
getFragment
:
function
(
fragment
)
{
if
(
fragment
==
null
)
{
if
(
fragment
==
null
)
{
if
(
this
.
_hasPushState
||
!
this
.
_wantsHashChange
||
forcePushState
)
{
if
(
this
.
_usePushState
||
!
this
.
_wantsHashChange
)
{
fragment
=
decodeURI
(
this
.
location
.
pathname
+
this
.
location
.
search
);
fragment
=
this
.
getPath
();
var
root
=
this
.
root
.
replace
(
trailingSlash
,
''
);
if
(
!
fragment
.
indexOf
(
root
))
fragment
=
fragment
.
slice
(
root
.
length
);
}
else
{
}
else
{
fragment
=
this
.
getHash
();
fragment
=
this
.
getHash
();
}
}
...
@@ -1383,7 +1623,7 @@
...
@@ -1383,7 +1623,7 @@
// Start the hash change handling, returning `true` if the current URL matches
// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
// an existing route, and `false` otherwise.
start
:
function
(
options
)
{
start
:
function
(
options
)
{
if
(
History
.
started
)
throw
new
Error
(
"
Backbone.history has already been started
"
);
if
(
History
.
started
)
throw
new
Error
(
'
Backbone.history has already been started
'
);
History
.
started
=
true
;
History
.
started
=
true
;
// Figure out the initial configuration. Do we need an iframe?
// Figure out the initial configuration. Do we need an iframe?
...
@@ -1391,36 +1631,16 @@
...
@@ -1391,36 +1631,16 @@
this
.
options
=
_
.
extend
({
root
:
'
/
'
},
this
.
options
,
options
);
this
.
options
=
_
.
extend
({
root
:
'
/
'
},
this
.
options
,
options
);
this
.
root
=
this
.
options
.
root
;
this
.
root
=
this
.
options
.
root
;
this
.
_wantsHashChange
=
this
.
options
.
hashChange
!==
false
;
this
.
_wantsHashChange
=
this
.
options
.
hashChange
!==
false
;
this
.
_hasHashChange
=
'
onhashchange
'
in
window
&&
(
document
.
documentMode
===
void
0
||
document
.
documentMode
>
7
);
this
.
_useHashChange
=
this
.
_wantsHashChange
&&
this
.
_hasHashChange
;
this
.
_wantsPushState
=
!!
this
.
options
.
pushState
;
this
.
_wantsPushState
=
!!
this
.
options
.
pushState
;
this
.
_hasPushState
=
!!
(
this
.
options
.
pushState
&&
this
.
history
&&
this
.
history
.
pushState
);
this
.
_hasPushState
=
!!
(
this
.
history
&&
this
.
history
.
pushState
);
var
fragment
=
this
.
getFragment
();
this
.
_usePushState
=
this
.
_wantsPushState
&&
this
.
_hasPushState
;
var
docMode
=
document
.
documentMode
;
this
.
fragment
=
this
.
getFragment
();
var
oldIE
=
(
isExplorer
.
exec
(
navigator
.
userAgent
.
toLowerCase
())
&&
(
!
docMode
||
docMode
<=
7
));
// Normalize root to always include a leading and trailing slash.
// Normalize root to always include a leading and trailing slash.
this
.
root
=
(
'
/
'
+
this
.
root
+
'
/
'
).
replace
(
rootStripper
,
'
/
'
);
this
.
root
=
(
'
/
'
+
this
.
root
+
'
/
'
).
replace
(
rootStripper
,
'
/
'
);
if
(
oldIE
&&
this
.
_wantsHashChange
)
{
var
frame
=
Backbone
.
$
(
'
<iframe src="javascript:0" tabindex="-1">
'
);
this
.
iframe
=
frame
.
hide
().
appendTo
(
'
body
'
)[
0
].
contentWindow
;
this
.
navigate
(
fragment
);
}
// Depending on whether we're using pushState or hashes, and whether
// 'onhashchange' is supported, determine how we check the URL state.
if
(
this
.
_hasPushState
)
{
Backbone
.
$
(
window
).
on
(
'
popstate
'
,
this
.
checkUrl
);
}
else
if
(
this
.
_wantsHashChange
&&
(
'
onhashchange
'
in
window
)
&&
!
oldIE
)
{
Backbone
.
$
(
window
).
on
(
'
hashchange
'
,
this
.
checkUrl
);
}
else
if
(
this
.
_wantsHashChange
)
{
this
.
_checkUrlInterval
=
setInterval
(
this
.
checkUrl
,
this
.
interval
);
}
// Determine if we need to change the base url, for a pushState link
// opened by a non-pushState browser.
this
.
fragment
=
fragment
;
var
loc
=
this
.
location
;
// Transition from hashChange to pushState or vice versa if both are
// Transition from hashChange to pushState or vice versa if both are
// requested.
// requested.
if
(
this
.
_wantsHashChange
&&
this
.
_wantsPushState
)
{
if
(
this
.
_wantsHashChange
&&
this
.
_wantsPushState
)
{
...
@@ -1428,27 +1648,75 @@
...
@@ -1428,27 +1648,75 @@
// If we've started off with a route from a `pushState`-enabled
// If we've started off with a route from a `pushState`-enabled
// browser, but we're currently in a browser that doesn't support it...
// browser, but we're currently in a browser that doesn't support it...
if
(
!
this
.
_hasPushState
&&
!
this
.
atRoot
())
{
if
(
!
this
.
_hasPushState
&&
!
this
.
atRoot
())
{
this
.
fragment
=
this
.
getFragment
(
null
,
true
)
;
var
root
=
this
.
root
.
slice
(
0
,
-
1
)
||
'
/
'
;
this
.
location
.
replace
(
this
.
root
+
'
#
'
+
this
.
fragment
);
this
.
location
.
replace
(
root
+
'
#
'
+
this
.
getPath
()
);
// Return immediately as browser will do redirect to new url
// Return immediately as browser will do redirect to new url
return
true
;
return
true
;
// Or if we've started out with a hash-based route, but we're currently
// Or if we've started out with a hash-based route, but we're currently
// in a browser where it could be `pushState`-based instead...
// in a browser where it could be `pushState`-based instead...
}
else
if
(
this
.
_hasPushState
&&
this
.
atRoot
()
&&
loc
.
hash
)
{
}
else
if
(
this
.
_hasPushState
&&
this
.
atRoot
())
{
this
.
fragment
=
this
.
getHash
().
replace
(
routeStripper
,
''
);
this
.
navigate
(
this
.
getHash
(),
{
replace
:
true
});
this
.
history
.
replaceState
({},
document
.
title
,
this
.
root
+
this
.
fragment
);
}
}
}
}
// Proxy an iframe to handle location events if the browser doesn't
// support the `hashchange` event, HTML5 history, or the user wants
// `hashChange` but not `pushState`.
if
(
!
this
.
_hasHashChange
&&
this
.
_wantsHashChange
&&
!
this
.
_usePushState
)
{
this
.
iframe
=
document
.
createElement
(
'
iframe
'
);
this
.
iframe
.
src
=
'
javascript:0
'
;
this
.
iframe
.
style
.
display
=
'
none
'
;
this
.
iframe
.
tabIndex
=
-
1
;
var
body
=
document
.
body
;
// Using `appendChild` will throw on IE < 9 if the document is not ready.
var
iWindow
=
body
.
insertBefore
(
this
.
iframe
,
body
.
firstChild
).
contentWindow
;
iWindow
.
document
.
open
();
iWindow
.
document
.
close
();
iWindow
.
location
.
hash
=
'
#
'
+
this
.
fragment
;
}
// Add a cross-platform `addEventListener` shim for older browsers.
var
addEventListener
=
window
.
addEventListener
||
function
(
eventName
,
listener
)
{
return
attachEvent
(
'
on
'
+
eventName
,
listener
);
};
// Depending on whether we're using pushState or hashes, and whether
// 'onhashchange' is supported, determine how we check the URL state.
if
(
this
.
_usePushState
)
{
addEventListener
(
'
popstate
'
,
this
.
checkUrl
,
false
);
}
else
if
(
this
.
_useHashChange
&&
!
this
.
iframe
)
{
addEventListener
(
'
hashchange
'
,
this
.
checkUrl
,
false
);
}
else
if
(
this
.
_wantsHashChange
)
{
this
.
_checkUrlInterval
=
setInterval
(
this
.
checkUrl
,
this
.
interval
);
}
if
(
!
this
.
options
.
silent
)
return
this
.
loadUrl
();
if
(
!
this
.
options
.
silent
)
return
this
.
loadUrl
();
},
},
// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
// but possibly useful for unit testing Routers.
// but possibly useful for unit testing Routers.
stop
:
function
()
{
stop
:
function
()
{
Backbone
.
$
(
window
).
off
(
'
popstate
'
,
this
.
checkUrl
).
off
(
'
hashchange
'
,
this
.
checkUrl
);
// Add a cross-platform `removeEventListener` shim for older browsers.
var
removeEventListener
=
window
.
removeEventListener
||
function
(
eventName
,
listener
)
{
return
detachEvent
(
'
on
'
+
eventName
,
listener
);
};
// Remove window listeners.
if
(
this
.
_usePushState
)
{
removeEventListener
(
'
popstate
'
,
this
.
checkUrl
,
false
);
}
else
if
(
this
.
_useHashChange
&&
!
this
.
iframe
)
{
removeEventListener
(
'
hashchange
'
,
this
.
checkUrl
,
false
);
}
// Clean up the iframe if necessary.
if
(
this
.
iframe
)
{
document
.
body
.
removeChild
(
this
.
iframe
);
this
.
iframe
=
null
;
}
// Some environments will throw when clearing an undefined interval.
if
(
this
.
_checkUrlInterval
)
clearInterval
(
this
.
_checkUrlInterval
);
if
(
this
.
_checkUrlInterval
)
clearInterval
(
this
.
_checkUrlInterval
);
History
.
started
=
false
;
History
.
started
=
false
;
},
},
...
@@ -1463,9 +1731,13 @@
...
@@ -1463,9 +1731,13 @@
// calls `loadUrl`, normalizing across the hidden iframe.
// calls `loadUrl`, normalizing across the hidden iframe.
checkUrl
:
function
(
e
)
{
checkUrl
:
function
(
e
)
{
var
current
=
this
.
getFragment
();
var
current
=
this
.
getFragment
();
// If the user pressed the back button, the iframe's hash will have
// changed and we should use that for comparison.
if
(
current
===
this
.
fragment
&&
this
.
iframe
)
{
if
(
current
===
this
.
fragment
&&
this
.
iframe
)
{
current
=
this
.
get
Fragment
(
this
.
getHash
(
this
.
iframe
)
);
current
=
this
.
get
Hash
(
this
.
iframe
.
contentWindow
);
}
}
if
(
current
===
this
.
fragment
)
return
false
;
if
(
current
===
this
.
fragment
)
return
false
;
if
(
this
.
iframe
)
this
.
navigate
(
current
);
if
(
this
.
iframe
)
this
.
navigate
(
current
);
this
.
loadUrl
();
this
.
loadUrl
();
...
@@ -1475,8 +1747,10 @@
...
@@ -1475,8 +1747,10 @@
// match, returns `true`. If no defined routes matches the fragment,
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
// returns `false`.
loadUrl
:
function
(
fragment
)
{
loadUrl
:
function
(
fragment
)
{
// If the root doesn't match, no routes can match either.
if
(
!
this
.
matchRoot
())
return
false
;
fragment
=
this
.
fragment
=
this
.
getFragment
(
fragment
);
fragment
=
this
.
fragment
=
this
.
getFragment
(
fragment
);
return
_
.
any
(
this
.
handlers
,
function
(
handler
)
{
return
_
.
some
(
this
.
handlers
,
function
(
handler
)
{
if
(
handler
.
route
.
test
(
fragment
))
{
if
(
handler
.
route
.
test
(
fragment
))
{
handler
.
callback
(
fragment
);
handler
.
callback
(
fragment
);
return
true
;
return
true
;
...
@@ -1495,31 +1769,42 @@
...
@@ -1495,31 +1769,42 @@
if
(
!
History
.
started
)
return
false
;
if
(
!
History
.
started
)
return
false
;
if
(
!
options
||
options
===
true
)
options
=
{
trigger
:
!!
options
};
if
(
!
options
||
options
===
true
)
options
=
{
trigger
:
!!
options
};
var
url
=
this
.
root
+
(
fragment
=
this
.
getFragment
(
fragment
||
''
));
// Normalize the fragment.
fragment
=
this
.
getFragment
(
fragment
||
''
);
// Strip the hash for matching.
// Don't include a trailing slash on the root.
fragment
=
fragment
.
replace
(
pathStripper
,
''
);
var
root
=
this
.
root
;
if
(
fragment
===
''
||
fragment
.
charAt
(
0
)
===
'
?
'
)
{
root
=
root
.
slice
(
0
,
-
1
)
||
'
/
'
;
}
var
url
=
root
+
fragment
;
// Strip the hash and decode for matching.
fragment
=
this
.
decodeFragment
(
fragment
.
replace
(
pathStripper
,
''
));
if
(
this
.
fragment
===
fragment
)
return
;
if
(
this
.
fragment
===
fragment
)
return
;
this
.
fragment
=
fragment
;
this
.
fragment
=
fragment
;
// Don't include a trailing slash on the root.
if
(
fragment
===
''
&&
url
!==
'
/
'
)
url
=
url
.
slice
(
0
,
-
1
);
// If pushState is available, we use it to set the fragment as a real URL.
// If pushState is available, we use it to set the fragment as a real URL.
if
(
this
.
_
has
PushState
)
{
if
(
this
.
_
use
PushState
)
{
this
.
history
[
options
.
replace
?
'
replaceState
'
:
'
pushState
'
]({},
document
.
title
,
url
);
this
.
history
[
options
.
replace
?
'
replaceState
'
:
'
pushState
'
]({},
document
.
title
,
url
);
// If hash changes haven't been explicitly disabled, update the hash
// If hash changes haven't been explicitly disabled, update the hash
// fragment to store history.
// fragment to store history.
}
else
if
(
this
.
_wantsHashChange
)
{
}
else
if
(
this
.
_wantsHashChange
)
{
this
.
_updateHash
(
this
.
location
,
fragment
,
options
.
replace
);
this
.
_updateHash
(
this
.
location
,
fragment
,
options
.
replace
);
if
(
this
.
iframe
&&
(
fragment
!==
this
.
getFragment
(
this
.
getHash
(
this
.
iframe
))))
{
if
(
this
.
iframe
&&
(
fragment
!==
this
.
getHash
(
this
.
iframe
.
contentWindow
)))
{
var
iWindow
=
this
.
iframe
.
contentWindow
;
// Opening and closing the iframe tricks IE7 and earlier to push a
// Opening and closing the iframe tricks IE7 and earlier to push a
// history entry on hash-tag change. When replace is true, we don't
// history entry on hash-tag change. When replace is true, we don't
// want this.
// want this.
if
(
!
options
.
replace
)
this
.
iframe
.
document
.
open
().
close
();
if
(
!
options
.
replace
)
{
this
.
_updateHash
(
this
.
iframe
.
location
,
fragment
,
options
.
replace
);
iWindow
.
document
.
open
();
iWindow
.
document
.
close
();
}
this
.
_updateHash
(
iWindow
.
location
,
fragment
,
options
.
replace
);
}
}
// If you've told us that you explicitly don't want fallback hashchange-
// If you've told us that you explicitly don't want fallback hashchange-
...
@@ -1550,7 +1835,7 @@
...
@@ -1550,7 +1835,7 @@
// Helpers
// Helpers
// -------
// -------
// Helper function to correctly set up the prototype chain
,
for subclasses.
// Helper function to correctly set up the prototype chain for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended.
// class properties to be extended.
var
extend
=
function
(
protoProps
,
staticProps
)
{
var
extend
=
function
(
protoProps
,
staticProps
)
{
...
@@ -1559,7 +1844,7 @@
...
@@ -1559,7 +1844,7 @@
// The constructor function for the new subclass is either defined by you
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent
's
constructor.
// by us to simply call the parent constructor.
if
(
protoProps
&&
_
.
has
(
protoProps
,
'
constructor
'
))
{
if
(
protoProps
&&
_
.
has
(
protoProps
,
'
constructor
'
))
{
child
=
protoProps
.
constructor
;
child
=
protoProps
.
constructor
;
}
else
{
}
else
{
...
@@ -1570,7 +1855,7 @@
...
@@ -1570,7 +1855,7 @@
_
.
extend
(
child
,
parent
,
staticProps
);
_
.
extend
(
child
,
parent
,
staticProps
);
// Set the prototype chain to inherit from `parent`, without calling
// Set the prototype chain to inherit from `parent`, without calling
// `parent`
's
constructor function.
// `parent` constructor function.
var
Surrogate
=
function
(){
this
.
constructor
=
child
;
};
var
Surrogate
=
function
(){
this
.
constructor
=
child
;
};
Surrogate
.
prototype
=
parent
.
prototype
;
Surrogate
.
prototype
=
parent
.
prototype
;
child
.
prototype
=
new
Surrogate
;
child
.
prototype
=
new
Surrogate
;
...
@@ -1598,7 +1883,7 @@
...
@@ -1598,7 +1883,7 @@
var
wrapError
=
function
(
model
,
options
)
{
var
wrapError
=
function
(
model
,
options
)
{
var
error
=
options
.
error
;
var
error
=
options
.
error
;
options
.
error
=
function
(
resp
)
{
options
.
error
=
function
(
resp
)
{
if
(
error
)
error
(
model
,
resp
,
options
);
if
(
error
)
error
.
call
(
options
.
context
,
model
,
resp
,
options
);
model
.
trigger
(
'
error
'
,
model
,
resp
,
options
);
model
.
trigger
(
'
error
'
,
model
,
resp
,
options
);
};
};
};
};
...
...
examples/backbone/node_modules/jquery/dist/jquery.js
View file @
eec62c61
/*!
/*!
* jQuery JavaScript Library v2.1.
3
* jQuery JavaScript Library v2.1.
4
* http://jquery.com/
* http://jquery.com/
*
*
* Includes Sizzle.js
* Includes Sizzle.js
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
* Released under the MIT license
* Released under the MIT license
* http://jquery.org/license
* http://jquery.org/license
*
*
* Date: 201
4-12-18T15:1
1Z
* Date: 201
5-04-28T16:0
1Z
*/
*/
(
function
(
global
,
factory
)
{
(
function
(
global
,
factory
)
{
...
@@ -67,7 +67,7 @@ var
...
@@ -67,7 +67,7 @@ var
// Use the correct document accordingly with window argument (sandbox)
// Use the correct document accordingly with window argument (sandbox)
document
=
window
.
document
,
document
=
window
.
document
,
version
=
"
2.1.
3
"
,
version
=
"
2.1.
4
"
,
// Define a local copy of jQuery
// Define a local copy of jQuery
jQuery
=
function
(
selector
,
context
)
{
jQuery
=
function
(
selector
,
context
)
{
...
@@ -531,7 +531,12 @@ jQuery.each("Boolean Number String Function Array Date RegExp Object Error".spli
...
@@ -531,7 +531,12 @@ jQuery.each("Boolean Number String Function Array Date RegExp Object Error".spli
});
});
function
isArraylike
(
obj
)
{
function
isArraylike
(
obj
)
{
var
length
=
obj
.
length
,
// Support: iOS 8.2 (not reproducible in simulator)
// `in` check used to prevent JIT error (gh-2145)
// hasOwn isn't used here due to false negatives
// regarding Nodelist length in IE
var
length
=
"
length
"
in
obj
&&
obj
.
length
,
type
=
jQuery
.
type
(
obj
);
type
=
jQuery
.
type
(
obj
);
if
(
type
===
"
function
"
||
jQuery
.
isWindow
(
obj
)
)
{
if
(
type
===
"
function
"
||
jQuery
.
isWindow
(
obj
)
)
{
...
...
examples/backbone/node_modules/todomvc-common/base.js
View file @
eec62c61
...
@@ -114,7 +114,12 @@
...
@@ -114,7 +114,12 @@
})({});
})({});
if
(
location
.
hostname
===
'
todomvc.com
'
)
{
if
(
location
.
hostname
===
'
todomvc.com
'
)
{
window
.
_gaq
=
[[
'
_setAccount
'
,
'
UA-31081062-1
'
],[
'
_trackPageview
'
]];(
function
(
d
,
t
){
var
g
=
d
.
createElement
(
t
),
s
=
d
.
getElementsByTagName
(
t
)[
0
];
g
.
src
=
'
//www.google-analytics.com/ga.js
'
;
s
.
parentNode
.
insertBefore
(
g
,
s
)}(
document
,
'
script
'
));
(
function
(
i
,
s
,
o
,
g
,
r
,
a
,
m
){
i
[
'
GoogleAnalyticsObject
'
]
=
r
;
i
[
r
]
=
i
[
r
]
||
function
(){
(
i
[
r
].
q
=
i
[
r
].
q
||
[]).
push
(
arguments
)},
i
[
r
].
l
=
1
*
new
Date
();
a
=
s
.
createElement
(
o
),
m
=
s
.
getElementsByTagName
(
o
)[
0
];
a
.
async
=
1
;
a
.
src
=
g
;
m
.
parentNode
.
insertBefore
(
a
,
m
)
})(
window
,
document
,
'
script
'
,
'
https://www.google-analytics.com/analytics.js
'
,
'
ga
'
);
ga
(
'
create
'
,
'
UA-31081062-1
'
,
'
auto
'
);
ga
(
'
send
'
,
'
pageview
'
);
}
}
/* jshint ignore:end */
/* jshint ignore:end */
...
@@ -228,7 +233,7 @@
...
@@ -228,7 +233,7 @@
xhr
.
onload
=
function
(
e
)
{
xhr
.
onload
=
function
(
e
)
{
var
parsedResponse
=
JSON
.
parse
(
e
.
target
.
responseText
);
var
parsedResponse
=
JSON
.
parse
(
e
.
target
.
responseText
);
if
(
parsedResponse
instanceof
Array
)
{
if
(
parsedResponse
instanceof
Array
)
{
var
count
=
parsedResponse
.
length
var
count
=
parsedResponse
.
length
;
if
(
count
!==
0
)
{
if
(
count
!==
0
)
{
issueLink
.
innerHTML
=
'
This app has
'
+
count
+
'
open issues
'
;
issueLink
.
innerHTML
=
'
This app has
'
+
count
+
'
open issues
'
;
document
.
getElementById
(
'
issue-count
'
).
style
.
display
=
'
inline
'
;
document
.
getElementById
(
'
issue-count
'
).
style
.
display
=
'
inline
'
;
...
...
examples/backbone/node_modules/underscore/underscore.js
View file @
eec62c61
// Underscore.js 1.
7.0
// Underscore.js 1.
8.3
// http://underscorejs.org
// http://underscorejs.org
// (c) 2009-201
4
Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// (c) 2009-201
5
Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
// Underscore may be freely distributed under the MIT license.
(
function
()
{
(
function
()
{
...
@@ -21,7 +21,6 @@
...
@@ -21,7 +21,6 @@
var
var
push
=
ArrayProto
.
push
,
push
=
ArrayProto
.
push
,
slice
=
ArrayProto
.
slice
,
slice
=
ArrayProto
.
slice
,
concat
=
ArrayProto
.
concat
,
toString
=
ObjProto
.
toString
,
toString
=
ObjProto
.
toString
,
hasOwnProperty
=
ObjProto
.
hasOwnProperty
;
hasOwnProperty
=
ObjProto
.
hasOwnProperty
;
...
@@ -30,7 +29,11 @@
...
@@ -30,7 +29,11 @@
var
var
nativeIsArray
=
Array
.
isArray
,
nativeIsArray
=
Array
.
isArray
,
nativeKeys
=
Object
.
keys
,
nativeKeys
=
Object
.
keys
,
nativeBind
=
FuncProto
.
bind
;
nativeBind
=
FuncProto
.
bind
,
nativeCreate
=
Object
.
create
;
// Naked function reference for surrogate-prototype-swapping.
var
Ctor
=
function
(){};
// Create a safe reference to the Underscore object for use below.
// Create a safe reference to the Underscore object for use below.
var
_
=
function
(
obj
)
{
var
_
=
function
(
obj
)
{
...
@@ -52,12 +55,12 @@
...
@@ -52,12 +55,12 @@
}
}
// Current version.
// Current version.
_
.
VERSION
=
'
1.
7.0
'
;
_
.
VERSION
=
'
1.
8.3
'
;
// Internal function that returns an efficient (for current engines) version
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
// functions.
var
createCallback
=
function
(
func
,
context
,
argCount
)
{
var
optimizeCb
=
function
(
func
,
context
,
argCount
)
{
if
(
context
===
void
0
)
return
func
;
if
(
context
===
void
0
)
return
func
;
switch
(
argCount
==
null
?
3
:
argCount
)
{
switch
(
argCount
==
null
?
3
:
argCount
)
{
case
1
:
return
function
(
value
)
{
case
1
:
return
function
(
value
)
{
...
@@ -81,12 +84,60 @@
...
@@ -81,12 +84,60 @@
// A mostly-internal function to generate callbacks that can be applied
// A mostly-internal function to generate callbacks that can be applied
// to each element in a collection, returning the desired result — either
// to each element in a collection, returning the desired result — either
// identity, an arbitrary callback, a property matcher, or a property accessor.
// identity, an arbitrary callback, a property matcher, or a property accessor.
_
.
iteratee
=
function
(
value
,
context
,
argCount
)
{
var
cb
=
function
(
value
,
context
,
argCount
)
{
if
(
value
==
null
)
return
_
.
identity
;
if
(
value
==
null
)
return
_
.
identity
;
if
(
_
.
isFunction
(
value
))
return
createCallback
(
value
,
context
,
argCount
);
if
(
_
.
isFunction
(
value
))
return
optimizeCb
(
value
,
context
,
argCount
);
if
(
_
.
isObject
(
value
))
return
_
.
matche
s
(
value
);
if
(
_
.
isObject
(
value
))
return
_
.
matche
r
(
value
);
return
_
.
property
(
value
);
return
_
.
property
(
value
);
};
};
_
.
iteratee
=
function
(
value
,
context
)
{
return
cb
(
value
,
context
,
Infinity
);
};
// An internal function for creating assigner functions.
var
createAssigner
=
function
(
keysFunc
,
undefinedOnly
)
{
return
function
(
obj
)
{
var
length
=
arguments
.
length
;
if
(
length
<
2
||
obj
==
null
)
return
obj
;
for
(
var
index
=
1
;
index
<
length
;
index
++
)
{
var
source
=
arguments
[
index
],
keys
=
keysFunc
(
source
),
l
=
keys
.
length
;
for
(
var
i
=
0
;
i
<
l
;
i
++
)
{
var
key
=
keys
[
i
];
if
(
!
undefinedOnly
||
obj
[
key
]
===
void
0
)
obj
[
key
]
=
source
[
key
];
}
}
return
obj
;
};
};
// An internal function for creating a new object that inherits from another.
var
baseCreate
=
function
(
prototype
)
{
if
(
!
_
.
isObject
(
prototype
))
return
{};
if
(
nativeCreate
)
return
nativeCreate
(
prototype
);
Ctor
.
prototype
=
prototype
;
var
result
=
new
Ctor
;
Ctor
.
prototype
=
null
;
return
result
;
};
var
property
=
function
(
key
)
{
return
function
(
obj
)
{
return
obj
==
null
?
void
0
:
obj
[
key
];
};
};
// Helper for collection methods to determine whether a collection
// should be iterated as an array or as an object
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
var
MAX_ARRAY_INDEX
=
Math
.
pow
(
2
,
53
)
-
1
;
var
getLength
=
property
(
'
length
'
);
var
isArrayLike
=
function
(
collection
)
{
var
length
=
getLength
(
collection
);
return
typeof
length
==
'
number
'
&&
length
>=
0
&&
length
<=
MAX_ARRAY_INDEX
;
};
// Collection Functions
// Collection Functions
// --------------------
// --------------------
...
@@ -95,11 +146,10 @@
...
@@ -95,11 +146,10 @@
// Handles raw objects in addition to array-likes. Treats all
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
// sparse array-likes as if they were dense.
_
.
each
=
_
.
forEach
=
function
(
obj
,
iteratee
,
context
)
{
_
.
each
=
_
.
forEach
=
function
(
obj
,
iteratee
,
context
)
{
if
(
obj
==
null
)
return
obj
;
iteratee
=
optimizeCb
(
iteratee
,
context
);
iteratee
=
createCallback
(
iteratee
,
context
);
var
i
,
length
;
var
i
,
length
=
obj
.
length
;
if
(
isArrayLike
(
obj
))
{
if
(
length
===
+
length
)
{
for
(
i
=
0
,
length
=
obj
.
length
;
i
<
length
;
i
++
)
{
for
(
i
=
0
;
i
<
length
;
i
++
)
{
iteratee
(
obj
[
i
],
i
,
obj
);
iteratee
(
obj
[
i
],
i
,
obj
);
}
}
}
else
{
}
else
{
...
@@ -113,77 +163,66 @@
...
@@ -113,77 +163,66 @@
// Return the results of applying the iteratee to each element.
// Return the results of applying the iteratee to each element.
_
.
map
=
_
.
collect
=
function
(
obj
,
iteratee
,
context
)
{
_
.
map
=
_
.
collect
=
function
(
obj
,
iteratee
,
context
)
{
if
(
obj
==
null
)
return
[];
iteratee
=
cb
(
iteratee
,
context
);
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
var
keys
=
!
isArrayLike
(
obj
)
&&
_
.
keys
(
obj
),
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
,
length
=
(
keys
||
obj
).
length
,
results
=
Array
(
length
),
results
=
Array
(
length
);
currentKey
;
for
(
var
index
=
0
;
index
<
length
;
index
++
)
{
for
(
var
index
=
0
;
index
<
length
;
index
++
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
var
currentKey
=
keys
?
keys
[
index
]
:
index
;
results
[
index
]
=
iteratee
(
obj
[
currentKey
],
currentKey
,
obj
);
results
[
index
]
=
iteratee
(
obj
[
currentKey
],
currentKey
,
obj
);
}
}
return
results
;
return
results
;
};
};
var
reduceError
=
'
Reduce of empty array with no initial value
'
;
// Create a reducing function iterating left or right.
function
createReduce
(
dir
)
{
// Optimized iterator function as using arguments.length
// in the main function will deoptimize the, see #1991.
function
iterator
(
obj
,
iteratee
,
memo
,
keys
,
index
,
length
)
{
for
(;
index
>=
0
&&
index
<
length
;
index
+=
dir
)
{
var
currentKey
=
keys
?
keys
[
index
]
:
index
;
memo
=
iteratee
(
memo
,
obj
[
currentKey
],
currentKey
,
obj
);
}
return
memo
;
}
return
function
(
obj
,
iteratee
,
memo
,
context
)
{
iteratee
=
optimizeCb
(
iteratee
,
context
,
4
);
var
keys
=
!
isArrayLike
(
obj
)
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
,
index
=
dir
>
0
?
0
:
length
-
1
;
// Determine the initial value if none is provided.
if
(
arguments
.
length
<
3
)
{
memo
=
obj
[
keys
?
keys
[
index
]
:
index
];
index
+=
dir
;
}
return
iterator
(
obj
,
iteratee
,
memo
,
keys
,
index
,
length
);
};
}
// **Reduce** builds up a single result from a list of values, aka `inject`,
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`.
// or `foldl`.
_
.
reduce
=
_
.
foldl
=
_
.
inject
=
function
(
obj
,
iteratee
,
memo
,
context
)
{
_
.
reduce
=
_
.
foldl
=
_
.
inject
=
createReduce
(
1
);
if
(
obj
==
null
)
obj
=
[];
iteratee
=
createCallback
(
iteratee
,
context
,
4
);
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
,
index
=
0
,
currentKey
;
if
(
arguments
.
length
<
3
)
{
if
(
!
length
)
throw
new
TypeError
(
reduceError
);
memo
=
obj
[
keys
?
keys
[
index
++
]
:
index
++
];
}
for
(;
index
<
length
;
index
++
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
memo
=
iteratee
(
memo
,
obj
[
currentKey
],
currentKey
,
obj
);
}
return
memo
;
};
// The right-associative version of reduce, also known as `foldr`.
// The right-associative version of reduce, also known as `foldr`.
_
.
reduceRight
=
_
.
foldr
=
function
(
obj
,
iteratee
,
memo
,
context
)
{
_
.
reduceRight
=
_
.
foldr
=
createReduce
(
-
1
);
if
(
obj
==
null
)
obj
=
[];
iteratee
=
createCallback
(
iteratee
,
context
,
4
);
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
index
=
(
keys
||
obj
).
length
,
currentKey
;
if
(
arguments
.
length
<
3
)
{
if
(
!
index
)
throw
new
TypeError
(
reduceError
);
memo
=
obj
[
keys
?
keys
[
--
index
]
:
--
index
];
}
while
(
index
--
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
memo
=
iteratee
(
memo
,
obj
[
currentKey
],
currentKey
,
obj
);
}
return
memo
;
};
// Return the first value which passes a truth test. Aliased as `detect`.
// Return the first value which passes a truth test. Aliased as `detect`.
_
.
find
=
_
.
detect
=
function
(
obj
,
predicate
,
context
)
{
_
.
find
=
_
.
detect
=
function
(
obj
,
predicate
,
context
)
{
var
result
;
var
key
;
predicate
=
_
.
iteratee
(
predicate
,
context
);
if
(
isArrayLike
(
obj
))
{
_
.
some
(
obj
,
function
(
value
,
index
,
list
)
{
key
=
_
.
findIndex
(
obj
,
predicate
,
context
);
if
(
predicate
(
value
,
index
,
list
))
{
}
else
{
result
=
value
;
key
=
_
.
findKey
(
obj
,
predicate
,
context
);
return
true
;
}
}
if
(
key
!==
void
0
&&
key
!==
-
1
)
return
obj
[
key
];
});
return
result
;
};
};
// Return all the elements that pass a truth test.
// Return all the elements that pass a truth test.
// Aliased as `select`.
// Aliased as `select`.
_
.
filter
=
_
.
select
=
function
(
obj
,
predicate
,
context
)
{
_
.
filter
=
_
.
select
=
function
(
obj
,
predicate
,
context
)
{
var
results
=
[];
var
results
=
[];
if
(
obj
==
null
)
return
results
;
predicate
=
cb
(
predicate
,
context
);
predicate
=
_
.
iteratee
(
predicate
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
predicate
(
value
,
index
,
list
))
results
.
push
(
value
);
if
(
predicate
(
value
,
index
,
list
))
results
.
push
(
value
);
});
});
...
@@ -192,19 +231,17 @@
...
@@ -192,19 +231,17 @@
// Return all the elements for which a truth test fails.
// Return all the elements for which a truth test fails.
_
.
reject
=
function
(
obj
,
predicate
,
context
)
{
_
.
reject
=
function
(
obj
,
predicate
,
context
)
{
return
_
.
filter
(
obj
,
_
.
negate
(
_
.
iteratee
(
predicate
)),
context
);
return
_
.
filter
(
obj
,
_
.
negate
(
cb
(
predicate
)),
context
);
};
};
// Determine whether all of the elements match a truth test.
// Determine whether all of the elements match a truth test.
// Aliased as `all`.
// Aliased as `all`.
_
.
every
=
_
.
all
=
function
(
obj
,
predicate
,
context
)
{
_
.
every
=
_
.
all
=
function
(
obj
,
predicate
,
context
)
{
if
(
obj
==
null
)
return
true
;
predicate
=
cb
(
predicate
,
context
);
predicate
=
_
.
iteratee
(
predicate
,
context
);
var
keys
=
!
isArrayLike
(
obj
)
&&
_
.
keys
(
obj
),
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
;
length
=
(
keys
||
obj
).
length
,
for
(
var
index
=
0
;
index
<
length
;
index
++
)
{
index
,
currentKey
;
var
currentKey
=
keys
?
keys
[
index
]
:
index
;
for
(
index
=
0
;
index
<
length
;
index
++
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
if
(
!
predicate
(
obj
[
currentKey
],
currentKey
,
obj
))
return
false
;
if
(
!
predicate
(
obj
[
currentKey
],
currentKey
,
obj
))
return
false
;
}
}
return
true
;
return
true
;
...
@@ -213,24 +250,22 @@
...
@@ -213,24 +250,22 @@
// Determine if at least one element in the object matches a truth test.
// Determine if at least one element in the object matches a truth test.
// Aliased as `any`.
// Aliased as `any`.
_
.
some
=
_
.
any
=
function
(
obj
,
predicate
,
context
)
{
_
.
some
=
_
.
any
=
function
(
obj
,
predicate
,
context
)
{
if
(
obj
==
null
)
return
false
;
predicate
=
cb
(
predicate
,
context
);
predicate
=
_
.
iteratee
(
predicate
,
context
);
var
keys
=
!
isArrayLike
(
obj
)
&&
_
.
keys
(
obj
),
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
;
length
=
(
keys
||
obj
).
length
,
for
(
var
index
=
0
;
index
<
length
;
index
++
)
{
index
,
currentKey
;
var
currentKey
=
keys
?
keys
[
index
]
:
index
;
for
(
index
=
0
;
index
<
length
;
index
++
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
if
(
predicate
(
obj
[
currentKey
],
currentKey
,
obj
))
return
true
;
if
(
predicate
(
obj
[
currentKey
],
currentKey
,
obj
))
return
true
;
}
}
return
false
;
return
false
;
};
};
// Determine if the array or object contains a given
value
(using `===`).
// Determine if the array or object contains a given
item
(using `===`).
// Aliased as `include`.
// Aliased as `include
s` and `include
`.
_
.
contains
=
_
.
include
=
function
(
obj
,
target
)
{
_
.
contains
=
_
.
include
s
=
_
.
include
=
function
(
obj
,
item
,
fromIndex
,
guard
)
{
if
(
obj
==
null
)
return
false
;
if
(
!
isArrayLike
(
obj
))
obj
=
_
.
values
(
obj
)
;
if
(
obj
.
length
!==
+
obj
.
length
)
obj
=
_
.
values
(
obj
)
;
if
(
typeof
fromIndex
!=
'
number
'
||
guard
)
fromIndex
=
0
;
return
_
.
indexOf
(
obj
,
target
)
>=
0
;
return
_
.
indexOf
(
obj
,
item
,
fromIndex
)
>=
0
;
};
};
// Invoke a method (with arguments) on every item in a collection.
// Invoke a method (with arguments) on every item in a collection.
...
@@ -238,7 +273,8 @@
...
@@ -238,7 +273,8 @@
var
args
=
slice
.
call
(
arguments
,
2
);
var
args
=
slice
.
call
(
arguments
,
2
);
var
isFunc
=
_
.
isFunction
(
method
);
var
isFunc
=
_
.
isFunction
(
method
);
return
_
.
map
(
obj
,
function
(
value
)
{
return
_
.
map
(
obj
,
function
(
value
)
{
return
(
isFunc
?
method
:
value
[
method
]).
apply
(
value
,
args
);
var
func
=
isFunc
?
method
:
value
[
method
];
return
func
==
null
?
func
:
func
.
apply
(
value
,
args
);
});
});
};
};
...
@@ -250,13 +286,13 @@
...
@@ -250,13 +286,13 @@
// Convenience version of a common use case of `filter`: selecting only objects
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
// containing specific `key:value` pairs.
_
.
where
=
function
(
obj
,
attrs
)
{
_
.
where
=
function
(
obj
,
attrs
)
{
return
_
.
filter
(
obj
,
_
.
matche
s
(
attrs
));
return
_
.
filter
(
obj
,
_
.
matche
r
(
attrs
));
};
};
// Convenience version of a common use case of `find`: getting the first object
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
// containing specific `key:value` pairs.
_
.
findWhere
=
function
(
obj
,
attrs
)
{
_
.
findWhere
=
function
(
obj
,
attrs
)
{
return
_
.
find
(
obj
,
_
.
matche
s
(
attrs
));
return
_
.
find
(
obj
,
_
.
matche
r
(
attrs
));
};
};
// Return the maximum element (or element-based computation).
// Return the maximum element (or element-based computation).
...
@@ -264,7 +300,7 @@
...
@@ -264,7 +300,7 @@
var
result
=
-
Infinity
,
lastComputed
=
-
Infinity
,
var
result
=
-
Infinity
,
lastComputed
=
-
Infinity
,
value
,
computed
;
value
,
computed
;
if
(
iteratee
==
null
&&
obj
!=
null
)
{
if
(
iteratee
==
null
&&
obj
!=
null
)
{
obj
=
obj
.
length
===
+
obj
.
length
?
obj
:
_
.
values
(
obj
);
obj
=
isArrayLike
(
obj
)
?
obj
:
_
.
values
(
obj
);
for
(
var
i
=
0
,
length
=
obj
.
length
;
i
<
length
;
i
++
)
{
for
(
var
i
=
0
,
length
=
obj
.
length
;
i
<
length
;
i
++
)
{
value
=
obj
[
i
];
value
=
obj
[
i
];
if
(
value
>
result
)
{
if
(
value
>
result
)
{
...
@@ -272,7 +308,7 @@
...
@@ -272,7 +308,7 @@
}
}
}
}
}
else
{
}
else
{
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
iteratee
=
cb
(
iteratee
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
computed
=
iteratee
(
value
,
index
,
list
);
computed
=
iteratee
(
value
,
index
,
list
);
if
(
computed
>
lastComputed
||
computed
===
-
Infinity
&&
result
===
-
Infinity
)
{
if
(
computed
>
lastComputed
||
computed
===
-
Infinity
&&
result
===
-
Infinity
)
{
...
@@ -289,7 +325,7 @@
...
@@ -289,7 +325,7 @@
var
result
=
Infinity
,
lastComputed
=
Infinity
,
var
result
=
Infinity
,
lastComputed
=
Infinity
,
value
,
computed
;
value
,
computed
;
if
(
iteratee
==
null
&&
obj
!=
null
)
{
if
(
iteratee
==
null
&&
obj
!=
null
)
{
obj
=
obj
.
length
===
+
obj
.
length
?
obj
:
_
.
values
(
obj
);
obj
=
isArrayLike
(
obj
)
?
obj
:
_
.
values
(
obj
);
for
(
var
i
=
0
,
length
=
obj
.
length
;
i
<
length
;
i
++
)
{
for
(
var
i
=
0
,
length
=
obj
.
length
;
i
<
length
;
i
++
)
{
value
=
obj
[
i
];
value
=
obj
[
i
];
if
(
value
<
result
)
{
if
(
value
<
result
)
{
...
@@ -297,7 +333,7 @@
...
@@ -297,7 +333,7 @@
}
}
}
}
}
else
{
}
else
{
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
iteratee
=
cb
(
iteratee
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
computed
=
iteratee
(
value
,
index
,
list
);
computed
=
iteratee
(
value
,
index
,
list
);
if
(
computed
<
lastComputed
||
computed
===
Infinity
&&
result
===
Infinity
)
{
if
(
computed
<
lastComputed
||
computed
===
Infinity
&&
result
===
Infinity
)
{
...
@@ -312,7 +348,7 @@
...
@@ -312,7 +348,7 @@
// Shuffle a collection, using the modern version of the
// Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_
.
shuffle
=
function
(
obj
)
{
_
.
shuffle
=
function
(
obj
)
{
var
set
=
obj
&&
obj
.
length
===
+
obj
.
length
?
obj
:
_
.
values
(
obj
);
var
set
=
isArrayLike
(
obj
)
?
obj
:
_
.
values
(
obj
);
var
length
=
set
.
length
;
var
length
=
set
.
length
;
var
shuffled
=
Array
(
length
);
var
shuffled
=
Array
(
length
);
for
(
var
index
=
0
,
rand
;
index
<
length
;
index
++
)
{
for
(
var
index
=
0
,
rand
;
index
<
length
;
index
++
)
{
...
@@ -328,7 +364,7 @@
...
@@ -328,7 +364,7 @@
// The internal `guard` argument allows it to work with `map`.
// The internal `guard` argument allows it to work with `map`.
_
.
sample
=
function
(
obj
,
n
,
guard
)
{
_
.
sample
=
function
(
obj
,
n
,
guard
)
{
if
(
n
==
null
||
guard
)
{
if
(
n
==
null
||
guard
)
{
if
(
obj
.
length
!==
+
obj
.
length
)
obj
=
_
.
values
(
obj
);
if
(
!
isArrayLike
(
obj
)
)
obj
=
_
.
values
(
obj
);
return
obj
[
_
.
random
(
obj
.
length
-
1
)];
return
obj
[
_
.
random
(
obj
.
length
-
1
)];
}
}
return
_
.
shuffle
(
obj
).
slice
(
0
,
Math
.
max
(
0
,
n
));
return
_
.
shuffle
(
obj
).
slice
(
0
,
Math
.
max
(
0
,
n
));
...
@@ -336,7 +372,7 @@
...
@@ -336,7 +372,7 @@
// Sort the object's values by a criterion produced by an iteratee.
// Sort the object's values by a criterion produced by an iteratee.
_
.
sortBy
=
function
(
obj
,
iteratee
,
context
)
{
_
.
sortBy
=
function
(
obj
,
iteratee
,
context
)
{
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
iteratee
=
cb
(
iteratee
,
context
);
return
_
.
pluck
(
_
.
map
(
obj
,
function
(
value
,
index
,
list
)
{
return
_
.
pluck
(
_
.
map
(
obj
,
function
(
value
,
index
,
list
)
{
return
{
return
{
value
:
value
,
value
:
value
,
...
@@ -358,7 +394,7 @@
...
@@ -358,7 +394,7 @@
var
group
=
function
(
behavior
)
{
var
group
=
function
(
behavior
)
{
return
function
(
obj
,
iteratee
,
context
)
{
return
function
(
obj
,
iteratee
,
context
)
{
var
result
=
{};
var
result
=
{};
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
iteratee
=
cb
(
iteratee
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
)
{
_
.
each
(
obj
,
function
(
value
,
index
)
{
var
key
=
iteratee
(
value
,
index
,
obj
);
var
key
=
iteratee
(
value
,
index
,
obj
);
behavior
(
result
,
value
,
key
);
behavior
(
result
,
value
,
key
);
...
@@ -386,37 +422,24 @@
...
@@ -386,37 +422,24 @@
if
(
_
.
has
(
result
,
key
))
result
[
key
]
++
;
else
result
[
key
]
=
1
;
if
(
_
.
has
(
result
,
key
))
result
[
key
]
++
;
else
result
[
key
]
=
1
;
});
});
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_
.
sortedIndex
=
function
(
array
,
obj
,
iteratee
,
context
)
{
iteratee
=
_
.
iteratee
(
iteratee
,
context
,
1
);
var
value
=
iteratee
(
obj
);
var
low
=
0
,
high
=
array
.
length
;
while
(
low
<
high
)
{
var
mid
=
low
+
high
>>>
1
;
if
(
iteratee
(
array
[
mid
])
<
value
)
low
=
mid
+
1
;
else
high
=
mid
;
}
return
low
;
};
// Safely create a real, live array from anything iterable.
// Safely create a real, live array from anything iterable.
_
.
toArray
=
function
(
obj
)
{
_
.
toArray
=
function
(
obj
)
{
if
(
!
obj
)
return
[];
if
(
!
obj
)
return
[];
if
(
_
.
isArray
(
obj
))
return
slice
.
call
(
obj
);
if
(
_
.
isArray
(
obj
))
return
slice
.
call
(
obj
);
if
(
obj
.
length
===
+
obj
.
length
)
return
_
.
map
(
obj
,
_
.
identity
);
if
(
isArrayLike
(
obj
)
)
return
_
.
map
(
obj
,
_
.
identity
);
return
_
.
values
(
obj
);
return
_
.
values
(
obj
);
};
};
// Return the number of elements in an object.
// Return the number of elements in an object.
_
.
size
=
function
(
obj
)
{
_
.
size
=
function
(
obj
)
{
if
(
obj
==
null
)
return
0
;
if
(
obj
==
null
)
return
0
;
return
obj
.
length
===
+
obj
.
length
?
obj
.
length
:
_
.
keys
(
obj
).
length
;
return
isArrayLike
(
obj
)
?
obj
.
length
:
_
.
keys
(
obj
).
length
;
};
};
// Split a collection into two arrays: one whose elements all satisfy the given
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
// predicate, and one whose elements all do not satisfy the predicate.
_
.
partition
=
function
(
obj
,
predicate
,
context
)
{
_
.
partition
=
function
(
obj
,
predicate
,
context
)
{
predicate
=
_
.
iteratee
(
predicate
,
context
);
predicate
=
cb
(
predicate
,
context
);
var
pass
=
[],
fail
=
[];
var
pass
=
[],
fail
=
[];
_
.
each
(
obj
,
function
(
value
,
key
,
obj
)
{
_
.
each
(
obj
,
function
(
value
,
key
,
obj
)
{
(
predicate
(
value
,
key
,
obj
)
?
pass
:
fail
).
push
(
value
);
(
predicate
(
value
,
key
,
obj
)
?
pass
:
fail
).
push
(
value
);
...
@@ -433,30 +456,27 @@
...
@@ -433,30 +456,27 @@
_
.
first
=
_
.
head
=
_
.
take
=
function
(
array
,
n
,
guard
)
{
_
.
first
=
_
.
head
=
_
.
take
=
function
(
array
,
n
,
guard
)
{
if
(
array
==
null
)
return
void
0
;
if
(
array
==
null
)
return
void
0
;
if
(
n
==
null
||
guard
)
return
array
[
0
];
if
(
n
==
null
||
guard
)
return
array
[
0
];
if
(
n
<
0
)
return
[];
return
_
.
initial
(
array
,
array
.
length
-
n
);
return
slice
.
call
(
array
,
0
,
n
);
};
};
// Returns everything but the last entry of the array. Especially useful on
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// the array, excluding the last N.
// `_.map`.
_
.
initial
=
function
(
array
,
n
,
guard
)
{
_
.
initial
=
function
(
array
,
n
,
guard
)
{
return
slice
.
call
(
array
,
0
,
Math
.
max
(
0
,
array
.
length
-
(
n
==
null
||
guard
?
1
:
n
)));
return
slice
.
call
(
array
,
0
,
Math
.
max
(
0
,
array
.
length
-
(
n
==
null
||
guard
?
1
:
n
)));
};
};
// Get the last element of an array. Passing **n** will return the last N
// Get the last element of an array. Passing **n** will return the last N
// values in the array.
The **guard** check allows it to work with `_.map`.
// values in the array.
_
.
last
=
function
(
array
,
n
,
guard
)
{
_
.
last
=
function
(
array
,
n
,
guard
)
{
if
(
array
==
null
)
return
void
0
;
if
(
array
==
null
)
return
void
0
;
if
(
n
==
null
||
guard
)
return
array
[
array
.
length
-
1
];
if
(
n
==
null
||
guard
)
return
array
[
array
.
length
-
1
];
return
slice
.
call
(
array
,
Math
.
max
(
array
.
length
-
n
,
0
));
return
_
.
rest
(
array
,
Math
.
max
(
0
,
array
.
length
-
n
));
};
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// the rest N values in the array.
// check allows it to work with `_.map`.
_
.
rest
=
_
.
tail
=
_
.
drop
=
function
(
array
,
n
,
guard
)
{
_
.
rest
=
_
.
tail
=
_
.
drop
=
function
(
array
,
n
,
guard
)
{
return
slice
.
call
(
array
,
n
==
null
||
guard
?
1
:
n
);
return
slice
.
call
(
array
,
n
==
null
||
guard
?
1
:
n
);
};
};
...
@@ -467,18 +487,20 @@
...
@@ -467,18 +487,20 @@
};
};
// Internal implementation of a recursive `flatten` function.
// Internal implementation of a recursive `flatten` function.
var
flatten
=
function
(
input
,
shallow
,
strict
,
output
)
{
var
flatten
=
function
(
input
,
shallow
,
strict
,
startIndex
)
{
if
(
shallow
&&
_
.
every
(
input
,
_
.
isArray
))
{
var
output
=
[],
idx
=
0
;
return
concat
.
apply
(
output
,
input
);
for
(
var
i
=
startIndex
||
0
,
length
=
getLength
(
input
);
i
<
length
;
i
++
)
{
}
for
(
var
i
=
0
,
length
=
input
.
length
;
i
<
length
;
i
++
)
{
var
value
=
input
[
i
];
var
value
=
input
[
i
];
if
(
!
_
.
isArray
(
value
)
&&
!
_
.
isArguments
(
value
))
{
if
(
isArrayLike
(
value
)
&&
(
_
.
isArray
(
value
)
||
_
.
isArguments
(
value
)))
{
if
(
!
strict
)
output
.
push
(
value
);
//flatten current level of array or arguments object
}
else
if
(
shallow
)
{
if
(
!
shallow
)
value
=
flatten
(
value
,
shallow
,
strict
);
push
.
apply
(
output
,
value
);
var
j
=
0
,
len
=
value
.
length
;
}
else
{
output
.
length
+=
len
;
flatten
(
value
,
shallow
,
strict
,
output
);
while
(
j
<
len
)
{
output
[
idx
++
]
=
value
[
j
++
];
}
}
else
if
(
!
strict
)
{
output
[
idx
++
]
=
value
;
}
}
}
}
return
output
;
return
output
;
...
@@ -486,7 +508,7 @@
...
@@ -486,7 +508,7 @@
// Flatten out an array, either recursively (by default), or just one level.
// Flatten out an array, either recursively (by default), or just one level.
_
.
flatten
=
function
(
array
,
shallow
)
{
_
.
flatten
=
function
(
array
,
shallow
)
{
return
flatten
(
array
,
shallow
,
false
,
[]
);
return
flatten
(
array
,
shallow
,
false
);
};
};
// Return a version of the array that does not contain the specified value(s).
// Return a version of the array that does not contain the specified value(s).
...
@@ -498,27 +520,26 @@
...
@@ -498,27 +520,26 @@
// been sorted, you have the option of using a faster algorithm.
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
// Aliased as `unique`.
_
.
uniq
=
_
.
unique
=
function
(
array
,
isSorted
,
iteratee
,
context
)
{
_
.
uniq
=
_
.
unique
=
function
(
array
,
isSorted
,
iteratee
,
context
)
{
if
(
array
==
null
)
return
[];
if
(
!
_
.
isBoolean
(
isSorted
))
{
if
(
!
_
.
isBoolean
(
isSorted
))
{
context
=
iteratee
;
context
=
iteratee
;
iteratee
=
isSorted
;
iteratee
=
isSorted
;
isSorted
=
false
;
isSorted
=
false
;
}
}
if
(
iteratee
!=
null
)
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
if
(
iteratee
!=
null
)
iteratee
=
cb
(
iteratee
,
context
);
var
result
=
[];
var
result
=
[];
var
seen
=
[];
var
seen
=
[];
for
(
var
i
=
0
,
length
=
array
.
length
;
i
<
length
;
i
++
)
{
for
(
var
i
=
0
,
length
=
getLength
(
array
);
i
<
length
;
i
++
)
{
var
value
=
array
[
i
];
var
value
=
array
[
i
],
computed
=
iteratee
?
iteratee
(
value
,
i
,
array
)
:
value
;
if
(
isSorted
)
{
if
(
isSorted
)
{
if
(
!
i
||
seen
!==
value
)
result
.
push
(
value
);
if
(
!
i
||
seen
!==
computed
)
result
.
push
(
value
);
seen
=
value
;
seen
=
computed
;
}
else
if
(
iteratee
)
{
}
else
if
(
iteratee
)
{
var
computed
=
iteratee
(
value
,
i
,
array
);
if
(
!
_
.
contains
(
seen
,
computed
))
{
if
(
_
.
indexOf
(
seen
,
computed
)
<
0
)
{
seen
.
push
(
computed
);
seen
.
push
(
computed
);
result
.
push
(
value
);
result
.
push
(
value
);
}
}
}
else
if
(
_
.
indexOf
(
result
,
value
)
<
0
)
{
}
else
if
(
!
_
.
contains
(
result
,
value
)
)
{
result
.
push
(
value
);
result
.
push
(
value
);
}
}
}
}
...
@@ -528,16 +549,15 @@
...
@@ -528,16 +549,15 @@
// Produce an array that contains the union: each distinct element from all of
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
// the passed-in arrays.
_
.
union
=
function
()
{
_
.
union
=
function
()
{
return
_
.
uniq
(
flatten
(
arguments
,
true
,
true
,
[]
));
return
_
.
uniq
(
flatten
(
arguments
,
true
,
true
));
};
};
// Produce an array that contains every item shared between all the
// Produce an array that contains every item shared between all the
// passed-in arrays.
// passed-in arrays.
_
.
intersection
=
function
(
array
)
{
_
.
intersection
=
function
(
array
)
{
if
(
array
==
null
)
return
[];
var
result
=
[];
var
result
=
[];
var
argsLength
=
arguments
.
length
;
var
argsLength
=
arguments
.
length
;
for
(
var
i
=
0
,
length
=
array
.
length
;
i
<
length
;
i
++
)
{
for
(
var
i
=
0
,
length
=
getLength
(
array
)
;
i
<
length
;
i
++
)
{
var
item
=
array
[
i
];
var
item
=
array
[
i
];
if
(
_
.
contains
(
result
,
item
))
continue
;
if
(
_
.
contains
(
result
,
item
))
continue
;
for
(
var
j
=
1
;
j
<
argsLength
;
j
++
)
{
for
(
var
j
=
1
;
j
<
argsLength
;
j
++
)
{
...
@@ -551,7 +571,7 @@
...
@@ -551,7 +571,7 @@
// Take the difference between one array and a number of other arrays.
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
// Only the elements present in just the first array will remain.
_
.
difference
=
function
(
array
)
{
_
.
difference
=
function
(
array
)
{
var
rest
=
flatten
(
slice
.
call
(
arguments
,
1
),
true
,
true
,
[]
);
var
rest
=
flatten
(
arguments
,
true
,
true
,
1
);
return
_
.
filter
(
array
,
function
(
value
){
return
_
.
filter
(
array
,
function
(
value
){
return
!
_
.
contains
(
rest
,
value
);
return
!
_
.
contains
(
rest
,
value
);
});
});
...
@@ -559,23 +579,28 @@
...
@@ -559,23 +579,28 @@
// Zip together multiple lists into a single array -- elements that share
// Zip together multiple lists into a single array -- elements that share
// an index go together.
// an index go together.
_
.
zip
=
function
(
array
)
{
_
.
zip
=
function
()
{
if
(
array
==
null
)
return
[];
return
_
.
unzip
(
arguments
);
var
length
=
_
.
max
(
arguments
,
'
length
'
).
length
;
};
var
results
=
Array
(
length
);
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
// Complement of _.zip. Unzip accepts an array of arrays and groups
results
[
i
]
=
_
.
pluck
(
arguments
,
i
);
// each array's elements on shared indices
_
.
unzip
=
function
(
array
)
{
var
length
=
array
&&
_
.
max
(
array
,
getLength
).
length
||
0
;
var
result
=
Array
(
length
);
for
(
var
index
=
0
;
index
<
length
;
index
++
)
{
result
[
index
]
=
_
.
pluck
(
array
,
index
);
}
}
return
result
s
;
return
result
;
};
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
// the corresponding values.
_
.
object
=
function
(
list
,
values
)
{
_
.
object
=
function
(
list
,
values
)
{
if
(
list
==
null
)
return
{};
var
result
=
{};
var
result
=
{};
for
(
var
i
=
0
,
length
=
list
.
length
;
i
<
length
;
i
++
)
{
for
(
var
i
=
0
,
length
=
getLength
(
list
)
;
i
<
length
;
i
++
)
{
if
(
values
)
{
if
(
values
)
{
result
[
list
[
i
]]
=
values
[
i
];
result
[
list
[
i
]]
=
values
[
i
];
}
else
{
}
else
{
...
@@ -585,40 +610,73 @@
...
@@ -585,40 +610,73 @@
return
result
;
return
result
;
};
};
// Return the position of the first occurrence of an item in an array,
// Generator function to create the findIndex and findLastIndex functions
// or -1 if the item is not included in the array.
function
createPredicateIndexFinder
(
dir
)
{
// If the array is large and already in sort order, pass `true`
return
function
(
array
,
predicate
,
context
)
{
// for **isSorted** to use binary search.
predicate
=
cb
(
predicate
,
context
);
_
.
indexOf
=
function
(
array
,
item
,
isSorted
)
{
var
length
=
getLength
(
array
);
if
(
array
==
null
)
return
-
1
;
var
index
=
dir
>
0
?
0
:
length
-
1
;
var
i
=
0
,
length
=
array
.
length
;
for
(;
index
>=
0
&&
index
<
length
;
index
+=
dir
)
{
if
(
isSorted
)
{
if
(
predicate
(
array
[
index
],
index
,
array
))
return
index
;
if
(
typeof
isSorted
==
'
number
'
)
{
i
=
isSorted
<
0
?
Math
.
max
(
0
,
length
+
isSorted
)
:
isSorted
;
}
else
{
i
=
_
.
sortedIndex
(
array
,
item
);
return
array
[
i
]
===
item
?
i
:
-
1
;
}
}
}
return
-
1
;
for
(;
i
<
length
;
i
++
)
if
(
array
[
i
]
===
item
)
return
i
;
};
return
-
1
;
}
};
// Returns the first index on an array-like that passes a predicate test
_
.
findIndex
=
createPredicateIndexFinder
(
1
);
_
.
findLastIndex
=
createPredicateIndexFinder
(
-
1
);
_
.
lastIndexOf
=
function
(
array
,
item
,
from
)
{
// Use a comparator function to figure out the smallest index at which
if
(
array
==
null
)
return
-
1
;
// an object should be inserted so as to maintain order. Uses binary search.
var
idx
=
array
.
length
;
_
.
sortedIndex
=
function
(
array
,
obj
,
iteratee
,
context
)
{
if
(
typeof
from
==
'
number
'
)
{
iteratee
=
cb
(
iteratee
,
context
,
1
);
idx
=
from
<
0
?
idx
+
from
+
1
:
Math
.
min
(
idx
,
from
+
1
);
var
value
=
iteratee
(
obj
);
var
low
=
0
,
high
=
getLength
(
array
);
while
(
low
<
high
)
{
var
mid
=
Math
.
floor
((
low
+
high
)
/
2
);
if
(
iteratee
(
array
[
mid
])
<
value
)
low
=
mid
+
1
;
else
high
=
mid
;
}
}
while
(
--
idx
>=
0
)
if
(
array
[
idx
]
===
item
)
return
idx
;
return
low
;
return
-
1
;
};
};
// Generator function to create the indexOf and lastIndexOf functions
function
createIndexFinder
(
dir
,
predicateFind
,
sortedIndex
)
{
return
function
(
array
,
item
,
idx
)
{
var
i
=
0
,
length
=
getLength
(
array
);
if
(
typeof
idx
==
'
number
'
)
{
if
(
dir
>
0
)
{
i
=
idx
>=
0
?
idx
:
Math
.
max
(
idx
+
length
,
i
);
}
else
{
length
=
idx
>=
0
?
Math
.
min
(
idx
+
1
,
length
)
:
idx
+
length
+
1
;
}
}
else
if
(
sortedIndex
&&
idx
&&
length
)
{
idx
=
sortedIndex
(
array
,
item
);
return
array
[
idx
]
===
item
?
idx
:
-
1
;
}
if
(
item
!==
item
)
{
idx
=
predicateFind
(
slice
.
call
(
array
,
i
,
length
),
_
.
isNaN
);
return
idx
>=
0
?
idx
+
i
:
-
1
;
}
for
(
idx
=
dir
>
0
?
i
:
length
-
1
;
idx
>=
0
&&
idx
<
length
;
idx
+=
dir
)
{
if
(
array
[
idx
]
===
item
)
return
idx
;
}
return
-
1
;
};
}
// Return the position of the first occurrence of an item in an array,
// or -1 if the item is not included in the array.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_
.
indexOf
=
createIndexFinder
(
1
,
_
.
findIndex
,
_
.
sortedIndex
);
_
.
lastIndexOf
=
createIndexFinder
(
-
1
,
_
.
findLastIndex
);
// Generate an integer Array containing an arithmetic progression. A port of
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_
.
range
=
function
(
start
,
stop
,
step
)
{
_
.
range
=
function
(
start
,
stop
,
step
)
{
if
(
arguments
.
length
<=
1
)
{
if
(
stop
==
null
)
{
stop
=
start
||
0
;
stop
=
start
||
0
;
start
=
0
;
start
=
0
;
}
}
...
@@ -637,25 +695,25 @@
...
@@ -637,25 +695,25 @@
// Function (ahem) Functions
// Function (ahem) Functions
// ------------------
// ------------------
// Reusable constructor function for prototype setting.
// Determines whether to execute a function as a constructor
var
Ctor
=
function
(){};
// or a normal function with the provided arguments
var
executeBound
=
function
(
sourceFunc
,
boundFunc
,
context
,
callingContext
,
args
)
{
if
(
!
(
callingContext
instanceof
boundFunc
))
return
sourceFunc
.
apply
(
context
,
args
);
var
self
=
baseCreate
(
sourceFunc
.
prototype
);
var
result
=
sourceFunc
.
apply
(
self
,
args
);
if
(
_
.
isObject
(
result
))
return
result
;
return
self
;
};
// Create a function bound to a given object (assigning `this`, and arguments,
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
// available.
_
.
bind
=
function
(
func
,
context
)
{
_
.
bind
=
function
(
func
,
context
)
{
var
args
,
bound
;
if
(
nativeBind
&&
func
.
bind
===
nativeBind
)
return
nativeBind
.
apply
(
func
,
slice
.
call
(
arguments
,
1
));
if
(
nativeBind
&&
func
.
bind
===
nativeBind
)
return
nativeBind
.
apply
(
func
,
slice
.
call
(
arguments
,
1
));
if
(
!
_
.
isFunction
(
func
))
throw
new
TypeError
(
'
Bind must be called on a function
'
);
if
(
!
_
.
isFunction
(
func
))
throw
new
TypeError
(
'
Bind must be called on a function
'
);
args
=
slice
.
call
(
arguments
,
2
);
var
args
=
slice
.
call
(
arguments
,
2
);
bound
=
function
()
{
var
bound
=
function
()
{
if
(
!
(
this
instanceof
bound
))
return
func
.
apply
(
context
,
args
.
concat
(
slice
.
call
(
arguments
)));
return
executeBound
(
func
,
bound
,
context
,
this
,
args
.
concat
(
slice
.
call
(
arguments
)));
Ctor
.
prototype
=
func
.
prototype
;
var
self
=
new
Ctor
;
Ctor
.
prototype
=
null
;
var
result
=
func
.
apply
(
self
,
args
.
concat
(
slice
.
call
(
arguments
)));
if
(
_
.
isObject
(
result
))
return
result
;
return
self
;
};
};
return
bound
;
return
bound
;
};
};
...
@@ -665,15 +723,16 @@
...
@@ -665,15 +723,16 @@
// as a placeholder, allowing any combination of arguments to be pre-filled.
// as a placeholder, allowing any combination of arguments to be pre-filled.
_
.
partial
=
function
(
func
)
{
_
.
partial
=
function
(
func
)
{
var
boundArgs
=
slice
.
call
(
arguments
,
1
);
var
boundArgs
=
slice
.
call
(
arguments
,
1
);
return
function
()
{
var
bound
=
function
()
{
var
position
=
0
;
var
position
=
0
,
length
=
boundArgs
.
length
;
var
args
=
boundArgs
.
slice
(
);
var
args
=
Array
(
length
);
for
(
var
i
=
0
,
length
=
args
.
length
;
i
<
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
if
(
args
[
i
]
===
_
)
args
[
i
]
=
arguments
[
position
++
];
args
[
i
]
=
boundArgs
[
i
]
===
_
?
arguments
[
position
++
]
:
boundArgs
[
i
];
}
}
while
(
position
<
arguments
.
length
)
args
.
push
(
arguments
[
position
++
]);
while
(
position
<
arguments
.
length
)
args
.
push
(
arguments
[
position
++
]);
return
func
.
apply
(
this
,
args
);
return
executeBound
(
func
,
bound
,
this
,
this
,
args
);
};
};
return
bound
;
};
};
// Bind a number of an object's methods to that object. Remaining arguments
// Bind a number of an object's methods to that object. Remaining arguments
...
@@ -693,7 +752,7 @@
...
@@ -693,7 +752,7 @@
_
.
memoize
=
function
(
func
,
hasher
)
{
_
.
memoize
=
function
(
func
,
hasher
)
{
var
memoize
=
function
(
key
)
{
var
memoize
=
function
(
key
)
{
var
cache
=
memoize
.
cache
;
var
cache
=
memoize
.
cache
;
var
address
=
hasher
?
hasher
.
apply
(
this
,
arguments
)
:
key
;
var
address
=
''
+
(
hasher
?
hasher
.
apply
(
this
,
arguments
)
:
key
)
;
if
(
!
_
.
has
(
cache
,
address
))
cache
[
address
]
=
func
.
apply
(
this
,
arguments
);
if
(
!
_
.
has
(
cache
,
address
))
cache
[
address
]
=
func
.
apply
(
this
,
arguments
);
return
cache
[
address
];
return
cache
[
address
];
};
};
...
@@ -712,9 +771,7 @@
...
@@ -712,9 +771,7 @@
// Defers a function, scheduling it to run after the current call stack has
// Defers a function, scheduling it to run after the current call stack has
// cleared.
// cleared.
_
.
defer
=
function
(
func
)
{
_
.
defer
=
_
.
partial
(
_
.
delay
,
_
,
1
);
return
_
.
delay
.
apply
(
_
,
[
func
,
1
].
concat
(
slice
.
call
(
arguments
,
1
)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// during a given window of time. Normally, the throttled function will run
...
@@ -739,8 +796,10 @@
...
@@ -739,8 +796,10 @@
context
=
this
;
context
=
this
;
args
=
arguments
;
args
=
arguments
;
if
(
remaining
<=
0
||
remaining
>
wait
)
{
if
(
remaining
<=
0
||
remaining
>
wait
)
{
clearTimeout
(
timeout
);
if
(
timeout
)
{
timeout
=
null
;
clearTimeout
(
timeout
);
timeout
=
null
;
}
previous
=
now
;
previous
=
now
;
result
=
func
.
apply
(
context
,
args
);
result
=
func
.
apply
(
context
,
args
);
if
(
!
timeout
)
context
=
args
=
null
;
if
(
!
timeout
)
context
=
args
=
null
;
...
@@ -761,7 +820,7 @@
...
@@ -761,7 +820,7 @@
var
later
=
function
()
{
var
later
=
function
()
{
var
last
=
_
.
now
()
-
timestamp
;
var
last
=
_
.
now
()
-
timestamp
;
if
(
last
<
wait
&&
last
>
0
)
{
if
(
last
<
wait
&&
last
>
=
0
)
{
timeout
=
setTimeout
(
later
,
wait
-
last
);
timeout
=
setTimeout
(
later
,
wait
-
last
);
}
else
{
}
else
{
timeout
=
null
;
timeout
=
null
;
...
@@ -814,7 +873,7 @@
...
@@ -814,7 +873,7 @@
};
};
};
};
// Returns a function that will only be executed
after being called N times
.
// Returns a function that will only be executed
on and after the Nth call
.
_
.
after
=
function
(
times
,
func
)
{
_
.
after
=
function
(
times
,
func
)
{
return
function
()
{
return
function
()
{
if
(
--
times
<
1
)
{
if
(
--
times
<
1
)
{
...
@@ -823,15 +882,14 @@
...
@@ -823,15 +882,14 @@
};
};
};
};
// Returns a function that will only be executed
before being called N times
.
// Returns a function that will only be executed
up to (but not including) the Nth call
.
_
.
before
=
function
(
times
,
func
)
{
_
.
before
=
function
(
times
,
func
)
{
var
memo
;
var
memo
;
return
function
()
{
return
function
()
{
if
(
--
times
>
0
)
{
if
(
--
times
>
0
)
{
memo
=
func
.
apply
(
this
,
arguments
);
memo
=
func
.
apply
(
this
,
arguments
);
}
else
{
func
=
null
;
}
}
if
(
times
<=
1
)
func
=
null
;
return
memo
;
return
memo
;
};
};
};
};
...
@@ -843,13 +901,47 @@
...
@@ -843,13 +901,47 @@
// Object Functions
// Object Functions
// ----------------
// ----------------
// Retrieve the names of an object's properties.
// Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
var
hasEnumBug
=
!
{
toString
:
null
}.
propertyIsEnumerable
(
'
toString
'
);
var
nonEnumerableProps
=
[
'
valueOf
'
,
'
isPrototypeOf
'
,
'
toString
'
,
'
propertyIsEnumerable
'
,
'
hasOwnProperty
'
,
'
toLocaleString
'
];
function
collectNonEnumProps
(
obj
,
keys
)
{
var
nonEnumIdx
=
nonEnumerableProps
.
length
;
var
constructor
=
obj
.
constructor
;
var
proto
=
(
_
.
isFunction
(
constructor
)
&&
constructor
.
prototype
)
||
ObjProto
;
// Constructor is a special case.
var
prop
=
'
constructor
'
;
if
(
_
.
has
(
obj
,
prop
)
&&
!
_
.
contains
(
keys
,
prop
))
keys
.
push
(
prop
);
while
(
nonEnumIdx
--
)
{
prop
=
nonEnumerableProps
[
nonEnumIdx
];
if
(
prop
in
obj
&&
obj
[
prop
]
!==
proto
[
prop
]
&&
!
_
.
contains
(
keys
,
prop
))
{
keys
.
push
(
prop
);
}
}
}
// Retrieve the names of an object's own properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
// Delegates to **ECMAScript 5**'s native `Object.keys`
_
.
keys
=
function
(
obj
)
{
_
.
keys
=
function
(
obj
)
{
if
(
!
_
.
isObject
(
obj
))
return
[];
if
(
!
_
.
isObject
(
obj
))
return
[];
if
(
nativeKeys
)
return
nativeKeys
(
obj
);
if
(
nativeKeys
)
return
nativeKeys
(
obj
);
var
keys
=
[];
var
keys
=
[];
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
keys
.
push
(
key
);
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
keys
.
push
(
key
);
// Ahem, IE < 9.
if
(
hasEnumBug
)
collectNonEnumProps
(
obj
,
keys
);
return
keys
;
};
// Retrieve all the property names of an object.
_
.
allKeys
=
function
(
obj
)
{
if
(
!
_
.
isObject
(
obj
))
return
[];
var
keys
=
[];
for
(
var
key
in
obj
)
keys
.
push
(
key
);
// Ahem, IE < 9.
if
(
hasEnumBug
)
collectNonEnumProps
(
obj
,
keys
);
return
keys
;
return
keys
;
};
};
...
@@ -864,6 +956,21 @@
...
@@ -864,6 +956,21 @@
return
values
;
return
values
;
};
};
// Returns the results of applying the iteratee to each element of the object
// In contrast to _.map it returns an object
_
.
mapObject
=
function
(
obj
,
iteratee
,
context
)
{
iteratee
=
cb
(
iteratee
,
context
);
var
keys
=
_
.
keys
(
obj
),
length
=
keys
.
length
,
results
=
{},
currentKey
;
for
(
var
index
=
0
;
index
<
length
;
index
++
)
{
currentKey
=
keys
[
index
];
results
[
currentKey
]
=
iteratee
(
obj
[
currentKey
],
currentKey
,
obj
);
}
return
results
;
};
// Convert an object into a list of `[key, value]` pairs.
// Convert an object into a list of `[key, value]` pairs.
_
.
pairs
=
function
(
obj
)
{
_
.
pairs
=
function
(
obj
)
{
var
keys
=
_
.
keys
(
obj
);
var
keys
=
_
.
keys
(
obj
);
...
@@ -896,37 +1003,38 @@
...
@@ -896,37 +1003,38 @@
};
};
// Extend a given object with all the properties in passed-in object(s).
// Extend a given object with all the properties in passed-in object(s).
_
.
extend
=
function
(
obj
)
{
_
.
extend
=
createAssigner
(
_
.
allKeys
);
if
(
!
_
.
isObject
(
obj
))
return
obj
;
var
source
,
prop
;
// Assigns a given object with all the own properties in the passed-in object(s)
for
(
var
i
=
1
,
length
=
arguments
.
length
;
i
<
length
;
i
++
)
{
// (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
source
=
arguments
[
i
];
_
.
extendOwn
=
_
.
assign
=
createAssigner
(
_
.
keys
);
for
(
prop
in
source
)
{
if
(
hasOwnProperty
.
call
(
source
,
prop
))
{
// Returns the first key on an object that passes a predicate test
obj
[
prop
]
=
source
[
prop
];
_
.
findKey
=
function
(
obj
,
predicate
,
context
)
{
}
predicate
=
cb
(
predicate
,
context
);
}
var
keys
=
_
.
keys
(
obj
),
key
;
for
(
var
i
=
0
,
length
=
keys
.
length
;
i
<
length
;
i
++
)
{
key
=
keys
[
i
];
if
(
predicate
(
obj
[
key
],
key
,
obj
))
return
key
;
}
}
return
obj
;
};
};
// Return a copy of the object only containing the whitelisted properties.
// Return a copy of the object only containing the whitelisted properties.
_
.
pick
=
function
(
obj
,
iteratee
,
context
)
{
_
.
pick
=
function
(
obj
ect
,
o
iteratee
,
context
)
{
var
result
=
{},
key
;
var
result
=
{},
obj
=
object
,
iteratee
,
keys
;
if
(
obj
==
null
)
return
result
;
if
(
obj
==
null
)
return
result
;
if
(
_
.
isFunction
(
iteratee
))
{
if
(
_
.
isFunction
(
oiteratee
))
{
iteratee
=
createCallback
(
iteratee
,
context
);
keys
=
_
.
allKeys
(
obj
);
for
(
key
in
obj
)
{
iteratee
=
optimizeCb
(
oiteratee
,
context
);
var
value
=
obj
[
key
];
if
(
iteratee
(
value
,
key
,
obj
))
result
[
key
]
=
value
;
}
}
else
{
}
else
{
var
keys
=
concat
.
apply
([],
slice
.
call
(
arguments
,
1
));
keys
=
flatten
(
arguments
,
false
,
false
,
1
);
obj
=
new
Object
(
obj
);
iteratee
=
function
(
value
,
key
,
obj
)
{
return
key
in
obj
;
};
for
(
var
i
=
0
,
length
=
keys
.
length
;
i
<
length
;
i
++
)
{
obj
=
Object
(
obj
);
key
=
keys
[
i
];
}
if
(
key
in
obj
)
result
[
key
]
=
obj
[
key
];
for
(
var
i
=
0
,
length
=
keys
.
length
;
i
<
length
;
i
++
)
{
}
var
key
=
keys
[
i
];
var
value
=
obj
[
key
];
if
(
iteratee
(
value
,
key
,
obj
))
result
[
key
]
=
value
;
}
}
return
result
;
return
result
;
};
};
...
@@ -936,7 +1044,7 @@
...
@@ -936,7 +1044,7 @@
if
(
_
.
isFunction
(
iteratee
))
{
if
(
_
.
isFunction
(
iteratee
))
{
iteratee
=
_
.
negate
(
iteratee
);
iteratee
=
_
.
negate
(
iteratee
);
}
else
{
}
else
{
var
keys
=
_
.
map
(
concat
.
apply
([],
slice
.
call
(
arguments
,
1
)
),
String
);
var
keys
=
_
.
map
(
flatten
(
arguments
,
false
,
false
,
1
),
String
);
iteratee
=
function
(
value
,
key
)
{
iteratee
=
function
(
value
,
key
)
{
return
!
_
.
contains
(
keys
,
key
);
return
!
_
.
contains
(
keys
,
key
);
};
};
...
@@ -945,15 +1053,15 @@
...
@@ -945,15 +1053,15 @@
};
};
// Fill in a given object with default properties.
// Fill in a given object with default properties.
_
.
defaults
=
function
(
obj
)
{
_
.
defaults
=
createAssigner
(
_
.
allKeys
,
true
);
if
(
!
_
.
isObject
(
obj
))
return
obj
;
for
(
var
i
=
1
,
length
=
arguments
.
length
;
i
<
length
;
i
++
)
{
// Creates an object that inherits from the given prototype object.
var
source
=
arguments
[
i
];
// If additional properties are provided then they will be added to the
for
(
var
prop
in
source
)
{
// created object.
if
(
obj
[
prop
]
===
void
0
)
obj
[
prop
]
=
source
[
prop
];
_
.
create
=
function
(
prototype
,
props
)
{
}
var
result
=
baseCreate
(
prototype
);
}
if
(
props
)
_
.
extendOwn
(
result
,
props
);
return
obj
;
return
result
;
};
};
// Create a (shallow-cloned) duplicate of an object.
// Create a (shallow-cloned) duplicate of an object.
...
@@ -970,6 +1078,19 @@
...
@@ -970,6 +1078,19 @@
return
obj
;
return
obj
;
};
};
// Returns whether an object has a given set of `key:value` pairs.
_
.
isMatch
=
function
(
object
,
attrs
)
{
var
keys
=
_
.
keys
(
attrs
),
length
=
keys
.
length
;
if
(
object
==
null
)
return
!
length
;
var
obj
=
Object
(
object
);
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
var
key
=
keys
[
i
];
if
(
attrs
[
key
]
!==
obj
[
key
]
||
!
(
key
in
obj
))
return
false
;
}
return
true
;
};
// Internal recursive comparison function for `isEqual`.
// Internal recursive comparison function for `isEqual`.
var
eq
=
function
(
a
,
b
,
aStack
,
bStack
)
{
var
eq
=
function
(
a
,
b
,
aStack
,
bStack
)
{
// Identical objects are equal. `0 === -0`, but they aren't identical.
// Identical objects are equal. `0 === -0`, but they aren't identical.
...
@@ -1004,74 +1125,76 @@
...
@@ -1004,74 +1125,76 @@
// of `NaN` are not equivalent.
// of `NaN` are not equivalent.
return
+
a
===
+
b
;
return
+
a
===
+
b
;
}
}
if
(
typeof
a
!=
'
object
'
||
typeof
b
!=
'
object
'
)
return
false
;
var
areArrays
=
className
===
'
[object Array]
'
;
if
(
!
areArrays
)
{
if
(
typeof
a
!=
'
object
'
||
typeof
b
!=
'
object
'
)
return
false
;
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
// from different frames are.
var
aCtor
=
a
.
constructor
,
bCtor
=
b
.
constructor
;
if
(
aCtor
!==
bCtor
&&
!
(
_
.
isFunction
(
aCtor
)
&&
aCtor
instanceof
aCtor
&&
_
.
isFunction
(
bCtor
)
&&
bCtor
instanceof
bCtor
)
&&
(
'
constructor
'
in
a
&&
'
constructor
'
in
b
))
{
return
false
;
}
}
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// Initializing stack of traversed objects.
// It's done here since we only need them for objects and arrays comparison.
aStack
=
aStack
||
[];
bStack
=
bStack
||
[];
var
length
=
aStack
.
length
;
var
length
=
aStack
.
length
;
while
(
length
--
)
{
while
(
length
--
)
{
// Linear search. Performance is inversely proportional to the number of
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
// unique nested structures.
if
(
aStack
[
length
]
===
a
)
return
bStack
[
length
]
===
b
;
if
(
aStack
[
length
]
===
a
)
return
bStack
[
length
]
===
b
;
}
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var
aCtor
=
a
.
constructor
,
bCtor
=
b
.
constructor
;
if
(
aCtor
!==
bCtor
&&
// Handle Object.create(x) cases
'
constructor
'
in
a
&&
'
constructor
'
in
b
&&
!
(
_
.
isFunction
(
aCtor
)
&&
aCtor
instanceof
aCtor
&&
_
.
isFunction
(
bCtor
)
&&
bCtor
instanceof
bCtor
)
)
{
return
false
;
}
// Add the first object to the stack of traversed objects.
// Add the first object to the stack of traversed objects.
aStack
.
push
(
a
);
aStack
.
push
(
a
);
bStack
.
push
(
b
);
bStack
.
push
(
b
);
var
size
,
result
;
// Recursively compare objects and arrays.
// Recursively compare objects and arrays.
if
(
className
===
'
[object Array]
'
)
{
if
(
areArrays
)
{
// Compare array lengths to determine if a deep comparison is necessary.
// Compare array lengths to determine if a deep comparison is necessary.
size
=
a
.
length
;
length
=
a
.
length
;
result
=
size
===
b
.
length
;
if
(
length
!==
b
.
length
)
return
false
;
if
(
result
)
{
// Deep compare the contents, ignoring non-numeric properties.
// Deep compare the contents, ignoring non-numeric properties.
while
(
length
--
)
{
while
(
size
--
)
{
if
(
!
eq
(
a
[
length
],
b
[
length
],
aStack
,
bStack
))
return
false
;
if
(
!
(
result
=
eq
(
a
[
size
],
b
[
size
],
aStack
,
bStack
)))
break
;
}
}
}
}
else
{
}
else
{
// Deep compare objects.
// Deep compare objects.
var
keys
=
_
.
keys
(
a
),
key
;
var
keys
=
_
.
keys
(
a
),
key
;
size
=
keys
.
length
;
length
=
keys
.
length
;
// Ensure that both objects contain the same number of properties before comparing deep equality.
// Ensure that both objects contain the same number of properties before comparing deep equality.
result
=
_
.
keys
(
b
).
length
===
size
;
if
(
_
.
keys
(
b
).
length
!==
length
)
return
false
;
if
(
result
)
{
while
(
length
--
)
{
while
(
size
--
)
{
// Deep compare each member
// Deep compare each member
key
=
keys
[
length
];
key
=
keys
[
size
];
if
(
!
(
_
.
has
(
b
,
key
)
&&
eq
(
a
[
key
],
b
[
key
],
aStack
,
bStack
)))
return
false
;
if
(
!
(
result
=
_
.
has
(
b
,
key
)
&&
eq
(
a
[
key
],
b
[
key
],
aStack
,
bStack
)))
break
;
}
}
}
}
}
// Remove the first object from the stack of traversed objects.
// Remove the first object from the stack of traversed objects.
aStack
.
pop
();
aStack
.
pop
();
bStack
.
pop
();
bStack
.
pop
();
return
result
;
return
true
;
};
};
// Perform a deep comparison to check if two objects are equal.
// Perform a deep comparison to check if two objects are equal.
_
.
isEqual
=
function
(
a
,
b
)
{
_
.
isEqual
=
function
(
a
,
b
)
{
return
eq
(
a
,
b
,
[],
[]
);
return
eq
(
a
,
b
);
};
};
// Is a given array, string, or object empty?
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
// An "empty" object has no enumerable own-properties.
_
.
isEmpty
=
function
(
obj
)
{
_
.
isEmpty
=
function
(
obj
)
{
if
(
obj
==
null
)
return
true
;
if
(
obj
==
null
)
return
true
;
if
(
_
.
isArray
(
obj
)
||
_
.
isString
(
obj
)
||
_
.
isArguments
(
obj
))
return
obj
.
length
===
0
;
if
(
isArrayLike
(
obj
)
&&
(
_
.
isArray
(
obj
)
||
_
.
isString
(
obj
)
||
_
.
isArguments
(
obj
)))
return
obj
.
length
===
0
;
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
return
false
;
return
_
.
keys
(
obj
).
length
===
0
;
return
true
;
};
};
// Is a given value a DOM element?
// Is a given value a DOM element?
...
@@ -1091,14 +1214,14 @@
...
@@ -1091,14 +1214,14 @@
return
type
===
'
function
'
||
type
===
'
object
'
&&
!!
obj
;
return
type
===
'
function
'
||
type
===
'
object
'
&&
!!
obj
;
};
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp
, isError
.
_
.
each
([
'
Arguments
'
,
'
Function
'
,
'
String
'
,
'
Number
'
,
'
Date
'
,
'
RegExp
'
],
function
(
name
)
{
_
.
each
([
'
Arguments
'
,
'
Function
'
,
'
String
'
,
'
Number
'
,
'
Date
'
,
'
RegExp
'
,
'
Error
'
],
function
(
name
)
{
_
[
'
is
'
+
name
]
=
function
(
obj
)
{
_
[
'
is
'
+
name
]
=
function
(
obj
)
{
return
toString
.
call
(
obj
)
===
'
[object
'
+
name
+
'
]
'
;
return
toString
.
call
(
obj
)
===
'
[object
'
+
name
+
'
]
'
;
};
};
});
});
// Define a fallback version of the method in browsers (ahem, IE), where
// Define a fallback version of the method in browsers (ahem, IE
< 9
), where
// there isn't any inspectable "Arguments" type.
// there isn't any inspectable "Arguments" type.
if
(
!
_
.
isArguments
(
arguments
))
{
if
(
!
_
.
isArguments
(
arguments
))
{
_
.
isArguments
=
function
(
obj
)
{
_
.
isArguments
=
function
(
obj
)
{
...
@@ -1106,8 +1229,9 @@
...
@@ -1106,8 +1229,9 @@
};
};
}
}
// Optimize `isFunction` if appropriate. Work around an IE 11 bug.
// Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
if
(
typeof
/./
!==
'
function
'
)
{
// IE 11 (#1621), and in Safari 8 (#1929).
if
(
typeof
/./
!=
'
function
'
&&
typeof
Int8Array
!=
'
object
'
)
{
_
.
isFunction
=
function
(
obj
)
{
_
.
isFunction
=
function
(
obj
)
{
return
typeof
obj
==
'
function
'
||
false
;
return
typeof
obj
==
'
function
'
||
false
;
};
};
...
@@ -1159,6 +1283,7 @@
...
@@ -1159,6 +1283,7 @@
return
value
;
return
value
;
};
};
// Predicate-generating functions. Often useful outside of Underscore.
_
.
constant
=
function
(
value
)
{
_
.
constant
=
function
(
value
)
{
return
function
()
{
return
function
()
{
return
value
;
return
value
;
...
@@ -1167,30 +1292,28 @@
...
@@ -1167,30 +1292,28 @@
_
.
noop
=
function
(){};
_
.
noop
=
function
(){};
_
.
property
=
function
(
key
)
{
_
.
property
=
property
;
return
function
(
obj
)
{
// Generates a function for a given object that returns a given property.
_
.
propertyOf
=
function
(
obj
)
{
return
obj
==
null
?
function
(){}
:
function
(
key
)
{
return
obj
[
key
];
return
obj
[
key
];
};
};
};
};
// Returns a predicate for checking whether an object has a given set of `key:value` pairs.
// Returns a predicate for checking whether an object has a given set of
_
.
matches
=
function
(
attrs
)
{
// `key:value` pairs.
var
pairs
=
_
.
pairs
(
attrs
),
length
=
pairs
.
length
;
_
.
matcher
=
_
.
matches
=
function
(
attrs
)
{
attrs
=
_
.
extendOwn
({},
attrs
);
return
function
(
obj
)
{
return
function
(
obj
)
{
if
(
obj
==
null
)
return
!
length
;
return
_
.
isMatch
(
obj
,
attrs
);
obj
=
new
Object
(
obj
);
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
var
pair
=
pairs
[
i
],
key
=
pair
[
0
];
if
(
pair
[
1
]
!==
obj
[
key
]
||
!
(
key
in
obj
))
return
false
;
}
return
true
;
};
};
};
};
// Run a function **n** times.
// Run a function **n** times.
_
.
times
=
function
(
n
,
iteratee
,
context
)
{
_
.
times
=
function
(
n
,
iteratee
,
context
)
{
var
accum
=
Array
(
Math
.
max
(
0
,
n
));
var
accum
=
Array
(
Math
.
max
(
0
,
n
));
iteratee
=
createCallback
(
iteratee
,
context
,
1
);
iteratee
=
optimizeCb
(
iteratee
,
context
,
1
);
for
(
var
i
=
0
;
i
<
n
;
i
++
)
accum
[
i
]
=
iteratee
(
i
);
for
(
var
i
=
0
;
i
<
n
;
i
++
)
accum
[
i
]
=
iteratee
(
i
);
return
accum
;
return
accum
;
};
};
...
@@ -1239,10 +1362,12 @@
...
@@ -1239,10 +1362,12 @@
// If the value of the named `property` is a function then invoke it with the
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
// `object` as context; otherwise, return it.
_
.
result
=
function
(
object
,
property
)
{
_
.
result
=
function
(
object
,
property
,
fallback
)
{
if
(
object
==
null
)
return
void
0
;
var
value
=
object
==
null
?
void
0
:
object
[
property
];
var
value
=
object
[
property
];
if
(
value
===
void
0
)
{
return
_
.
isFunction
(
value
)
?
object
[
property
]()
:
value
;
value
=
fallback
;
}
return
_
.
isFunction
(
value
)
?
value
.
call
(
object
)
:
value
;
};
};
// Generate a unique integer id (unique within the entire client session).
// Generate a unique integer id (unique within the entire client session).
...
@@ -1357,8 +1482,8 @@
...
@@ -1357,8 +1482,8 @@
// underscore functions. Wrapped objects may be chained.
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
// Helper function to continue chaining intermediate results.
var
result
=
function
(
obj
)
{
var
result
=
function
(
instance
,
obj
)
{
return
this
.
_chain
?
_
(
obj
).
chain
()
:
obj
;
return
instance
.
_chain
?
_
(
obj
).
chain
()
:
obj
;
};
};
// Add your own custom functions to the Underscore object.
// Add your own custom functions to the Underscore object.
...
@@ -1368,7 +1493,7 @@
...
@@ -1368,7 +1493,7 @@
_
.
prototype
[
name
]
=
function
()
{
_
.
prototype
[
name
]
=
function
()
{
var
args
=
[
this
.
_wrapped
];
var
args
=
[
this
.
_wrapped
];
push
.
apply
(
args
,
arguments
);
push
.
apply
(
args
,
arguments
);
return
result
.
call
(
this
,
func
.
apply
(
_
,
args
));
return
result
(
this
,
func
.
apply
(
_
,
args
));
};
};
});
});
};
};
...
@@ -1383,7 +1508,7 @@
...
@@ -1383,7 +1508,7 @@
var
obj
=
this
.
_wrapped
;
var
obj
=
this
.
_wrapped
;
method
.
apply
(
obj
,
arguments
);
method
.
apply
(
obj
,
arguments
);
if
((
name
===
'
shift
'
||
name
===
'
splice
'
)
&&
obj
.
length
===
0
)
delete
obj
[
0
];
if
((
name
===
'
shift
'
||
name
===
'
splice
'
)
&&
obj
.
length
===
0
)
delete
obj
[
0
];
return
result
.
call
(
this
,
obj
);
return
result
(
this
,
obj
);
};
};
});
});
...
@@ -1391,7 +1516,7 @@
...
@@ -1391,7 +1516,7 @@
_
.
each
([
'
concat
'
,
'
join
'
,
'
slice
'
],
function
(
name
)
{
_
.
each
([
'
concat
'
,
'
join
'
,
'
slice
'
],
function
(
name
)
{
var
method
=
ArrayProto
[
name
];
var
method
=
ArrayProto
[
name
];
_
.
prototype
[
name
]
=
function
()
{
_
.
prototype
[
name
]
=
function
()
{
return
result
.
call
(
this
,
method
.
apply
(
this
.
_wrapped
,
arguments
));
return
result
(
this
,
method
.
apply
(
this
.
_wrapped
,
arguments
));
};
};
});
});
...
@@ -1400,6 +1525,14 @@
...
@@ -1400,6 +1525,14 @@
return
this
.
_wrapped
;
return
this
.
_wrapped
;
};
};
// Provide unwrapping proxy for some methods used in engine operations
// such as arithmetic and JSON stringification.
_
.
prototype
.
valueOf
=
_
.
prototype
.
toJSON
=
_
.
prototype
.
value
;
_
.
prototype
.
toString
=
function
()
{
return
''
+
this
.
_wrapped
;
};
// AMD registration happens at the end for compatibility with AMD loaders
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// practice for AMD registration is to be anonymous, underscore registers
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment