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
Sven Franck
todomvc
Commits
25e31ff1
Commit
25e31ff1
authored
Feb 08, 2015
by
Sam Saccone
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update backbone.marionette to new UI
parent
93071606
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
9908 additions
and
7118 deletions
+9908
-7118
examples/backbone_marionette/.gitignore
examples/backbone_marionette/.gitignore
+21
-0
examples/backbone_marionette/bower.json
examples/backbone_marionette/bower.json
+0
-11
examples/backbone_marionette/bower_components/todomvc-common/bg.png
...ackbone_marionette/bower_components/todomvc-common/bg.png
+0
-0
examples/backbone_marionette/css/app.css
examples/backbone_marionette/css/app.css
+1
-0
examples/backbone_marionette/index.html
examples/backbone_marionette/index.html
+9
-10
examples/backbone_marionette/node_modules/backbone.localstorage/backbone.localStorage.js
...de_modules/backbone.localstorage/backbone.localStorage.js
+88
-52
examples/backbone_marionette/node_modules/backbone.marionette/lib/backbone.marionette.js
...de_modules/backbone.marionette/lib/backbone.marionette.js
+1395
-1234
examples/backbone_marionette/node_modules/backbone/backbone.js
...les/backbone_marionette/node_modules/backbone/backbone.js
+216
-179
examples/backbone_marionette/node_modules/jquery/dist/jquery.js
...es/backbone_marionette/node_modules/jquery/dist/jquery.js
+6189
-5632
examples/backbone_marionette/node_modules/todomvc-app-css/index.css
...ackbone_marionette/node_modules/todomvc-app-css/index.css
+394
-0
examples/backbone_marionette/node_modules/todomvc-common/base.css
.../backbone_marionette/node_modules/todomvc-common/base.css
+141
-0
examples/backbone_marionette/node_modules/todomvc-common/base.js
...s/backbone_marionette/node_modules/todomvc-common/base.js
+27
-0
examples/backbone_marionette/node_modules/underscore/underscore.js
...backbone_marionette/node_modules/underscore/underscore.js
+1415
-0
examples/backbone_marionette/package.json
examples/backbone_marionette/package.json
+12
-0
No files found.
examples/backbone_marionette/.gitignore
0 → 100644
View file @
25e31ff1
node_modules/backbone.marionette
!node_modules/backbone.marionette/lib/backbone.marionette.js
node_modules/todomvc-app-css
!node_modules/todomvc-app-css/index.css
node_modules/todomvc-common
!node_modules/todomvc-common/base.js
!node_modules/todomvc-common/base.css
node_modules/jquery
!node_modules/jquery/dist/jquery.js
node_modules/underscore
!node_modules/underscore/underscore.js
node_modules/backbone
!node_modules/backbone/backbone.js
node_modules/backbone.localstorage
!node_modules/backbone.localstorage/backbone.localStorage.js
examples/backbone_marionette/bower.json
deleted
100644 → 0
View file @
93071606
{
"name"
:
"todomvc-backbone-marionette"
,
"version"
:
"0.0.0"
,
"dependencies"
:
{
"jquery"
:
"~1.10.2"
,
"todomvc-common"
:
"~0.3.0"
,
"underscore"
:
"~1.4.4"
,
"backbone.localStorage"
:
"~1.1.6"
,
"backbone.marionette"
:
"~2.1.0"
}
}
examples/backbone_marionette/bower_components/todomvc-common/bg.png
deleted
100644 → 0
View file @
93071606
2.08 KB
examples/backbone_marionette/css/app.css
View file @
25e31ff1
...
...
@@ -7,3 +7,4 @@
#footer
{
display
:
none
;
}
examples/backbone_marionette/index.html
View file @
25e31ff1
...
...
@@ -3,7 +3,8 @@
<head>
<meta
charset=
"utf-8"
>
<title>
Marionette • TodoMVC
</title>
<link
rel=
"stylesheet"
href=
"bower_components/todomvc-common/base.css"
>
<link
rel=
"stylesheet"
href=
"node_modules/todomvc-app-css/index.css"
>
<link
rel=
"stylesheet"
href=
"node_modules/todomvc-common/base.css"
>
<link
rel=
"stylesheet"
href=
"css/app.css"
>
</head>
<body>
...
...
@@ -36,9 +37,7 @@
<
a
href
=
"
#/completed
"
>
Completed
<
/a
>
<
/li
>
<
/ul
>
<
button
id
=
"
clear-completed
"
<%
if
(
!
completedCount
)
{
%>
class
=
"
hidden
"
<%
}
%>>
Clear
completed
(
<%=
completedCount
%>
)
<
/button
>
<
button
id
=
"
clear-completed
"
<%
if
(
!
completedCount
)
{
%>
class
=
"
hidden
"
<%
}
%>>
Clear
completed
(
<%=
completedCount
%>
)
<
/button
>
</script>
<script
type=
"text/html"
id=
"template-header"
>
<
h1
>
todos
<
/h1
>
...
...
@@ -58,12 +57,12 @@
<
ul
id
=
"
todo-list
"
><
/ul
>
</script>
<!-- vendor libraries -->
<script
src=
"
bower_component
s/todomvc-common/base.js"
></script>
<script
src=
"
bower_components/jquery
/jquery.js"
></script>
<script
src=
"
bower_component
s/underscore/underscore.js"
></script>
<script
src=
"
bower_component
s/backbone/backbone.js"
></script>
<script
src=
"
bower_components/backbone.localS
torage/backbone.localStorage.js"
></script>
<script
src=
"
bower_component
s/backbone.marionette/lib/backbone.marionette.js"
></script>
<script
src=
"
node_module
s/todomvc-common/base.js"
></script>
<script
src=
"
node_modules/jquery/dist
/jquery.js"
></script>
<script
src=
"
node_module
s/underscore/underscore.js"
></script>
<script
src=
"
node_module
s/backbone/backbone.js"
></script>
<script
src=
"
node_modules/backbone.locals
torage/backbone.localStorage.js"
></script>
<script
src=
"
node_module
s/backbone.marionette/lib/backbone.marionette.js"
></script>
<!-- application -->
<script
src=
"js/TodoMVC.js"
></script>
<script
src=
"js/TodoMVC.Todos.js"
></script>
...
...
examples/backbone_marionette/
bower_components/backbone.localS
torage/backbone.localStorage.js
→
examples/backbone_marionette/
node_modules/backbone.locals
torage/backbone.localStorage.js
View file @
25e31ff1
/**
* Backbone localStorage Adapter
* Version 1.1.6
* Version 1.1.
1
6
*
* https://github.com/jeromegn/Backbone.localStorage
*/
(
function
(
root
,
factory
)
{
if
(
typeof
exports
===
'
object
'
&&
root
.
require
)
{
module
.
exports
=
factory
(
require
(
"
underscore
"
),
require
(
"
backbone
"
));
if
(
typeof
exports
===
'
object
'
&&
typeof
require
===
'
function
'
)
{
module
.
exports
=
factory
(
require
(
"
backbone
"
));
}
else
if
(
typeof
define
===
"
function
"
&&
define
.
amd
)
{
// AMD. Register as an anonymous module.
define
([
"
underscore
"
,
"
backbone
"
],
function
(
_
,
Backbone
)
{
define
([
"
backbone
"
],
function
(
Backbone
)
{
// Use global variables if the locals are undefined.
return
factory
(
_
||
root
.
_
,
Backbone
||
root
.
Backbone
);
return
factory
(
Backbone
||
root
.
Backbone
);
});
}
else
{
// RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
factory
(
_
,
Backbone
);
factory
(
Backbone
);
}
}(
this
,
function
(
_
,
Backbone
)
{
}(
this
,
function
(
Backbone
)
{
// A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that.
// Hold reference to Underscore.js and Backbone.js in the closure in order
// to make things work even if they are removed from the global namespace
// Generate four random hex digits.
function
S4
()
{
return
(((
1
+
Math
.
random
())
*
0x10000
)
|
0
).
toString
(
16
).
substring
(
1
);
...
...
@@ -35,19 +31,49 @@ function guid() {
return
(
S4
()
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
S4
()
+
S4
());
};
function
isObject
(
item
)
{
return
item
===
Object
(
item
);
}
function
contains
(
array
,
item
)
{
var
i
=
array
.
length
;
while
(
i
--
)
if
(
array
[
i
]
===
item
)
return
true
;
return
false
;
}
function
extend
(
obj
,
props
)
{
for
(
var
key
in
props
)
obj
[
key
]
=
props
[
key
]
return
obj
;
}
function
result
(
object
,
property
)
{
if
(
object
==
null
)
return
void
0
;
var
value
=
object
[
property
];
return
(
typeof
value
===
'
function
'
)
?
object
[
property
]()
:
value
;
}
// Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table.
// window.Store is deprectated, use Backbone.LocalStorage instead
Backbone
.
LocalStorage
=
window
.
Store
=
function
(
name
)
{
Backbone
.
LocalStorage
=
window
.
Store
=
function
(
name
,
serializer
)
{
if
(
!
this
.
localStorage
)
{
throw
"
Backbone.localStorage: Environment does not support localStorage.
"
}
this
.
name
=
name
;
this
.
serializer
=
serializer
||
{
serialize
:
function
(
item
)
{
return
isObject
(
item
)
?
JSON
.
stringify
(
item
)
:
item
;
},
// fix for "illegal access" error on Android when JSON.parse is passed null
deserialize
:
function
(
data
)
{
return
data
&&
JSON
.
parse
(
data
);
}
};
var
store
=
this
.
localStorage
().
getItem
(
this
.
name
);
this
.
records
=
(
store
&&
store
.
split
(
"
,
"
))
||
[];
};
_
.
extend
(
Backbone
.
LocalStorage
.
prototype
,
{
extend
(
Backbone
.
LocalStorage
.
prototype
,
{
// Save the current state of the **Store** to *localStorage*.
save
:
function
()
{
...
...
@@ -57,11 +83,11 @@ _.extend(Backbone.LocalStorage.prototype, {
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
// have an id of it's own.
create
:
function
(
model
)
{
if
(
!
model
.
id
)
{
if
(
!
model
.
id
&&
model
.
id
!==
0
)
{
model
.
id
=
guid
();
model
.
set
(
model
.
idAttribute
,
model
.
id
);
}
this
.
localStorage
().
setItem
(
this
.
name
+
"
-
"
+
model
.
id
,
JSON
.
stringify
(
model
));
this
.
localStorage
().
setItem
(
this
.
_itemName
(
model
.
id
),
this
.
serializer
.
serialize
(
model
));
this
.
records
.
push
(
model
.
id
.
toString
());
this
.
save
();
return
this
.
find
(
model
);
...
...
@@ -69,36 +95,40 @@ _.extend(Backbone.LocalStorage.prototype, {
// Update a model by replacing its copy in `this.data`.
update
:
function
(
model
)
{
this
.
localStorage
().
setItem
(
this
.
name
+
"
-
"
+
model
.
id
,
JSON
.
stringify
(
model
));
if
(
!
_
.
include
(
this
.
records
,
model
.
id
.
toString
()))
this
.
records
.
push
(
model
.
id
.
toString
());
this
.
save
();
this
.
localStorage
().
setItem
(
this
.
_itemName
(
model
.
id
),
this
.
serializer
.
serialize
(
model
));
var
modelId
=
model
.
id
.
toString
();
if
(
!
contains
(
this
.
records
,
modelId
))
{
this
.
records
.
push
(
modelId
);
this
.
save
();
}
return
this
.
find
(
model
);
},
// Retrieve a model from `this.data` by id.
find
:
function
(
model
)
{
return
this
.
jsonData
(
this
.
localStorage
().
getItem
(
this
.
name
+
"
-
"
+
model
.
id
));
return
this
.
serializer
.
deserialize
(
this
.
localStorage
().
getItem
(
this
.
_itemName
(
model
.
id
)
));
},
// Return the array of all models currently in storage.
findAll
:
function
()
{
// Lodash removed _#chain in v1.0.0-rc.1
return
(
_
.
chain
||
_
)(
this
.
records
)
.
map
(
function
(
id
){
return
this
.
jsonData
(
this
.
localStorage
().
getItem
(
this
.
name
+
"
-
"
+
id
));
},
this
)
.
compact
()
.
value
()
;
var
result
=
[];
for
(
var
i
=
0
,
id
,
data
;
i
<
this
.
records
.
length
;
i
++
)
{
id
=
this
.
records
[
i
];
data
=
this
.
serializer
.
deserialize
(
this
.
localStorage
().
getItem
(
this
.
_itemName
(
id
)
));
if
(
data
!=
null
)
result
.
push
(
data
);
}
return
result
;
},
// Delete a model from `this.data`, returning it.
destroy
:
function
(
model
)
{
if
(
model
.
isNew
())
return
false
this
.
localStorage
().
removeItem
(
this
.
name
+
"
-
"
+
model
.
id
);
this
.
records
=
_
.
reject
(
this
.
records
,
function
(
id
){
return
id
===
model
.
id
.
toString
();
});
this
.
localStorage
().
removeItem
(
this
.
_itemName
(
model
.
id
));
var
modelId
=
model
.
id
.
toString
();
for
(
var
i
=
0
,
id
;
i
<
this
.
records
.
length
;
i
++
)
{
if
(
this
.
records
[
i
]
===
modelId
)
{
this
.
records
.
splice
(
i
,
1
);
}
}
this
.
save
();
return
model
;
},
...
...
@@ -107,11 +137,6 @@ _.extend(Backbone.LocalStorage.prototype, {
return
localStorage
;
},
// fix for "illegal access" error on Android when JSON.parse is passed null
jsonData
:
function
(
data
)
{
return
data
&&
JSON
.
parse
(
data
);
},
// Clear localStorage for specific collection.
_clear
:
function
()
{
var
local
=
this
.
localStorage
(),
...
...
@@ -120,11 +145,12 @@ _.extend(Backbone.LocalStorage.prototype, {
// Remove id-tracking item (e.g., "foo").
local
.
removeItem
(
this
.
name
);
// Lodash removed _#chain in v1.0.0-rc.1
// Match all data items (e.g., "foo-ID") and remove.
(
_
.
chain
||
_
)(
local
).
keys
()
.
filter
(
function
(
k
)
{
return
itemRe
.
test
(
k
);
})
.
each
(
function
(
k
)
{
local
.
removeItem
(
k
);
});
for
(
var
k
in
local
)
{
if
(
itemRe
.
test
(
k
))
{
local
.
removeItem
(
k
);
}
}
this
.
records
.
length
=
0
;
},
...
...
@@ -132,6 +158,10 @@ _.extend(Backbone.LocalStorage.prototype, {
// Size of localStorage.
_storageSize
:
function
()
{
return
this
.
localStorage
().
length
;
},
_itemName
:
function
(
id
)
{
return
this
.
name
+
"
-
"
+
id
;
}
});
...
...
@@ -140,9 +170,13 @@ _.extend(Backbone.LocalStorage.prototype, {
// *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
Backbone
.
LocalStorage
.
sync
=
window
.
Store
.
sync
=
Backbone
.
localSync
=
function
(
method
,
model
,
options
)
{
var
store
=
model
.
localStorage
||
model
.
collection
.
localStorage
;
var
store
=
result
(
model
,
'
localStorage
'
)
||
result
(
model
.
collection
,
'
localStorage
'
)
;
var
resp
,
errorMessage
,
syncDfd
=
Backbone
.
$
.
Deferred
&&
Backbone
.
$
.
Deferred
();
//If $ is having Deferred - use it.
var
resp
,
errorMessage
;
//If $ is having Deferred - use it.
var
syncDfd
=
Backbone
.
$
?
(
Backbone
.
$
.
Deferred
&&
Backbone
.
$
.
Deferred
())
:
(
Backbone
.
Deferred
&&
Backbone
.
Deferred
());
try
{
...
...
@@ -204,8 +238,10 @@ Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(m
Backbone
.
ajaxSync
=
Backbone
.
sync
;
Backbone
.
getSyncMethod
=
function
(
model
)
{
if
(
model
.
localStorage
||
(
model
.
collection
&&
model
.
collection
.
localStorage
))
{
Backbone
.
getSyncMethod
=
function
(
model
,
options
)
{
var
forceAjaxSync
=
options
&&
options
.
ajaxSync
;
if
(
!
forceAjaxSync
&&
(
result
(
model
,
'
localStorage
'
)
||
result
(
model
.
collection
,
'
localStorage
'
)))
{
return
Backbone
.
localSync
;
}
...
...
@@ -215,7 +251,7 @@ Backbone.getSyncMethod = function(model) {
// Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone
.
sync
=
function
(
method
,
model
,
options
)
{
return
Backbone
.
getSyncMethod
(
model
).
apply
(
this
,
[
method
,
model
,
options
]);
return
Backbone
.
getSyncMethod
(
model
,
options
).
apply
(
this
,
[
method
,
model
,
options
]);
};
return
Backbone
.
LocalStorage
;
...
...
examples/backbone_marionette/
bower_component
s/backbone.marionette/lib/backbone.marionette.js
→
examples/backbone_marionette/
node_module
s/backbone.marionette/lib/backbone.marionette.js
View file @
25e31ff1
// MarionetteJS (Backbone.Marionette)
// ----------------------------------
// v2.
1.0
// v2.
3.2
//
// Copyright (c)201
4
Derick Bailey, Muted Solutions, LLC.
// Copyright (c)201
5
Derick Bailey, Muted Solutions, LLC.
// Distributed under MIT license
//
// http://marionettejs.com
...
...
@@ -19,24 +19,26 @@
(
function
(
root
,
factory
)
{
/* istanbul ignore next */
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
define
([
'
backbone
'
,
'
underscore
'
],
function
(
Backbone
,
_
)
{
return
(
root
.
Marionette
=
factory
(
root
,
Backbone
,
_
));
return
(
root
.
Marionette
=
root
.
Mn
=
factory
(
root
,
Backbone
,
_
));
});
}
else
if
(
typeof
exports
!==
'
undefined
'
)
{
var
Backbone
=
require
(
'
backbone
'
);
var
_
=
require
(
'
underscore
'
);
module
.
exports
=
factory
(
root
,
Backbone
,
_
);
}
else
{
root
.
Marionette
=
factory
(
root
,
root
.
Backbone
,
root
.
_
);
root
.
Marionette
=
root
.
Mn
=
factory
(
root
,
root
.
Backbone
,
root
.
_
);
}
}(
this
,
function
(
root
,
Backbone
,
_
)
{
'
use strict
'
;
/* istanbul ignore next */
// Backbone.BabySitter
// -------------------
// v0.1.
4
// v0.1.
5
//
// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
// Distributed under MIT license
...
...
@@ -165,13 +167,15 @@
// return the public API
return
Container
;
}(
Backbone
,
_
);
Backbone
.
ChildViewContainer
.
VERSION
=
"
0.1.
4
"
;
Backbone
.
ChildViewContainer
.
VERSION
=
"
0.1.
5
"
;
Backbone
.
ChildViewContainer
.
noConflict
=
function
()
{
Backbone
.
ChildViewContainer
=
previousChildViewContainer
;
return
this
;
};
return
Backbone
.
ChildViewContainer
;
})(
Backbone
,
_
);
/* istanbul ignore next */
// Backbone.Wreqr (Backbone.Marionette)
// ----------------------------------
// v1.3.1
...
...
@@ -489,7 +493,7 @@
var
Marionette
=
Backbone
.
Marionette
=
{};
Marionette
.
VERSION
=
'
2.
1.0
'
;
Marionette
.
VERSION
=
'
2.
3.2
'
;
Marionette
.
noConflict
=
function
()
{
root
.
Marionette
=
previousMarionette
;
...
...
@@ -501,26 +505,26 @@
// Get the Deferred creator for later use
Marionette
.
Deferred
=
Backbone
.
$
.
Deferred
;
/* jshint unused: false */
/* jshint unused: false */
/* global console */
// Helpers
// -------
// For slicing `arguments` in functions
var
slice
=
Array
.
prototype
.
slice
;
function
throwError
(
message
,
name
)
{
var
error
=
new
Error
(
message
);
error
.
name
=
name
||
'
Error
'
;
throw
error
;
}
// Marionette.extend
// -----------------
// Borrow the Backbone `extend` method so we can use it as needed
Marionette
.
extend
=
Backbone
.
Model
.
extend
;
// Marionette.isNodeAttached
// -------------------------
// Determine if `el` is a child of the document
Marionette
.
isNodeAttached
=
function
(
el
)
{
return
Backbone
.
$
.
contains
(
document
.
documentElement
,
el
);
};
// Marionette.getOption
// --------------------
...
...
@@ -528,15 +532,11 @@
// object or its `options`, with `options` taking precedence.
Marionette
.
getOption
=
function
(
target
,
optionName
)
{
if
(
!
target
||
!
optionName
)
{
return
;
}
var
value
;
if
(
target
.
options
&&
(
target
.
options
[
optionName
]
!==
undefined
))
{
value
=
target
.
options
[
optionName
];
return
target
.
options
[
optionName
];
}
else
{
value
=
target
[
optionName
];
return
target
[
optionName
];
}
return
value
;
};
// Proxy `Marionette.getOption`
...
...
@@ -544,44 +544,67 @@
return
Marionette
.
getOption
(
this
,
optionName
);
};
// Similar to `_.result`, this is a simple helper
// If a function is provided we call it with context
// otherwise just return the value. If the value is
// undefined return a default value
Marionette
.
_getValue
=
function
(
value
,
context
,
params
)
{
if
(
_
.
isFunction
(
value
))
{
// We need to ensure that params is not undefined
// to prevent `apply` from failing in ie8
params
=
params
||
[];
value
=
value
.
apply
(
context
,
params
);
}
return
value
;
};
// Marionette.normalizeMethods
// ----------------------
// Pass in a mapping of events => functions or function names
// and return a mapping of events => functions
Marionette
.
normalizeMethods
=
function
(
hash
)
{
var
normalizedHash
=
{};
_
.
each
(
hash
,
function
(
method
,
name
)
{
return
_
.
reduce
(
hash
,
function
(
normalizedHash
,
method
,
name
)
{
if
(
!
_
.
isFunction
(
method
))
{
method
=
this
[
method
];
}
if
(
!
method
)
{
return
;
}
if
(
method
)
{
normalizedHash
[
name
]
=
method
;
},
this
);
}
return
normalizedHash
;
},
{},
this
);
};
// utility method for parsing @ui. syntax strings
// into associated selector
Marionette
.
normalizeUIString
=
function
(
uiString
,
ui
)
{
return
uiString
.
replace
(
/@ui
\.[
a-zA-Z_$0-9
]
*/g
,
function
(
r
)
{
return
ui
[
r
.
slice
(
4
)];
});
};
// allows for the use of the @ui. syntax within
// a given key for triggers and events
// swaps the @ui with the associated selector
// swaps the @ui with the associated selector.
// Returns a new, non-mutated, parsed events hash.
Marionette
.
normalizeUIKeys
=
function
(
hash
,
ui
)
{
if
(
typeof
(
hash
)
===
'
undefined
'
)
{
return
;
}
return
_
.
reduce
(
hash
,
function
(
memo
,
val
,
key
)
{
var
normalizedKey
=
Marionette
.
normalizeUIString
(
key
,
ui
);
memo
[
normalizedKey
]
=
val
;
return
memo
;
},
{});
};
_
.
each
(
_
.
keys
(
hash
),
function
(
v
)
{
var
pattern
=
/@ui
\.[
a-zA-Z_$0-9
]
*/g
;
if
(
v
.
match
(
pattern
))
{
hash
[
v
.
replace
(
pattern
,
function
(
r
)
{
return
ui
[
r
.
slice
(
4
)];
})]
=
hash
[
v
];
delete
hash
[
v
]
;
// allows for the use of the @ui. syntax within
// a given value for regions
// swaps the @ui with the associated selector
Marionette
.
normalizeUIValues
=
function
(
hash
,
ui
)
{
_
.
each
(
hash
,
function
(
val
,
key
)
{
if
(
_
.
isString
(
val
))
{
hash
[
key
]
=
Marionette
.
normalizeUIString
(
val
,
ui
)
;
}
});
return
hash
;
};
...
...
@@ -604,15 +627,31 @@
});
};
// Trigger an event and/or a corresponding method name. Examples:
//
// `this.triggerMethod("foo")` will trigger the "foo" event and
// call the "onFoo" method.
//
// `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
// call the "onFooBar" method.
Marionette
.
triggerMethod
=
(
function
()
{
var
deprecate
=
Marionette
.
deprecate
=
function
(
message
,
test
)
{
if
(
_
.
isObject
(
message
))
{
message
=
(
message
.
prev
+
'
is going to be removed in the future.
'
+
'
Please use
'
+
message
.
next
+
'
instead.
'
+
(
message
.
url
?
'
See:
'
+
message
.
url
:
''
)
);
}
if
((
test
===
undefined
||
!
test
)
&&
!
deprecate
.
_cache
[
message
])
{
deprecate
.
_warn
(
'
Deprecation warning:
'
+
message
);
deprecate
.
_cache
[
message
]
=
true
;
}
};
deprecate
.
_warn
=
typeof
console
!==
'
undefined
'
&&
(
console
.
warn
||
console
.
log
)
||
function
()
{};
deprecate
.
_cache
=
{};
/* jshint maxstatements: 14, maxcomplexity: 7 */
// Trigger Method
// --------------
Marionette
.
_triggerMethod
=
(
function
()
{
// split the event name on the ":"
var
splitter
=
/
(
^|:
)(\w)
/gi
;
...
...
@@ -622,90 +661,109 @@
return
eventName
.
toUpperCase
();
}
// actual triggerMethod implementation
var
triggerMethod
=
function
(
event
)
{
return
function
(
context
,
event
,
args
)
{
var
noEventArg
=
arguments
.
length
<
3
;
if
(
noEventArg
)
{
args
=
event
;
event
=
args
[
0
];
}
// get the method name from the event name
var
methodName
=
'
on
'
+
event
.
replace
(
splitter
,
getEventName
);
var
method
=
this
[
methodName
];
var
method
=
context
[
methodName
];
var
result
;
// call the onMethodName if it exists
if
(
_
.
isFunction
(
method
))
{
// pass all arg
ument
s, except the event name
result
=
method
.
apply
(
this
,
_
.
tail
(
arguments
)
);
// pass all args, except the event name
result
=
method
.
apply
(
context
,
noEventArg
?
_
.
rest
(
args
)
:
args
);
}
// trigger the event, if a trigger method exists
if
(
_
.
isFunction
(
this
.
trigger
))
{
this
.
trigger
.
apply
(
this
,
arguments
);
if
(
_
.
isFunction
(
context
.
trigger
))
{
if
(
noEventArg
+
args
.
length
>
1
)
{
context
.
trigger
.
apply
(
context
,
noEventArg
?
args
:
[
event
].
concat
(
_
.
rest
(
args
,
0
)));
}
else
{
context
.
trigger
(
event
);
}
}
return
result
;
};
return
triggerMethod
;
})();
// DOMRefresh
// ----------
// Trigger an event and/or a corresponding method name. Examples:
//
// `this.triggerMethod("foo")` will trigger the "foo" event and
// call the "onFoo" method.
//
// `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
// call the "onFooBar" method.
Marionette
.
triggerMethod
=
function
(
event
)
{
return
Marionette
.
_triggerMethod
(
this
,
arguments
);
};
// triggerMethodOn invokes triggerMethod on a specific context
//
// e.g. `Marionette.triggerMethodOn(view, 'show')`
// will trigger a "show" event or invoke onShow the view.
Marionette
.
triggerMethodOn
=
function
(
context
)
{
var
fnc
=
_
.
isFunction
(
context
.
triggerMethod
)
?
context
.
triggerMethod
:
Marionette
.
triggerMethod
;
return
fnc
.
apply
(
context
,
_
.
rest
(
arguments
));
};
// DOM Refresh
// -----------
// Monitor a view's state, and after it has been rendered and shown
// in the DOM, trigger a "dom:refresh" event every time it is
// re-rendered.
Marionette
.
MonitorDOMRefresh
=
(
function
(
documentElement
)
{
Marionette
.
MonitorDOMRefresh
=
function
(
view
)
{
// track when the view has been shown in the DOM,
// using a Marionette.Region (or by other means of triggering "show")
function
handleShow
(
view
)
{
function
handleShow
()
{
view
.
_isShown
=
true
;
triggerDOMRefresh
(
view
);
triggerDOMRefresh
();
}
// track when the view has been rendered
function
handleRender
(
view
)
{
function
handleRender
()
{
view
.
_isRendered
=
true
;
triggerDOMRefresh
(
view
);
triggerDOMRefresh
();
}
// Trigger the "dom:refresh" event and corresponding "onDomRefresh" method
function
triggerDOMRefresh
(
view
)
{
if
(
view
.
_isShown
&&
view
.
_isRendered
&&
isInDOM
(
view
))
{
function
triggerDOMRefresh
()
{
if
(
view
.
_isShown
&&
view
.
_isRendered
&&
Marionette
.
isNodeAttached
(
view
.
el
))
{
if
(
_
.
isFunction
(
view
.
triggerMethod
))
{
view
.
triggerMethod
(
'
dom:refresh
'
);
}
}
}
function
isInDOM
(
view
)
{
return
Backbone
.
$
.
contains
(
documentElement
,
view
.
el
);
}
// Export public API
return
function
(
view
)
{
view
.
listenTo
(
view
,
'
show
'
,
function
()
{
handleShow
(
view
);
});
view
.
listenTo
(
view
,
'
render
'
,
function
()
{
handleRender
(
view
);
view
.
on
({
show
:
handleShow
,
render
:
handleRender
});
};
})(
document
.
documentElement
);
/* jshint maxparams: 5 */
//
Marionette.bindEntityEvents & unbindEntity
Events
// ---------------------------
//
Bind Entity Events & Unbind Entity
Events
// ---------------------------
--------------
//
// These methods are used to bind/unbind a backbone "entity" (collection/model)
// These methods are used to bind/unbind a backbone "entity" (
e.g.
collection/model)
// to methods on a target object.
//
// The first parameter, `target`, must have a `listenTo` method from the
// EventBinder object.
// The first parameter, `target`, must have the Backbone.Events module mixed in.
//
// The second parameter is the
entity (Backbone.Model or Backbone.Collection)
// to bind the events from.
// The second parameter is the
`entity` (Backbone.Model, Backbone.Collection or
//
any object that has Backbone.Events mixed in)
to bind the events from.
//
// The third parameter is a hash of { "event:name": "eventHandler" }
// configuration. Multiple handlers can be separated by a space. A
...
...
@@ -723,7 +781,7 @@
var
method
=
target
[
methodName
];
if
(
!
method
)
{
throwError
(
'
Method "
'
+
methodName
+
throw
new
Marionette
.
Error
(
'
Method "
'
+
methodName
+
'
" was configured as an event handler, but does not exist.
'
);
}
...
...
@@ -757,11 +815,17 @@
function
iterateEvents
(
target
,
entity
,
bindings
,
functionCallback
,
stringCallback
)
{
if
(
!
entity
||
!
bindings
)
{
return
;
}
// allow the bindings to be a function
if
(
_
.
isFunction
(
bindings
))
{
bindings
=
bindings
.
call
(
target
);
// type-check bindings
if
(
!
_
.
isObject
(
bindings
))
{
throw
new
Marionette
.
Error
({
message
:
'
Bindings must be an object or function.
'
,
url
:
'
marionette.functions.html#marionettebindentityevents
'
});
}
// allow the bindings to be a function
bindings
=
Marionette
.
_getValue
(
bindings
,
target
);
// iterate the bindings and bind them
_
.
each
(
bindings
,
function
(
methods
,
evt
)
{
...
...
@@ -797,6 +861,45 @@
})(
Marionette
);
// Error
// -----
var
errorProps
=
[
'
description
'
,
'
fileName
'
,
'
lineNumber
'
,
'
name
'
,
'
message
'
,
'
number
'
];
Marionette
.
Error
=
Marionette
.
extend
.
call
(
Error
,
{
urlRoot
:
'
http://marionettejs.com/docs/v
'
+
Marionette
.
VERSION
+
'
/
'
,
constructor
:
function
(
message
,
options
)
{
if
(
_
.
isObject
(
message
))
{
options
=
message
;
message
=
options
.
message
;
}
else
if
(
!
options
)
{
options
=
{};
}
var
error
=
Error
.
call
(
this
,
message
);
_
.
extend
(
this
,
_
.
pick
(
error
,
errorProps
),
_
.
pick
(
options
,
errorProps
));
this
.
captureStackTrace
();
if
(
options
.
url
)
{
this
.
url
=
this
.
urlRoot
+
options
.
url
;
}
},
captureStackTrace
:
function
()
{
if
(
Error
.
captureStackTrace
)
{
Error
.
captureStackTrace
(
this
,
Marionette
.
Error
);
}
},
toString
:
function
()
{
return
this
.
name
+
'
:
'
+
this
.
message
+
(
this
.
url
?
'
See:
'
+
this
.
url
:
''
);
}
});
Marionette
.
Error
.
extend
=
Marionette
.
extend
;
// Callbacks
// ---------
...
...
@@ -847,9 +950,9 @@
}
});
//
Marionette
Controller
// ----------
-----------
//
// Controller
// ----------
// A multi-purpose object to use as a controller for
// modules and routers, and as a mediator for workflow
// and coordination of other objects, views, and more.
...
...
@@ -869,9 +972,8 @@
// Ensure it can trigger events with Backbone.Events
_
.
extend
(
Marionette
.
Controller
.
prototype
,
Backbone
.
Events
,
{
destroy
:
function
()
{
var
args
=
slice
.
call
(
arguments
);
this
.
triggerMethod
.
apply
(
this
,
[
'
before:destroy
'
].
concat
(
args
));
this
.
triggerMethod
.
apply
(
this
,
[
'
destroy
'
].
concat
(
args
));
Marionette
.
_triggerMethod
(
this
,
'
before:destroy
'
,
arguments
);
Marionette
.
_triggerMethod
(
this
,
'
destroy
'
,
arguments
);
this
.
stopListening
();
this
.
off
();
...
...
@@ -887,16 +989,15 @@
});
//
Marionette
Object
// ------
---------------
//
// Object
// ------
// A Base Class that other Classes should descend from.
// Object borrows many conventions and utilities from Backbone.
Marionette
.
Object
=
function
(
options
)
{
this
.
options
=
_
.
extend
({},
_
.
result
(
this
,
'
options
'
),
options
);
this
.
initialize
(
this
.
option
s
);
this
.
initialize
.
apply
(
this
,
argument
s
);
};
Marionette
.
Object
.
extend
=
Marionette
.
extend
;
...
...
@@ -904,7 +1005,8 @@
// Object Methods
// --------------
_
.
extend
(
Marionette
.
Object
.
prototype
,
{
// Ensure it can trigger events with Backbone.Events
_
.
extend
(
Marionette
.
Object
.
prototype
,
Backbone
.
Events
,
{
//this is a noop method intended to be overridden by classes that extend from this base
initialize
:
function
()
{},
...
...
@@ -922,25 +1024,26 @@
// Proxy `getOption` to enable getting options from this or this.options by name.
getOption
:
Marionette
.
proxyGetOption
,
// Proxy `
un
bindEntityEvents` to enable binding view's events from another entity.
// Proxy `bindEntityEvents` to enable binding view's events from another entity.
bindEntityEvents
:
Marionette
.
proxyBindEntityEvents
,
// Proxy `unbindEntityEvents` to enable unbinding view's events from another entity.
unbindEntityEvents
:
Marionette
.
proxyUnbindEntityEvents
});
// Ensure it can trigger events with Backbone.Events
_
.
extend
(
Marionette
.
Object
.
prototype
,
Backbone
.
Events
);
/* jshint maxcomplexity: 10, maxstatements: 29 */
/* jshint maxcomplexity: 16, maxstatements: 45, maxlen: 120 */
// Region
// ------
//
// Manage the visual regions of your composite application. See
// http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
Marionette
.
Region
=
function
(
options
)
{
Marionette
.
Region
=
Marionette
.
Object
.
extend
({
constructor
:
function
(
options
)
{
// set options temporarily so that we can get `el`.
// options will be overriden by Object.constructor
this
.
options
=
options
||
{};
this
.
el
=
this
.
getOption
(
'
el
'
);
...
...
@@ -948,107 +1051,16 @@
this
.
el
=
this
.
el
instanceof
Backbone
.
$
?
this
.
el
[
0
]
:
this
.
el
;
if
(
!
this
.
el
)
{
throwError
(
'
An "el" must be specified for a region.
'
,
'
NoElError
'
);
throw
new
Marionette
.
Error
({
name
:
'
NoElError
'
,
message
:
'
An "el" must be specified for a region.
'
});
}
this
.
$el
=
this
.
getEl
(
this
.
el
);
if
(
this
.
initialize
)
{
var
args
=
slice
.
apply
(
arguments
);
this
.
initialize
.
apply
(
this
,
args
);
}
};
// Region Class methods
// -------------------
_
.
extend
(
Marionette
.
Region
,
{
// Build an instance of a region by passing in a configuration object
// and a default region class to use if none is specified in the config.
//
// The config object should either be a string as a jQuery DOM selector,
// a Region class directly, or an object literal that specifies both
// a selector and regionClass:
//
// ```js
// {
// selector: "#foo",
// regionClass: MyCustomRegion
// }
// ```
//
buildRegion
:
function
(
regionConfig
,
DefaultRegionClass
)
{
if
(
_
.
isString
(
regionConfig
))
{
return
this
.
_buildRegionFromSelector
(
regionConfig
,
DefaultRegionClass
);
}
if
(
regionConfig
.
selector
||
regionConfig
.
el
||
regionConfig
.
regionClass
)
{
return
this
.
_buildRegionFromObject
(
regionConfig
,
DefaultRegionClass
);
}
if
(
_
.
isFunction
(
regionConfig
))
{
return
this
.
_buildRegionFromRegionClass
(
regionConfig
);
}
throwError
(
'
Improper region configuration type. Please refer
'
+
'
to http://marionettejs.com/docs/marionette.region.html#region-configuration-types
'
);
Marionette
.
Object
.
call
(
this
,
options
);
},
// Build the region from a string selector like '#foo-region'
_buildRegionFromSelector
:
function
(
selector
,
DefaultRegionClass
)
{
return
new
DefaultRegionClass
({
el
:
selector
});
},
// Build the region from a configuration object
// ```js
// { selector: '#foo', regionClass: FooRegion }
// ```
_buildRegionFromObject
:
function
(
regionConfig
,
DefaultRegionClass
)
{
var
RegionClass
=
regionConfig
.
regionClass
||
DefaultRegionClass
;
var
options
=
_
.
omit
(
regionConfig
,
'
selector
'
,
'
regionClass
'
);
if
(
regionConfig
.
selector
&&
!
options
.
el
)
{
options
.
el
=
regionConfig
.
selector
;
}
var
region
=
new
RegionClass
(
options
);
// override the `getEl` function if we have a parentEl
// this must be overridden to ensure the selector is found
// on the first use of the region. if we try to assign the
// region's `el` to `parentEl.find(selector)` in the object
// literal to build the region, the element will not be
// guaranteed to be in the DOM already, and will cause problems
if
(
regionConfig
.
parentEl
)
{
region
.
getEl
=
function
(
el
)
{
if
(
_
.
isObject
(
el
))
{
return
Backbone
.
$
(
el
);
}
var
parentEl
=
regionConfig
.
parentEl
;
if
(
_
.
isFunction
(
parentEl
))
{
parentEl
=
parentEl
();
}
return
parentEl
.
find
(
el
);
};
}
return
region
;
},
// Build the region directly from a given `RegionClass`
_buildRegionFromRegionClass
:
function
(
RegionClass
)
{
return
new
RegionClass
();
}
});
// Region Instance Methods
// -----------------------
_
.
extend
(
Marionette
.
Region
.
prototype
,
Backbone
.
Events
,
{
// Displays a backbone view instance inside of the region.
// Handles calling the `render` method for you. Reads content
// directly from the `el` attribute. Also calls an optional
...
...
@@ -1058,27 +1070,47 @@
// the old view being destroyed on show.
// The `forceShow` option can be used to force a view to be
// re-rendered if it's already shown in the region.
show
:
function
(
view
,
options
){
this
.
_ensureElement
();
if
(
!
this
.
_ensureElement
())
{
return
;
}
this
.
_ensureViewIsIntact
(
view
);
var
showOptions
=
options
||
{};
var
isDifferentView
=
view
!==
this
.
currentView
;
var
preventDestroy
=
!!
showOptions
.
preventDestroy
;
var
forceShow
=
!!
showOptions
.
forceShow
;
//
we are only changing the view if there is a
view to change to begin with
//
We are only changing the view if there is a current
view to change to begin with
var
isChangingView
=
!!
this
.
currentView
;
// only destroy the view if we don't want to preventDestroy and the view is different
var
_shouldDestroyView
=
!
preventDestroy
&&
isDifferentView
;
// Only destroy the current view if we don't want to `preventDestroy` and if
// the view given in the first argument is different than `currentView`
var
_shouldDestroyView
=
isDifferentView
&&
!
preventDestroy
;
// Only show the view given in the first argument if it is different than
// the current view or if we want to re-show the view. Note that if
// `_shouldDestroyView` is true, then `_shouldShowView` is also necessarily true.
var
_shouldShowView
=
isDifferentView
||
forceShow
;
if
(
isChangingView
)
{
this
.
triggerMethod
(
'
before:swapOut
'
,
this
.
currentView
,
this
,
options
);
}
if
(
this
.
currentView
)
{
delete
this
.
currentView
.
_parent
;
}
if
(
_shouldDestroyView
)
{
this
.
empty
();
}
// show the view if the view is different or if you want to re-show the view
var
_shouldShowView
=
isDifferentView
||
forceShow
;
// A `destroy` event is attached to the clean up manually removed views.
// We need to detach this event when a new view is going to be shown as it
// is no longer relevant.
}
else
if
(
isChangingView
&&
_shouldShowView
)
{
this
.
currentView
.
off
(
'
destroy
'
,
this
.
empty
,
this
);
}
if
(
_shouldShowView
)
{
...
...
@@ -1087,42 +1119,73 @@
// If this happens we need to remove the reference
// to the currentView since once a view has been destroyed
// we can not reuse it.
view
.
once
(
'
destroy
'
,
_
.
bind
(
this
.
empty
,
this
)
);
view
.
once
(
'
destroy
'
,
this
.
empty
,
this
);
view
.
render
();
view
.
_parent
=
this
;
if
(
isChangingView
)
{
this
.
triggerMethod
(
'
before:swap
'
,
view
);
this
.
triggerMethod
(
'
before:swap
'
,
view
,
this
,
options
);
}
this
.
triggerMethod
(
'
before:show
'
,
view
);
this
.
triggerMethod
(
'
before:show
'
,
view
,
this
,
options
);
Marionette
.
triggerMethodOn
(
view
,
'
before:show
'
,
view
,
this
,
options
);
if
(
_
.
isFunction
(
view
.
triggerMethod
))
{
view
.
triggerMethod
(
'
before:show
'
);
}
else
{
this
.
triggerMethod
.
call
(
view
,
'
before:show
'
);
if
(
isChangingView
)
{
this
.
triggerMethod
(
'
swapOut
'
,
this
.
currentView
,
this
,
options
);
}
// An array of views that we're about to display
var
attachedRegion
=
Marionette
.
isNodeAttached
(
this
.
el
);
// The views that we're about to attach to the document
// It's important that we prevent _getNestedViews from being executed unnecessarily
// as it's a potentially-slow method
var
displayedViews
=
[];
var
triggerBeforeAttach
=
showOptions
.
triggerBeforeAttach
||
this
.
triggerBeforeAttach
;
var
triggerAttach
=
showOptions
.
triggerAttach
||
this
.
triggerAttach
;
if
(
attachedRegion
&&
triggerBeforeAttach
)
{
displayedViews
=
this
.
_displayedViews
(
view
);
this
.
_triggerAttach
(
displayedViews
,
'
before:
'
);
}
this
.
attachHtml
(
view
);
this
.
currentView
=
view
;
if
(
isChangingView
)
{
this
.
triggerMethod
(
'
swap
'
,
view
);
if
(
attachedRegion
&&
triggerAttach
)
{
displayedViews
=
this
.
_displayedViews
(
view
);
this
.
_triggerAttach
(
displayedViews
);
}
this
.
triggerMethod
(
'
show
'
,
view
);
if
(
_
.
isFunction
(
view
.
triggerMethod
))
{
view
.
triggerMethod
(
'
show
'
);
}
else
{
this
.
triggerMethod
.
call
(
view
,
'
show
'
);
if
(
isChangingView
)
{
this
.
triggerMethod
(
'
swap
'
,
view
,
this
,
options
);
}
this
.
triggerMethod
(
'
show
'
,
view
,
this
,
options
);
Marionette
.
triggerMethodOn
(
view
,
'
show
'
,
view
,
this
,
options
);
return
this
;
}
return
this
;
},
triggerBeforeAttach
:
true
,
triggerAttach
:
true
,
_triggerAttach
:
function
(
views
,
prefix
)
{
var
eventName
=
(
prefix
||
''
)
+
'
attach
'
;
_
.
each
(
views
,
function
(
view
)
{
Marionette
.
triggerMethodOn
(
view
,
eventName
,
view
,
this
);
},
this
);
},
_displayedViews
:
function
(
view
)
{
return
_
.
union
([
view
],
_
.
result
(
view
,
'
_getNestedViews
'
)
||
[]);
},
_ensureElement
:
function
(){
if
(
!
_
.
isObject
(
this
.
el
))
{
this
.
$el
=
this
.
getEl
(
this
.
el
);
...
...
@@ -1130,21 +1193,43 @@
}
if
(
!
this
.
$el
||
this
.
$el
.
length
===
0
)
{
throwError
(
'
An "el"
'
+
this
.
$el
.
selector
+
'
must exist in DOM
'
);
if
(
this
.
getOption
(
'
allowMissingEl
'
))
{
return
false
;
}
else
{
throw
new
Marionette
.
Error
(
'
An "el"
'
+
this
.
$el
.
selector
+
'
must exist in DOM
'
);
}
}
return
true
;
},
// Override this method to change how the region finds the
// DOM element that it manages. Return a jQuery selector object.
_ensureViewIsIntact
:
function
(
view
)
{
if
(
!
view
)
{
throw
new
Marionette
.
Error
({
name
:
'
ViewNotValid
'
,
message
:
'
The view passed is undefined and therefore invalid. You must pass a view instance to show.
'
});
}
if
(
view
.
isDestroyed
)
{
throw
new
Marionette
.
Error
({
name
:
'
ViewDestroyedError
'
,
message
:
'
View (cid: "
'
+
view
.
cid
+
'
") has already been destroyed and cannot be used.
'
});
}
},
// Override this method to change how the region finds the DOM
// element that it manages. Return a jQuery selector object scoped
// to a provided parent el or the document if none exists.
getEl
:
function
(
el
)
{
return
Backbone
.
$
(
el
);
return
Backbone
.
$
(
el
,
Marionette
.
_getValue
(
this
.
options
.
parentEl
,
this
)
);
},
// Override this method to change how the new view is
// appended to the `$el` that the region is managing
attachHtml
:
function
(
view
)
{
// empty the node and append new view
this
.
el
.
innerHTML
=
''
;
this
.
$el
.
contents
().
detach
();
this
.
el
.
appendChild
(
view
.
el
);
},
...
...
@@ -1157,6 +1242,7 @@
// we should not remove anything
if
(
!
view
)
{
return
;
}
view
.
off
(
'
destroy
'
,
this
.
empty
,
this
);
this
.
triggerMethod
(
'
before:empty
'
,
view
);
this
.
_destroyView
();
this
.
triggerMethod
(
'
empty
'
,
view
);
...
...
@@ -1175,6 +1261,10 @@
view
.
destroy
();
}
else
if
(
view
.
remove
)
{
view
.
remove
();
// appending isDestroyed to raw Backbone View allows regions
// to throw a ViewDestroyedError for this view
view
.
isDestroyed
=
true
;
}
},
...
...
@@ -1207,29 +1297,84 @@
delete
this
.
$el
;
return
this
;
}
},
// Proxy `getOption` to enable getting options from this or this.options by name.
getOption
:
Marionette
.
proxyGetOption
,
// Static Methods
{
// import the `triggerMethod` to trigger events with corresponding
// methods if the method exists
triggerMethod
:
Marionette
.
triggerMethod
// Build an instance of a region by passing in a configuration object
// and a default region class to use if none is specified in the config.
//
// The config object should either be a string as a jQuery DOM selector,
// a Region class directly, or an object literal that specifies a selector,
// a custom regionClass, and any options to be supplied to the region:
//
// ```js
// {
// selector: "#foo",
// regionClass: MyCustomRegion,
// allowMissingEl: false
// }
// ```
//
buildRegion
:
function
(
regionConfig
,
DefaultRegionClass
)
{
if
(
_
.
isString
(
regionConfig
))
{
return
this
.
_buildRegionFromSelector
(
regionConfig
,
DefaultRegionClass
);
}
if
(
regionConfig
.
selector
||
regionConfig
.
el
||
regionConfig
.
regionClass
)
{
return
this
.
_buildRegionFromObject
(
regionConfig
,
DefaultRegionClass
);
}
if
(
_
.
isFunction
(
regionConfig
))
{
return
this
.
_buildRegionFromRegionClass
(
regionConfig
);
}
throw
new
Marionette
.
Error
({
message
:
'
Improper region configuration type.
'
,
url
:
'
marionette.region.html#region-configuration-types
'
});
},
// Copy the `extend` function used by Backbone's classes
Marionette
.
Region
.
extend
=
Marionette
.
extend
;
// Build the region from a string selector like '#foo-region'
_buildRegionFromSelector
:
function
(
selector
,
DefaultRegionClass
)
{
return
new
DefaultRegionClass
({
el
:
selector
});
},
// Marionette.RegionManager
// ------------------------
//
// Manage one or more related `Marionette.Region` objects.
Marionette
.
RegionManager
=
(
function
(
Marionette
)
{
// Build the region from a configuration object
// ```js
// { selector: '#foo', regionClass: FooRegion, allowMissingEl: false }
// ```
_buildRegionFromObject
:
function
(
regionConfig
,
DefaultRegionClass
)
{
var
RegionClass
=
regionConfig
.
regionClass
||
DefaultRegionClass
;
var
options
=
_
.
omit
(
regionConfig
,
'
selector
'
,
'
regionClass
'
);
if
(
regionConfig
.
selector
&&
!
options
.
el
)
{
options
.
el
=
regionConfig
.
selector
;
}
var
RegionManager
=
Marionette
.
Controller
.
extend
({
return
new
RegionClass
(
options
);
},
// Build the region directly from a given `RegionClass`
_buildRegionFromRegionClass
:
function
(
RegionClass
)
{
return
new
RegionClass
();
}
});
// Region Manager
// --------------
// Manage one or more related `Marionette.Region` objects.
Marionette
.
RegionManager
=
Marionette
.
Controller
.
extend
({
constructor
:
function
(
options
)
{
this
.
_regions
=
{};
Marionette
.
Controller
.
call
(
this
,
options
);
this
.
addRegions
(
this
.
getOption
(
'
regions
'
));
},
// Add multiple regions using an object literal or a
...
...
@@ -1237,26 +1382,19 @@
// each key becomes the region name, and each value is
// the region definition.
addRegions
:
function
(
regionDefinitions
,
defaults
)
{
if
(
_
.
isFunction
(
regionDefinitions
))
{
regionDefinitions
=
regionDefinitions
.
apply
(
this
,
arguments
);
}
regionDefinitions
=
Marionette
.
_getValue
(
regionDefinitions
,
this
,
arguments
);
var
regions
=
{};
_
.
each
(
regionDefinitions
,
function
(
definition
,
name
)
{
return
_
.
reduce
(
regionDefinitions
,
function
(
regions
,
definition
,
name
)
{
if
(
_
.
isString
(
definition
))
{
definition
=
{
selector
:
definition
};
}
if
(
definition
.
selector
)
{
definition
=
_
.
defaults
({},
definition
,
defaults
);
}
var
region
=
this
.
addRegion
(
name
,
definition
);
regions
[
name
]
=
region
;
},
this
);
regions
[
name
]
=
this
.
addRegion
(
name
,
definition
);
return
regions
;
},
{},
this
);
},
// Add an individual region to the region manager,
...
...
@@ -1264,20 +1402,15 @@
addRegion
:
function
(
name
,
definition
)
{
var
region
;
var
isObject
=
_
.
isObject
(
definition
);
var
isString
=
_
.
isString
(
definition
);
var
hasSelector
=
!!
definition
.
selector
;
if
(
isString
||
(
isObject
&&
hasSelector
))
{
region
=
Marionette
.
Region
.
buildRegion
(
definition
,
Marionette
.
Region
);
}
else
if
(
_
.
isFunction
(
definition
))
{
region
=
Marionette
.
Region
.
buildRegion
(
definition
,
Marionette
.
Region
);
}
else
{
if
(
definition
instanceof
Marionette
.
Region
)
{
region
=
definition
;
}
else
{
region
=
Marionette
.
Region
.
buildRegion
(
definition
,
Marionette
.
Region
);
}
this
.
triggerMethod
(
'
before:add:region
'
,
name
,
region
);
region
.
_parent
=
this
;
this
.
_store
(
name
,
region
);
this
.
triggerMethod
(
'
add:region
'
,
name
,
region
);
...
...
@@ -1318,10 +1451,7 @@
// leave them attached
emptyRegions
:
function
()
{
var
regions
=
this
.
getRegions
();
_
.
each
(
regions
,
function
(
region
)
{
region
.
empty
();
},
this
);
_
.
invoke
(
regions
,
'
empty
'
);
return
regions
;
},
...
...
@@ -1343,6 +1473,8 @@
this
.
triggerMethod
(
'
before:remove:region
'
,
name
,
region
);
region
.
empty
();
region
.
stopListening
();
delete
region
.
_parent
;
delete
this
.
_regions
[
name
];
this
.
_setLength
();
this
.
triggerMethod
(
'
remove:region
'
,
name
,
region
);
...
...
@@ -1352,13 +1484,9 @@
_setLength
:
function
()
{
this
.
length
=
_
.
size
(
this
.
_regions
);
}
});
Marionette
.
actAsCollection
(
RegionManager
.
prototype
,
'
_regions
'
);
return
RegionManager
;
})(
Marionette
);
Marionette
.
actAsCollection
(
Marionette
.
RegionManager
.
prototype
,
'
_regions
'
);
// Template Cache
...
...
@@ -1399,7 +1527,7 @@
// `clear("#t1", "#t2", "...")`
clear
:
function
()
{
var
i
;
var
args
=
slice
.
call
(
arguments
);
var
args
=
_
.
toArray
(
arguments
);
var
length
=
args
.
length
;
if
(
length
>
0
)
{
...
...
@@ -1440,7 +1568,10 @@
var
template
=
Backbone
.
$
(
templateId
).
html
();
if
(
!
template
||
template
.
length
===
0
)
{
throwError
(
'
Could not find template: "
'
+
templateId
+
'
"
'
,
'
NoTemplateError
'
);
throw
new
Marionette
.
Error
({
name
:
'
NoTemplateError
'
,
message
:
'
Could not find template: "
'
+
templateId
+
'
"
'
});
}
return
template
;
...
...
@@ -1468,16 +1599,13 @@
// custom rendering and template handling for all of Marionette.
render
:
function
(
template
,
data
)
{
if
(
!
template
)
{
throwError
(
'
Cannot render the template since its false, null or undefined.
'
,
'
TemplateNotFoundError
'
);
throw
new
Marionette
.
Error
({
name
:
'
TemplateNotFoundError
'
,
message
:
'
Cannot render the template since its false, null or undefined.
'
});
}
var
templateFunc
;
if
(
typeof
template
===
'
function
'
)
{
templateFunc
=
template
;
}
else
{
templateFunc
=
Marionette
.
TemplateCache
.
get
(
template
);
}
var
templateFunc
=
_
.
isFunction
(
template
)
?
template
:
Marionette
.
TemplateCache
.
get
(
template
);
return
templateFunc
(
data
);
}
...
...
@@ -1485,31 +1613,30 @@
/* jshint maxlen: 114, nonew: false */
//
Marionette.
View
// ----
-----------
// View
// ----
// The core view class that other Marionette views extend from.
Marionette
.
View
=
Backbone
.
View
.
extend
({
isDestroyed
:
false
,
constructor
:
function
(
options
)
{
_
.
bindAll
(
this
,
'
render
'
);
options
=
Marionette
.
_getValue
(
options
,
this
);
// this exposes view options to the view initializer
// this is a backfill since backbone removed the assignment
// of this.options
// at some point however this may be removed
this
.
options
=
_
.
extend
({},
_
.
result
(
this
,
'
options
'
),
_
.
isFunction
(
options
)
?
options
.
call
(
this
)
:
options
);
// parses out the @ui DSL for events
this
.
events
=
this
.
normalizeUIKeys
(
_
.
result
(
this
,
'
events
'
));
this
.
options
=
_
.
extend
({},
_
.
result
(
this
,
'
options
'
),
options
);
if
(
_
.
isObject
(
this
.
behaviors
))
{
new
Marionette
.
Behaviors
(
this
);
}
this
.
_behaviors
=
Marionette
.
Behaviors
(
this
);
Backbone
.
View
.
apply
(
this
,
arguments
);
Marionette
.
MonitorDOMRefresh
(
this
);
this
.
listenTo
(
this
,
'
show
'
,
this
.
onShowCalled
);
this
.
on
(
'
show
'
,
this
.
onShowCalled
);
},
// Get the template for this view
...
...
@@ -1523,7 +1650,7 @@
// Serialize a model by returning its attributes. Clones
// the attributes to allow modification.
serializeModel
:
function
(
model
){
return
model
.
toJSON
.
apply
(
model
,
slice
.
call
(
arguments
,
1
));
return
model
.
toJSON
.
apply
(
model
,
_
.
rest
(
arguments
));
},
// Mix in template helper methods. Looks for a
...
...
@@ -1534,65 +1661,39 @@
mixinTemplateHelpers
:
function
(
target
)
{
target
=
target
||
{};
var
templateHelpers
=
this
.
getOption
(
'
templateHelpers
'
);
if
(
_
.
isFunction
(
templateHelpers
))
{
templateHelpers
=
templateHelpers
.
call
(
this
);
}
templateHelpers
=
Marionette
.
_getValue
(
templateHelpers
,
this
);
return
_
.
extend
(
target
,
templateHelpers
);
},
// normalize the keys of passed hash with the views `ui` selectors.
// `{"@ui.foo": "bar"}`
normalizeUIKeys
:
function
(
hash
)
{
var
uiBindings
=
_
.
result
(
this
,
'
_uiBindings
'
);
return
Marionette
.
normalizeUIKeys
(
hash
,
uiBindings
||
_
.
result
(
this
,
'
ui
'
));
},
// normalize the values of passed hash with the views `ui` selectors.
// `{foo: "@ui.bar"}`
normalizeUIValues
:
function
(
hash
)
{
var
ui
=
_
.
result
(
this
,
'
ui
'
);
var
uiBindings
=
_
.
result
(
this
,
'
_uiBindings
'
);
return
Marionette
.
normalizeUI
Key
s
(
hash
,
uiBindings
||
ui
);
return
Marionette
.
normalizeUI
Value
s
(
hash
,
uiBindings
||
ui
);
},
// Configure `triggers` to forward DOM events to view
// events. `triggers: {"click .foo": "do:foo"}`
configureTriggers
:
function
()
{
if
(
!
this
.
triggers
)
{
return
;
}
var
triggerEvents
=
{};
// Allow `triggers` to be configured as a function
var
triggers
=
this
.
normalizeUIKeys
(
_
.
result
(
this
,
'
triggers
'
));
// Configure the triggers, prevent default
// action and stop propagation of DOM events
_
.
each
(
triggers
,
function
(
value
,
key
)
{
var
hasOptions
=
_
.
isObject
(
value
);
var
eventName
=
hasOptions
?
value
.
event
:
value
;
// build the event handler function for the DOM event
triggerEvents
[
key
]
=
function
(
e
)
{
// stop the event in its tracks
if
(
e
)
{
var
prevent
=
e
.
preventDefault
;
var
stop
=
e
.
stopPropagation
;
var
shouldPrevent
=
hasOptions
?
value
.
preventDefault
:
prevent
;
var
shouldStop
=
hasOptions
?
value
.
stopPropagation
:
stop
;
if
(
shouldPrevent
&&
prevent
)
{
prevent
.
apply
(
e
);
}
if
(
shouldStop
&&
stop
)
{
stop
.
apply
(
e
);
}
}
// build the args for the event
var
args
=
{
view
:
this
,
model
:
this
.
model
,
collection
:
this
.
collection
};
// trigger the event
this
.
triggerMethod
(
eventName
,
args
);
};
configureTriggers
:
function
()
{
if
(
!
this
.
triggers
)
{
return
;
}
},
this
);
// Allow `triggers` to be configured as a function
var
triggers
=
this
.
normalizeUIKeys
(
_
.
result
(
this
,
'
triggers
'
));
return
triggerEvents
;
// Configure the triggers, prevent default
// action and stop propagation of DOM events
return
_
.
reduce
(
triggers
,
function
(
events
,
value
,
key
)
{
events
[
key
]
=
this
.
_buildViewTrigger
(
value
);
return
events
;
},
{},
this
);
},
// Overriding Backbone.View's delegateEvents to handle
...
...
@@ -1601,25 +1702,32 @@
this
.
_delegateDOMEvents
(
events
);
this
.
bindEntityEvents
(
this
.
model
,
this
.
getOption
(
'
modelEvents
'
));
this
.
bindEntityEvents
(
this
.
collection
,
this
.
getOption
(
'
collectionEvents
'
));
_
.
each
(
this
.
_behaviors
,
function
(
behavior
)
{
behavior
.
bindEntityEvents
(
this
.
model
,
behavior
.
getOption
(
'
modelEvents
'
));
behavior
.
bindEntityEvents
(
this
.
collection
,
behavior
.
getOption
(
'
collectionEvents
'
));
},
this
);
return
this
;
},
// internal method to delegate DOM events and triggers
_delegateDOMEvents
:
function
(
events
)
{
events
=
events
||
this
.
events
;
if
(
_
.
isFunction
(
events
))
{
events
=
events
.
call
(
this
);
}
_delegateDOMEvents
:
function
(
eventsArg
)
{
var
events
=
Marionette
.
_getValue
(
eventsArg
||
this
.
events
,
this
);
// normalize ui keys
events
=
this
.
normalizeUIKeys
(
events
);
if
(
_
.
isUndefined
(
eventsArg
))
{
this
.
events
=
events
;}
var
combinedEvents
=
{};
// look up if this view has behavior events
var
behaviorEvents
=
_
.
result
(
this
,
'
behaviorEvents
'
)
||
{};
var
triggers
=
this
.
configureTriggers
();
var
behaviorTriggers
=
_
.
result
(
this
,
'
behaviorTriggers
'
)
||
{};
// behavior events will be overriden by view events and or triggers
_
.
extend
(
combinedEvents
,
behaviorEvents
,
events
,
triggers
);
_
.
extend
(
combinedEvents
,
behaviorEvents
,
events
,
triggers
,
behaviorTriggers
);
Backbone
.
View
.
prototype
.
delegateEvents
.
call
(
this
,
combinedEvents
);
},
...
...
@@ -1627,10 +1735,16 @@
// Overriding Backbone.View's undelegateEvents to handle unbinding
// the `triggers`, `modelEvents`, and `collectionEvents` config
undelegateEvents
:
function
()
{
var
args
=
slice
.
call
(
arguments
);
Backbone
.
View
.
prototype
.
undelegateEvents
.
apply
(
this
,
args
);
Backbone
.
View
.
prototype
.
undelegateEvents
.
apply
(
this
,
arguments
);
this
.
unbindEntityEvents
(
this
.
model
,
this
.
getOption
(
'
modelEvents
'
));
this
.
unbindEntityEvents
(
this
.
collection
,
this
.
getOption
(
'
collectionEvents
'
));
_
.
each
(
this
.
_behaviors
,
function
(
behavior
)
{
behavior
.
unbindEntityEvents
(
this
.
model
,
behavior
.
getOption
(
'
modelEvents
'
));
behavior
.
unbindEntityEvents
(
this
.
collection
,
behavior
.
getOption
(
'
collectionEvents
'
));
},
this
);
return
this
;
},
...
...
@@ -1640,9 +1754,10 @@
// Internal helper method to verify whether the view hasn't been destroyed
_ensureViewIsIntact
:
function
()
{
if
(
this
.
isDestroyed
)
{
var
err
=
new
Error
(
'
Cannot use a view thats already been destroyed.
'
);
err
.
name
=
'
ViewDestroyedError
'
;
throw
err
;
throw
new
Marionette
.
Error
({
name
:
'
ViewDestroyedError
'
,
message
:
'
View (cid: "
'
+
this
.
cid
+
'
") has already been destroyed and cannot be used.
'
});
}
},
...
...
@@ -1653,7 +1768,7 @@
destroy
:
function
()
{
if
(
this
.
isDestroyed
)
{
return
;
}
var
args
=
slice
.
call
(
arguments
);
var
args
=
_
.
toArray
(
arguments
);
this
.
triggerMethod
.
apply
(
this
,
[
'
before:destroy
'
].
concat
(
args
));
...
...
@@ -1668,12 +1783,24 @@
// remove the view from the DOM
this
.
remove
();
// Call destroy on each behavior after
// destroying the view.
// This unbinds event listeners
// that behaviors have registered for.
_
.
invoke
(
this
.
_behaviors
,
'
destroy
'
,
args
);
return
this
;
},
bindUIElements
:
function
()
{
this
.
_bindUIElements
();
_
.
invoke
(
this
.
_behaviors
,
this
.
_bindUIElements
);
},
// This method binds the elements specified in the "ui" hash inside the view's code with
// the associated jQuery selectors.
bindUIElements
:
function
()
{
_
bindUIElements
:
function
()
{
if
(
!
this
.
ui
)
{
return
;
}
// store the ui hash in _uiBindings so they can be reset later
...
...
@@ -1689,14 +1816,18 @@
this
.
ui
=
{};
// bind each of the selectors
_
.
each
(
_
.
keys
(
bindings
),
function
(
key
)
{
var
selector
=
bindings
[
key
];
_
.
each
(
bindings
,
function
(
selector
,
key
)
{
this
.
ui
[
key
]
=
this
.
$
(
selector
);
},
this
);
},
// This method unbinds the elements specified in the "ui" hash
unbindUIElements
:
function
()
{
this
.
_unbindUIElements
();
_
.
invoke
(
this
.
_behaviors
,
this
.
_unbindUIElements
);
},
_unbindUIElements
:
function
()
{
if
(
!
this
.
ui
||
!
this
.
_uiBindings
)
{
return
;
}
// delete all of the existing ui bindings
...
...
@@ -1709,9 +1840,81 @@
delete
this
.
_uiBindings
;
},
// Internal method to create an event handler for a given `triggerDef` like
// 'click:foo'
_buildViewTrigger
:
function
(
triggerDef
)
{
var
hasOptions
=
_
.
isObject
(
triggerDef
);
var
options
=
_
.
defaults
({},
(
hasOptions
?
triggerDef
:
{}),
{
preventDefault
:
true
,
stopPropagation
:
true
});
var
eventName
=
hasOptions
?
options
.
event
:
triggerDef
;
return
function
(
e
)
{
if
(
e
)
{
if
(
e
.
preventDefault
&&
options
.
preventDefault
)
{
e
.
preventDefault
();
}
if
(
e
.
stopPropagation
&&
options
.
stopPropagation
)
{
e
.
stopPropagation
();
}
}
var
args
=
{
view
:
this
,
model
:
this
.
model
,
collection
:
this
.
collection
};
this
.
triggerMethod
(
eventName
,
args
);
};
},
setElement
:
function
()
{
var
ret
=
Backbone
.
View
.
prototype
.
setElement
.
apply
(
this
,
arguments
);
// proxy behavior $el to the view's $el.
// This is needed because a view's $el proxy
// is not set until after setElement is called.
_
.
invoke
(
this
.
_behaviors
,
'
proxyViewProperties
'
,
this
);
return
ret
;
},
// import the `triggerMethod` to trigger events with corresponding
// methods if the method exists
triggerMethod
:
Marionette
.
triggerMethod
,
triggerMethod
:
function
()
{
var
triggerMethod
=
Marionette
.
_triggerMethod
;
var
ret
=
triggerMethod
(
this
,
arguments
);
var
behaviors
=
this
.
_behaviors
;
// Use good ol' for as this is a very hot function
for
(
var
i
=
0
,
length
=
behaviors
&&
behaviors
.
length
;
i
<
length
;
i
++
)
{
triggerMethod
(
behaviors
[
i
],
arguments
);
}
return
ret
;
},
// This method returns any views that are immediate
// children of this view
_getImmediateChildren
:
function
()
{
return
[];
},
// Returns an array of every nested view within this view
_getNestedViews
:
function
()
{
var
children
=
this
.
_getImmediateChildren
();
if
(
!
children
.
length
)
{
return
children
;
}
return
_
.
reduce
(
children
,
function
(
memo
,
view
)
{
if
(
!
view
.
_getNestedViews
)
{
return
memo
;
}
return
memo
.
concat
(
view
.
_getNestedViews
());
},
children
);
},
// Imports the "normalizeMethods" to transform hashes of
// events=>function references/names to a hash of events=>function references
...
...
@@ -1720,7 +1923,7 @@
// Proxy `getOption` to enable getting options from this or this.options by name.
getOption
:
Marionette
.
proxyGetOption
,
// Proxy `
un
bindEntityEvents` to enable binding view's events from another entity.
// Proxy `bindEntityEvents` to enable binding view's events from another entity.
bindEntityEvents
:
Marionette
.
proxyBindEntityEvents
,
// Proxy `unbindEntityEvents` to enable unbinding view's events from another entity.
...
...
@@ -1749,21 +1952,27 @@
// You can override the `serializeData` method in your own view definition,
// to provide custom serialization for your view's data.
serializeData
:
function
(){
var
data
=
{};
if
(
this
.
model
)
{
data
=
_
.
partial
(
this
.
serializeModel
,
this
.
model
).
apply
(
this
,
arguments
);
if
(
!
this
.
model
&&
!
this
.
collection
)
{
return
{};
}
else
if
(
this
.
collection
)
{
data
=
{
items
:
_
.
partial
(
this
.
serializeCollection
,
this
.
collection
).
apply
(
this
,
arguments
)
};
var
args
=
[
this
.
model
||
this
.
collection
];
if
(
arguments
.
length
)
{
args
.
push
.
apply
(
args
,
arguments
);
}
return
data
;
if
(
this
.
model
)
{
return
this
.
serializeModel
.
apply
(
this
,
args
);
}
else
{
return
{
items
:
this
.
serializeCollection
.
apply
(
this
,
args
)
};
}
},
// Serialize a collection by serializing each of its models.
serializeCollection
:
function
(
collection
){
return
collection
.
toJSON
.
apply
(
collection
,
slice
.
call
(
arguments
,
1
));
return
collection
.
toJSON
.
apply
(
collection
,
_
.
rest
(
arguments
));
},
// Render the view, defaulting to underscore.js templates.
...
...
@@ -1797,8 +2006,10 @@
}
if
(
!
template
)
{
throwError
(
'
Cannot render the template since it is null or undefined.
'
,
'
UndefinedTemplateError
'
);
throw
new
Marionette
.
Error
({
name
:
'
UndefinedTemplateError
'
,
message
:
'
Cannot render the template since it is null or undefined.
'
});
}
// Add in entity data and template helpers
...
...
@@ -1813,7 +2024,7 @@
},
// Attaches the content of a given view.
// This method can be overriden to optimize rendering,
// This method can be overrid
d
en to optimize rendering,
// or to render in a non standard way.
//
// For example, using `innerHTML` instead of `$el.html`
...
...
@@ -1828,14 +2039,6 @@
this
.
$el
.
html
(
html
);
return
this
;
},
// Override the default destroy event to add a few
// more events that are triggered.
destroy
:
function
()
{
if
(
this
.
isDestroyed
)
{
return
;
}
return
Marionette
.
View
.
prototype
.
destroy
.
apply
(
this
,
arguments
);
}
});
...
...
@@ -1858,13 +2061,15 @@
// This will fallback onto appending childView's to the end.
constructor
:
function
(
options
){
var
initOptions
=
options
||
{};
if
(
_
.
isUndefined
(
this
.
sort
)){
this
.
sort
=
_
.
isUndefined
(
initOptions
.
sort
)
?
true
:
initOptions
.
sort
;
}
this
.
once
(
'
render
'
,
this
.
_initialEvents
);
this
.
_initChildViewStorage
();
Marionette
.
View
.
apply
(
this
,
arguments
);
this
.
_initialEvents
();
this
.
initRenderBuffer
();
},
...
...
@@ -1891,23 +2096,24 @@
_triggerBeforeShowBufferedChildren
:
function
()
{
if
(
this
.
_isShown
)
{
_
.
invoke
(
this
.
_bufferedChildren
,
'
triggerMethod
'
,
'
before:show
'
);
_
.
each
(
this
.
_bufferedChildren
,
_
.
partial
(
this
.
_triggerMethodOnChild
,
'
before:show
'
)
);
}
},
_triggerShowBufferedChildren
:
function
()
{
if
(
this
.
_isShown
)
{
_
.
each
(
this
.
_bufferedChildren
,
function
(
child
)
{
if
(
_
.
isFunction
(
child
.
triggerMethod
))
{
child
.
triggerMethod
(
'
show
'
);
}
else
{
Marionette
.
triggerMethod
.
call
(
child
,
'
show
'
);
}
});
_
.
each
(
this
.
_bufferedChildren
,
_
.
partial
(
this
.
_triggerMethodOnChild
,
'
show
'
));
this
.
_bufferedChildren
=
[];
}
},
// Internal method for _.each loops to call `Marionette.triggerMethodOn` on
// a child view
_triggerMethodOnChild
:
function
(
event
,
childView
)
{
Marionette
.
triggerMethodOn
(
childView
,
event
);
},
// Configured the initial events that the collection view
// binds to.
_initialEvents
:
function
()
{
...
...
@@ -1938,14 +2144,8 @@
},
// Override from `Marionette.View` to trigger show on child views
onShowCalled
:
function
(){
this
.
children
.
each
(
function
(
child
){
if
(
_
.
isFunction
(
child
.
triggerMethod
))
{
child
.
triggerMethod
(
'
show
'
);
}
else
{
Marionette
.
triggerMethod
.
call
(
child
,
'
show
'
);
}
});
onShowCalled
:
function
()
{
this
.
children
.
each
(
_
.
partial
(
this
.
_triggerMethodOnChild
,
'
show
'
));
},
// Render children views. Override this method to
...
...
@@ -1981,6 +2181,9 @@
}
},
// Internal reference to what index a `emptyView` is.
_emptyViewIndex
:
-
1
,
// Internal method. Separated so that CompositeView can have
// more control over events being triggered, around the rendering
// process
...
...
@@ -2029,8 +2232,12 @@
// rendered empty, and then a child is added to the collection.
destroyEmptyView
:
function
()
{
if
(
this
.
_showingEmptyView
)
{
this
.
triggerMethod
(
'
before:remove:empty
'
);
this
.
destroyChildren
();
delete
this
.
_showingEmptyView
;
this
.
triggerMethod
(
'
remove:empty
'
);
}
},
...
...
@@ -2040,25 +2247,30 @@
},
// Render and show the emptyView. Similar to addChild method
// but "
child:adde
d" events are not fired, and the event from
// but "
add:chil
d" events are not fired, and the event from
// emptyView are not forwarded
addEmptyView
:
function
(
child
,
EmptyView
){
addEmptyView
:
function
(
child
,
EmptyView
)
{
// get the emptyViewOptions, falling back to childViewOptions
var
emptyViewOptions
=
this
.
getOption
(
'
emptyViewOptions
'
)
||
this
.
getOption
(
'
childViewOptions
'
);
if
(
_
.
isFunction
(
emptyViewOptions
)){
emptyViewOptions
=
emptyViewOptions
.
call
(
this
);
emptyViewOptions
=
emptyViewOptions
.
call
(
this
,
child
,
this
.
_emptyViewIndex
);
}
// build the empty view
var
view
=
this
.
buildChildView
(
child
,
EmptyView
,
emptyViewOptions
);
view
.
_parent
=
this
;
// Proxy emptyView events
this
.
proxyChildEvents
(
view
);
// trigger the 'before:show' event on `view` if the collection view
// has already been shown
if
(
this
.
_isShown
){
this
.
triggerMethod
.
call
(
view
,
'
before:show
'
);
if
(
this
.
_isShown
)
{
Marionette
.
triggerMethodOn
(
view
,
'
before:show
'
);
}
// Store the `emptyView` like a `childView` so we can properly
...
...
@@ -2066,12 +2278,12 @@
this
.
children
.
add
(
view
);
// Render it and show it
this
.
renderChildView
(
view
,
-
1
);
this
.
renderChildView
(
view
,
this
.
_emptyViewIndex
);
// call the 'show' method if the collection view
// has already been shown
if
(
this
.
_isShown
){
this
.
triggerMethod
.
call
(
view
,
'
show
'
);
if
(
this
.
_isShown
)
{
Marionette
.
triggerMethodOn
(
view
,
'
show
'
);
}
},
...
...
@@ -2085,7 +2297,10 @@
var
childView
=
this
.
getOption
(
'
childView
'
);
if
(
!
childView
)
{
throwError
(
'
A "childView" must be specified
'
,
'
NoChildViewError
'
);
throw
new
Marionette
.
Error
({
name
:
'
NoChildViewError
'
,
message
:
'
A "childView" must be specified
'
});
}
return
childView
;
...
...
@@ -2097,9 +2312,7 @@
// in order to keep the children in sync with the collection.
addChild
:
function
(
child
,
ChildView
,
index
)
{
var
childViewOptions
=
this
.
getOption
(
'
childViewOptions
'
);
if
(
_
.
isFunction
(
childViewOptions
))
{
childViewOptions
=
childViewOptions
.
call
(
this
,
child
,
index
);
}
childViewOptions
=
Marionette
.
_getValue
(
childViewOptions
,
this
,
[
child
,
index
]);
var
view
=
this
.
buildChildView
(
child
,
ChildView
,
childViewOptions
);
...
...
@@ -2108,6 +2321,8 @@
this
.
_addChildView
(
view
,
index
);
view
.
_parent
=
this
;
return
view
;
},
...
...
@@ -2121,22 +2336,14 @@
if
(
increment
)
{
// assign the index to the view
view
.
_index
=
index
;
// increment the index of views after this one
this
.
children
.
each
(
function
(
laterView
)
{
if
(
laterView
.
_index
>=
view
.
_index
)
{
laterView
.
_index
++
;
}
});
}
else
{
// decrement the index of views after this one
// update the indexes of views after this one
this
.
children
.
each
(
function
(
laterView
)
{
if
(
laterView
.
_index
>=
view
.
_index
)
{
laterView
.
_index
--
;
laterView
.
_index
+=
increment
?
1
:
-
1
;
}
});
}
},
...
...
@@ -2153,12 +2360,8 @@
this
.
children
.
add
(
view
);
this
.
renderChildView
(
view
,
index
);
if
(
this
.
_isShown
&&
!
this
.
isBuffering
){
if
(
_
.
isFunction
(
view
.
triggerMethod
))
{
view
.
triggerMethod
(
'
show
'
);
}
else
{
Marionette
.
triggerMethod
.
call
(
view
,
'
show
'
);
}
if
(
this
.
_isShown
&&
!
this
.
isBuffering
)
{
Marionette
.
triggerMethodOn
(
view
,
'
show
'
);
}
this
.
triggerMethod
(
'
add:child
'
,
view
);
...
...
@@ -2189,6 +2392,7 @@
if
(
view
.
destroy
)
{
view
.
destroy
();
}
else
if
(
view
.
remove
)
{
view
.
remove
();
}
delete
view
.
_parent
;
this
.
stopListening
(
view
);
this
.
children
.
remove
(
view
);
this
.
triggerMethod
(
'
remove:child
'
,
view
);
...
...
@@ -2201,7 +2405,7 @@
},
// check if the collection is empty
isEmpty
:
function
(
collection
)
{
isEmpty
:
function
()
{
return
!
this
.
collection
||
this
.
collection
.
length
===
0
;
},
...
...
@@ -2297,7 +2501,7 @@
// Forward all child view events through the parent,
// prepending "childview:" to the event name
this
.
listenTo
(
view
,
'
all
'
,
function
()
{
var
args
=
slice
.
call
(
arguments
);
var
args
=
_
.
toArray
(
arguments
);
var
rootEvent
=
args
[
0
];
var
childEvents
=
this
.
normalizeMethods
(
_
.
result
(
this
,
'
childEvents
'
));
...
...
@@ -2311,6 +2515,10 @@
this
.
triggerMethod
.
apply
(
this
,
args
);
},
this
);
},
_getImmediateChildren
:
function
()
{
return
_
.
values
(
this
.
children
.
_views
);
}
});
...
...
@@ -2340,7 +2548,7 @@
// Bind only after composite view is rendered to avoid adding child views
// to nonexistent childViewContainer
this
.
once
(
'
render
'
,
function
()
{
if
(
this
.
collection
)
{
this
.
listenTo
(
this
.
collection
,
'
add
'
,
this
.
_onCollectionAdd
);
this
.
listenTo
(
this
.
collection
,
'
remove
'
,
this
.
_onCollectionRemove
);
...
...
@@ -2350,8 +2558,6 @@
this
.
listenTo
(
this
.
collection
,
'
sort
'
,
this
.
_sortViews
);
}
}
});
},
// Retrieve the `childView` to be used when rendering each of
...
...
@@ -2361,14 +2567,10 @@
getChildView
:
function
(
child
)
{
var
childView
=
this
.
getOption
(
'
childView
'
)
||
this
.
constructor
;
if
(
!
childView
)
{
throwError
(
'
A "childView" must be specified
'
,
'
NoChildViewError
'
);
}
return
childView
;
},
// Serialize the
collection
for the view.
// Serialize the
model
for the view.
// You can override the `serializeData` method in your own view
// definition, to provide custom serialization for your view's data.
serializeData
:
function
()
{
...
...
@@ -2381,9 +2583,7 @@
return
data
;
},
// Renders the model once, and the collection once. Calling
// this again will tell the model's view to re-render itself
// but the collection will not re-render.
// Renders the model and the collection.
render
:
function
()
{
this
.
_ensureViewIsIntact
();
this
.
isRendered
=
true
;
...
...
@@ -2425,7 +2625,7 @@
},
// Attaches the content of the root.
// This method can be overriden to optimize rendering,
// This method can be overrid
d
en to optimize rendering,
// or to render in a non standard way.
//
// For example, using `innerHTML` instead of `$el.html`
...
...
@@ -2452,13 +2652,13 @@
// Overidden from CollectionView to ensure view is appended to
// childViewContainer
_insertAfter
:
function
(
childView
)
{
var
$container
=
this
.
getChildViewContainer
(
this
);
var
$container
=
this
.
getChildViewContainer
(
this
,
childView
);
$container
.
append
(
childView
.
el
);
},
// Internal method to ensure an `$childViewContainer` exists, for the
// `attachHtml` method to use.
getChildViewContainer
:
function
(
containerView
)
{
getChildViewContainer
:
function
(
containerView
,
childView
)
{
if
(
'
$childViewContainer
'
in
containerView
)
{
return
containerView
.
$childViewContainer
;
}
...
...
@@ -2467,7 +2667,7 @@
var
childViewContainer
=
Marionette
.
getOption
(
containerView
,
'
childViewContainer
'
);
if
(
childViewContainer
)
{
var
selector
=
_
.
isFunction
(
childViewContainer
)
?
childViewContainer
.
call
(
containerView
)
:
childViewContainer
;
var
selector
=
Marionette
.
_getValue
(
childViewContainer
,
containerView
)
;
if
(
selector
.
charAt
(
0
)
===
'
@
'
&&
containerView
.
ui
)
{
container
=
containerView
.
ui
[
selector
.
substr
(
4
)];
...
...
@@ -2476,8 +2676,10 @@
}
if
(
container
.
length
<=
0
)
{
throwError
(
'
The specified "childViewContainer" was not found:
'
+
containerView
.
childViewContainer
,
'
ChildViewContainerMissingError
'
);
throw
new
Marionette
.
Error
({
name
:
'
ChildViewContainerMissingError
'
,
message
:
'
The specified "childViewContainer" was not found:
'
+
containerView
.
childViewContainer
});
}
}
else
{
...
...
@@ -2496,8 +2698,8 @@
}
});
// LayoutView
// ----------
// Layout
View
// ----------
-
// Used for managing application layoutViews, nested layoutViews and
// multiple regions within an application or sub-application.
...
...
@@ -2549,7 +2751,6 @@
// Add a single region, by name, to the layoutView
addRegion
:
function
(
name
,
definition
)
{
this
.
triggerMethod
(
'
before:region:add
'
,
name
);
var
regions
=
{};
regions
[
name
]
=
definition
;
return
this
.
_buildRegions
(
regions
)[
name
];
...
...
@@ -2563,7 +2764,6 @@
// Remove a single region from the LayoutView, by name
removeRegion
:
function
(
name
)
{
this
.
triggerMethod
(
'
before:region:remove
'
,
name
);
delete
this
.
regions
[
name
];
return
this
.
regionManager
.
removeRegion
(
name
);
},
...
...
@@ -2582,11 +2782,9 @@
// internal method to build regions
_buildRegions
:
function
(
regions
)
{
var
that
=
this
;
var
defaults
=
{
regionClass
:
this
.
getOption
(
'
regionClass
'
),
parentEl
:
function
()
{
return
that
.
$el
;
}
parentEl
:
_
.
partial
(
_
.
result
,
this
,
'
el
'
)
};
return
this
.
regionManager
.
addRegions
(
regions
,
defaults
);
...
...
@@ -2598,36 +2796,31 @@
var
regions
;
this
.
_initRegionManager
();
if
(
_
.
isFunction
(
this
.
regions
))
{
regions
=
this
.
regions
(
options
);
}
else
{
regions
=
this
.
regions
||
{};
}
regions
=
Marionette
.
_getValue
(
this
.
regions
,
this
,
[
options
])
||
{};
// Enable users to define `regions` as instance options.
var
regionOptions
=
this
.
getOption
.
call
(
options
,
'
regions
'
);
// enable region options to be a function
if
(
_
.
isFunction
(
regionOptions
))
{
regionOptions
=
regionOptions
.
call
(
this
,
options
);
}
regionOptions
=
Marionette
.
_getValue
(
regionOptions
,
this
,
[
options
]);
_
.
extend
(
regions
,
regionOptions
);
// Normalize region selectors hash to allow
// a user to use the @ui. syntax.
regions
=
this
.
normalizeUIValues
(
regions
);
this
.
addRegions
(
regions
);
},
// Internal method to re-initialize all of the regions by updating the `el` that
// they point to
_reInitializeRegions
:
function
()
{
this
.
regionManager
.
emptyRegions
();
this
.
regionManager
.
each
(
function
(
region
)
{
region
.
reset
();
});
this
.
regionManager
.
invoke
(
'
reset
'
);
},
// Enable easy overiding of the default `RegionManager`
// for customized region interactions and bu
is
ness specific
// Enable easy over
r
iding of the default `RegionManager`
// for customized region interactions and bu
si
ness specific
// view logic for better control over single regions.
getRegionManager
:
function
()
{
return
new
Marionette
.
RegionManager
();
...
...
@@ -2637,6 +2830,7 @@
// and all regions in it
_initRegionManager
:
function
()
{
this
.
regionManager
=
this
.
getRegionManager
();
this
.
regionManager
.
_parent
=
this
;
this
.
listenTo
(
this
.
regionManager
,
'
before:add:region
'
,
function
(
name
)
{
this
.
triggerMethod
(
'
before:add:region
'
,
name
);
...
...
@@ -2655,20 +2849,27 @@
delete
this
[
name
];
this
.
triggerMethod
(
'
remove:region
'
,
name
,
region
);
});
},
_getImmediateChildren
:
function
()
{
return
_
.
chain
(
this
.
regionManager
.
getRegions
())
.
pluck
(
'
currentView
'
)
.
compact
()
.
value
();
}
});
// Behavior
// --------
---
// --------
// A Behavior is an isolated set of DOM /
// user interactions that can be mixed into any View.
// Behaviors allow you to blackbox View specific interactions
// into portable logical chunks, keeping your views simple and your code DRY.
Marionette
.
Behavior
=
(
function
(
_
,
Backbone
)
{
function
Behavior
(
options
,
view
)
{
Marionette
.
Behavior
=
Marionette
.
Object
.
extend
(
{
constructor
:
function
(
options
,
view
)
{
// Setup reference to the view.
// this comes in handle when a behavior
// wants to directly talk up the chain
...
...
@@ -2677,62 +2878,48 @@
this
.
defaults
=
_
.
result
(
this
,
'
defaults
'
)
||
{};
this
.
options
=
_
.
extend
({},
this
.
defaults
,
options
);
Marionette
.
Object
.
apply
(
this
,
arguments
);
},
// proxy behavior $ method to the view
// this is useful for doing jquery DOM lookups
// scoped to behaviors view.
this
.
$
=
function
()
{
$
:
function
()
{
return
this
.
view
.
$
.
apply
(
this
.
view
,
arguments
);
};
// Call the initialize method passing
// the arguments from the instance constructor
this
.
initialize
.
apply
(
this
,
arguments
);
}
_
.
extend
(
Behavior
.
prototype
,
Backbone
.
Events
,
{
initialize
:
function
()
{},
},
// stopListening to behavior `onListen` events.
// Stops the behavior from listening to events.
// Overrides Object#destroy to prevent additional events from being triggered.
destroy
:
function
()
{
this
.
stopListening
();
},
// import the `triggerMethod` to trigger events with corresponding
// methods if the method exists
triggerMethod
:
Marionette
.
triggerMethod
,
// Proxy `getOption` to enable getting options from this or this.options by name.
getOption
:
Marionette
.
proxyGetOption
,
// Proxy `unbindEntityEvents` to enable binding view's events from another entity.
bindEntityEvents
:
Marionette
.
proxyBindEntityEvents
,
// Proxy `unbindEntityEvents` to enable unbinding view's events from another entity.
unbindEntityEvents
:
Marionette
.
proxyUnbindEntityEvents
proxyViewProperties
:
function
(
view
)
{
this
.
$el
=
view
.
$el
;
this
.
el
=
view
.
el
;
}
});
// Borrow Backbones extend implementation
// this allows us to setup a proper
// inheritence pattern that follow in suite
// with the rest of Marionette views.
Behavior
.
extend
=
Marionette
.
extend
;
return
Behavior
;
})(
_
,
Backbone
);
/* jshint maxlen: 143, nonew: false */
// Marionette.Behaviors
// --------
/* jshint maxlen: 143 */
// Behaviors
// ---------
// Behaviors is a utility class that takes care of
// glu
e
ing your behavior instances to their given View.
// gluing your behavior instances to their given View.
// The most important part of this class is that you
// **MUST** override the class level behaviorsLookup
// method for things to work properly.
Marionette
.
Behaviors
=
(
function
(
Marionette
,
_
)
{
// Borrow event splitter from Backbone
var
delegateEventSplitter
=
/^
(\S
+
)\s
*
(
.*
)
$/
;
function
Behaviors
(
view
,
behaviors
)
{
if
(
!
_
.
isObject
(
view
.
behaviors
))
{
return
{};
}
// Behaviors defined on a view can be a flat object literal
// or it can be a function that returns an object.
behaviors
=
Behaviors
.
parseBehaviors
(
view
,
behaviors
||
_
.
result
(
view
,
'
behaviors
'
));
...
...
@@ -2741,86 +2928,23 @@
// calling the methods first on each behavior
// and then eventually calling the method on the view.
Behaviors
.
wrap
(
view
,
behaviors
,
_
.
keys
(
methods
));
return
behaviors
;
}
var
methods
=
{
setElement
:
function
(
setElement
,
behaviors
)
{
setElement
.
apply
(
this
,
_
.
tail
(
arguments
,
2
));
// proxy behavior $el to the view's $el.
// This is needed because a view's $el proxy
// is not set until after setElement is called.
_
.
each
(
behaviors
,
function
(
b
)
{
b
.
$el
=
this
.
$el
;
b
.
el
=
this
.
el
;
},
this
);
return
this
;
},
destroy
:
function
(
destroy
,
behaviors
)
{
var
args
=
_
.
tail
(
arguments
,
2
);
destroy
.
apply
(
this
,
args
);
// Call destroy on each behavior after
// destroying the view.
// This unbinds event listeners
// that behaviors have registerd for.
_
.
invoke
(
behaviors
,
'
destroy
'
,
args
);
return
this
;
},
bindUIElements
:
function
(
bindUIElements
,
behaviors
)
{
bindUIElements
.
apply
(
this
);
_
.
invoke
(
behaviors
,
bindUIElements
);
},
unbindUIElements
:
function
(
unbindUIElements
,
behaviors
)
{
unbindUIElements
.
apply
(
this
);
_
.
invoke
(
behaviors
,
unbindUIElements
);
},
triggerMethod
:
function
(
triggerMethod
,
behaviors
)
{
var
args
=
_
.
tail
(
arguments
,
2
);
triggerMethod
.
apply
(
this
,
args
);
_
.
each
(
behaviors
,
function
(
b
)
{
triggerMethod
.
apply
(
b
,
args
);
});
},
delegateEvents
:
function
(
delegateEvents
,
behaviors
)
{
var
args
=
_
.
tail
(
arguments
,
2
);
delegateEvents
.
apply
(
this
,
args
);
_
.
each
(
behaviors
,
function
(
b
)
{
Marionette
.
bindEntityEvents
(
b
,
this
.
model
,
Marionette
.
getOption
(
b
,
'
modelEvents
'
));
Marionette
.
bindEntityEvents
(
b
,
this
.
collection
,
Marionette
.
getOption
(
b
,
'
collectionEvents
'
));
},
this
);
return
this
;
},
undelegateEvents
:
function
(
undelegateEvents
,
behaviors
)
{
var
args
=
_
.
tail
(
arguments
,
2
);
undelegateEvents
.
apply
(
this
,
args
);
_
.
each
(
behaviors
,
function
(
b
)
{
Marionette
.
unbindEntityEvents
(
b
,
this
.
model
,
Marionette
.
getOption
(
b
,
'
modelEvents
'
));
Marionette
.
unbindEntityEvents
(
b
,
this
.
collection
,
Marionette
.
getOption
(
b
,
'
collectionEvents
'
));
},
this
);
return
this
;
behaviorTriggers
:
function
(
behaviorTriggers
,
behaviors
)
{
var
triggerBuilder
=
new
BehaviorTriggersBuilder
(
this
,
behaviors
);
return
triggerBuilder
.
buildBehaviorTriggers
();
},
behaviorEvents
:
function
(
behaviorEvents
,
behaviors
)
{
var
_behaviorsEvents
=
{};
var
viewUI
=
_
.
result
(
this
,
'
ui
'
);
var
viewUI
=
this
.
_uiBindings
||
_
.
result
(
this
,
'
ui
'
);
_
.
each
(
behaviors
,
function
(
b
,
i
)
{
var
_events
=
{};
var
behaviorEvents
=
_
.
clone
(
_
.
result
(
b
,
'
events
'
))
||
{};
var
behaviorUI
=
_
.
result
(
b
,
'
ui
'
);
var
behaviorUI
=
b
.
_uiBindings
||
_
.
result
(
b
,
'
ui
'
);
// Construct an internal UI hash first using
// the views UI hash and then the behaviors UI hash.
...
...
@@ -2833,21 +2957,25 @@
// a user to use the @ui. syntax.
behaviorEvents
=
Marionette
.
normalizeUIKeys
(
behaviorEvents
,
ui
);
_
.
each
(
_
.
keys
(
behaviorEvents
),
function
(
key
)
{
// Append white-space at the end of each key to prevent behavior key collisions.
// This is relying on the fact that backbone events considers "click .foo" the same as
// "click .foo ".
var
j
=
0
;
_
.
each
(
behaviorEvents
,
function
(
behaviour
,
key
)
{
var
match
=
key
.
match
(
delegateEventSplitter
);
// +2 is used because new Array(1) or 0 is "" and not " "
var
whitespace
=
(
new
Array
(
i
+
2
)).
join
(
'
'
);
var
eventKey
=
key
+
whitespace
;
var
handler
=
_
.
isFunction
(
behaviorEvents
[
key
])
?
behaviorEvents
[
key
]
:
b
[
behaviorEvents
[
key
]];
// Set event name to be namespaced using the view cid,
// the behavior index, and the behavior event index
// to generate a non colliding event namespace
// http://api.jquery.com/event.namespace/
var
eventName
=
match
[
1
]
+
'
.
'
+
[
this
.
cid
,
i
,
j
++
,
'
'
].
join
(
''
),
selector
=
match
[
2
];
var
eventKey
=
eventName
+
selector
;
var
handler
=
_
.
isFunction
(
behaviour
)
?
behaviour
:
b
[
behaviour
];
_events
[
eventKey
]
=
_
.
bind
(
handler
,
b
);
});
}
,
this
);
_behaviorsEvents
=
_
.
extend
(
_behaviorsEvents
,
_events
);
});
}
,
this
);
return
_behaviorsEvents
;
}
...
...
@@ -2865,9 +2993,10 @@
// }
// ```
behaviorsLookup
:
function
()
{
throw
new
Error
(
'
You must define where your behaviors are stored.
'
+
'
See https://github.com/marionettejs/backbone.marionette
'
+
'
/blob/master/docs/marionette.behaviors.md#behaviorslookup
'
);
throw
new
Marionette
.
Error
({
message
:
'
You must define where your behaviors are stored.
'
,
url
:
'
marionette.behaviors.html#behaviorslookup
'
});
},
// Takes care of getting the behavior class
...
...
@@ -2881,7 +3010,7 @@
}
// Get behavior class can be either a flat object or a method
return
_
.
isFunction
(
Behaviors
.
behaviorsLookup
)
?
Behaviors
.
behaviorsLookup
.
apply
(
this
,
arguments
)[
key
]
:
Behaviors
.
behaviorsLookup
[
key
];
return
Marionette
.
_getValue
(
Behaviors
.
behaviorsLookup
,
this
,
[
options
,
key
])
[
key
];
},
// Iterate over the behaviors object, for each behavior
...
...
@@ -2909,13 +3038,51 @@
}
});
// Class to build handlers for `triggers` on behaviors
// for views
function
BehaviorTriggersBuilder
(
view
,
behaviors
)
{
this
.
_view
=
view
;
this
.
_viewUI
=
_
.
result
(
view
,
'
ui
'
);
this
.
_behaviors
=
behaviors
;
this
.
_triggers
=
{};
}
_
.
extend
(
BehaviorTriggersBuilder
.
prototype
,
{
// Main method to build the triggers hash with event keys and handlers
buildBehaviorTriggers
:
function
()
{
_
.
each
(
this
.
_behaviors
,
this
.
_buildTriggerHandlersForBehavior
,
this
);
return
this
.
_triggers
;
},
// Internal method to build all trigger handlers for a given behavior
_buildTriggerHandlersForBehavior
:
function
(
behavior
,
i
)
{
var
ui
=
_
.
extend
({},
this
.
_viewUI
,
_
.
result
(
behavior
,
'
ui
'
));
var
triggersHash
=
_
.
clone
(
_
.
result
(
behavior
,
'
triggers
'
))
||
{};
triggersHash
=
Marionette
.
normalizeUIKeys
(
triggersHash
,
ui
);
_
.
each
(
triggersHash
,
_
.
bind
(
this
.
_setHandlerForBehavior
,
this
,
behavior
,
i
));
},
// Internal method to create and assign the trigger handler for a given
// behavior
_setHandlerForBehavior
:
function
(
behavior
,
i
,
eventName
,
trigger
)
{
// Unique identifier for the `this._triggers` hash
var
triggerKey
=
trigger
.
replace
(
/^
\S
+/
,
function
(
triggerName
)
{
return
triggerName
+
'
.
'
+
'
behaviortriggers
'
+
i
;
});
this
.
_triggers
[
triggerKey
]
=
this
.
_view
.
_buildViewTrigger
(
eventName
);
}
});
return
Behaviors
;
})(
Marionette
,
_
);
// AppRouter
// ---------
// App
Router
// ---------
-
// Reduce the boilerplate code of handling route events
// and then calling a single method on another object.
...
...
@@ -2935,10 +3102,10 @@
Marionette
.
AppRouter
=
Backbone
.
Router
.
extend
({
constructor
:
function
(
options
)
{
Backbone
.
Router
.
apply
(
this
,
arguments
);
this
.
options
=
options
||
{};
Backbone
.
Router
.
apply
(
this
,
arguments
);
var
appRoutes
=
this
.
getOption
(
'
appRoutes
'
);
var
controller
=
this
.
_getController
();
this
.
processAppRoutes
(
controller
,
appRoutes
);
...
...
@@ -2955,11 +3122,10 @@
// process the route event and trigger the onRoute
// method call, if it exists
_processOnRoute
:
function
(
routeName
,
routeArgs
)
{
// find the path that matched
var
routePath
=
_
.
invert
(
this
.
getOption
(
'
appRoutes
'
))[
routeName
];
// make sure an onRoute is there, and call it
// make sure an onRoute before trying to call it
if
(
_
.
isFunction
(
this
.
onRoute
))
{
// find the path that matches the current route
var
routePath
=
_
.
invert
(
this
.
getOption
(
'
appRoutes
'
))[
routeName
];
this
.
onRoute
(
routeName
,
routePath
,
routeArgs
);
}
},
...
...
@@ -2985,14 +3151,20 @@
var
method
=
controller
[
methodName
];
if
(
!
method
)
{
throwError
(
'
Method "
'
+
methodName
+
'
" was not found on the controller
'
);
throw
new
Marionette
.
Error
(
'
Method "
'
+
methodName
+
'
" was not found on the controller
'
);
}
this
.
route
(
route
,
methodName
,
_
.
bind
(
method
,
controller
));
},
// Proxy `getOption` to enable getting options from this or this.options by name.
getOption
:
Marionette
.
proxyGetOption
getOption
:
Marionette
.
proxyGetOption
,
triggerMethod
:
Marionette
.
triggerMethod
,
bindEntityEvents
:
Marionette
.
proxyBindEntityEvents
,
unbindEntityEvents
:
Marionette
.
proxyUnbindEntityEvents
});
// Application
...
...
@@ -3001,15 +3173,16 @@
// Contain and manage the composite application as a whole.
// Stores and starts up `Region` objects, includes an
// event aggregator as `app.vent`
Marionette
.
Application
=
function
(
options
)
{
Marionette
.
Application
=
Marionette
.
Object
.
extend
({
constructor
:
function
(
options
)
{
this
.
_initializeRegions
(
options
);
this
.
_initCallbacks
=
new
Marionette
.
Callbacks
();
this
.
submodules
=
{};
_
.
extend
(
this
,
options
);
this
.
_initChannel
();
};
Marionette
.
Object
.
call
(
this
,
options
);
},
_
.
extend
(
Marionette
.
Application
.
prototype
,
Backbone
.
Events
,
{
// Command execution, facilitated by Backbone.Wreqr.Commands
execute
:
function
()
{
this
.
commands
.
execute
.
apply
(
this
.
commands
,
arguments
);
...
...
@@ -3074,9 +3247,7 @@
// Overwrite the module class if the user specifies one
var
ModuleClass
=
Marionette
.
Module
.
getClass
(
moduleDefinition
);
// slice the args, and add this application object as the
// first argument of the array
var
args
=
slice
.
call
(
arguments
);
var
args
=
_
.
toArray
(
arguments
);
args
.
unshift
(
this
);
// see the Marionette.Module object for more information
...
...
@@ -3116,23 +3287,24 @@
// Internal method to set up the region manager
_initRegionManager
:
function
()
{
this
.
_regionManager
=
this
.
getRegionManager
();
this
.
_regionManager
.
_parent
=
this
;
this
.
listenTo
(
this
.
_regionManager
,
'
before:add:region
'
,
function
(
name
)
{
this
.
triggerMethod
(
'
before:add:region
'
,
name
);
this
.
listenTo
(
this
.
_regionManager
,
'
before:add:region
'
,
function
()
{
Marionette
.
_triggerMethod
(
this
,
'
before:add:region
'
,
arguments
);
});
this
.
listenTo
(
this
.
_regionManager
,
'
add:region
'
,
function
(
name
,
region
)
{
this
[
name
]
=
region
;
this
.
triggerMethod
(
'
add:region
'
,
name
,
region
);
Marionette
.
_triggerMethod
(
this
,
'
add:region
'
,
arguments
);
});
this
.
listenTo
(
this
.
_regionManager
,
'
before:remove:region
'
,
function
(
name
)
{
this
.
triggerMethod
(
'
before:remove:region
'
,
name
);
this
.
listenTo
(
this
.
_regionManager
,
'
before:remove:region
'
,
function
()
{
Marionette
.
_triggerMethod
(
this
,
'
before:remove:region
'
,
arguments
);
});
this
.
listenTo
(
this
.
_regionManager
,
'
remove:region
'
,
function
(
name
,
region
)
{
this
.
listenTo
(
this
.
_regionManager
,
'
remove:region
'
,
function
(
name
)
{
delete
this
[
name
];
this
.
triggerMethod
(
'
remove:region
'
,
name
,
region
);
Marionette
.
_triggerMethod
(
this
,
'
remove:region
'
,
arguments
);
});
},
...
...
@@ -3143,19 +3315,9 @@
this
.
vent
=
_
.
result
(
this
,
'
vent
'
)
||
this
.
channel
.
vent
;
this
.
commands
=
_
.
result
(
this
,
'
commands
'
)
||
this
.
channel
.
commands
;
this
.
reqres
=
_
.
result
(
this
,
'
reqres
'
)
||
this
.
channel
.
reqres
;
},
// import the `triggerMethod` to trigger events with corresponding
// methods if the method exists
triggerMethod
:
Marionette
.
triggerMethod
,
// Proxy `getOption` to enable getting options from this or this.options by name.
getOption
:
Marionette
.
proxyGetOption
}
});
// Copy the `extend` function used by Backbone's classes
Marionette
.
Application
.
extend
=
Marionette
.
extend
;
/* jshint maxparams: 9 */
// Module
...
...
@@ -3179,9 +3341,6 @@
// within a module.
this
.
app
=
app
;
// By default modules start with their parents.
this
.
startWithParent
=
true
;
if
(
_
.
isFunction
(
this
.
initialize
))
{
this
.
initialize
(
moduleName
,
app
,
this
.
options
);
}
...
...
@@ -3193,6 +3352,9 @@
// can be used as an event aggregator or pub/sub.
_
.
extend
(
Marionette
.
Module
.
prototype
,
Backbone
.
Events
,
{
// By default modules start with their parents.
startWithParent
:
true
,
// Initialize is an empty function by default. Override it with your own
// initialization logic when extending Marionette.Module.
initialize
:
function
()
{},
...
...
@@ -3243,7 +3405,7 @@
// stop the sub-modules; depth-first, to make sure the
// sub-modules are stopped / finalized before parents
_
.
each
(
this
.
submodules
,
function
(
mod
)
{
mod
.
stop
();
}
);
_
.
invoke
(
this
.
submodules
,
'
stop
'
);
// run the finalizers
this
.
_finalizerCallbacks
.
run
(
undefined
,
this
);
...
...
@@ -3302,8 +3464,7 @@
// get the custom args passed in after the module definition and
// get rid of the module name and definition function
var
customArgs
=
slice
.
call
(
arguments
);
customArgs
.
splice
(
0
,
3
);
var
customArgs
=
_
.
rest
(
arguments
,
3
);
// Split the module names and get the number of submodules.
// i.e. an example module name of `Doge.Wow.Amaze` would
...
...
examples/backbone_marionette/
bower_component
s/backbone/backbone.js
→
examples/backbone_marionette/
node_module
s/backbone/backbone.js
View file @
25e31ff1
// Backbone.js 1.
0.0
// Backbone.js 1.
1.2
// (c) 2010-201
3 Jeremy Ashkenas, DocumentCloud Inc.
// (c) 2010-201
4 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(
function
(){
(
function
(
root
,
factory
)
{
// Set up Backbone appropriately for the environment. Start with AMD.
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
define
([
'
underscore
'
,
'
jquery
'
,
'
exports
'
],
function
(
_
,
$
,
exports
)
{
// Export global even in AMD case in case this script is loaded with
// others that may still expect a global Backbone.
root
.
Backbone
=
factory
(
root
,
exports
,
_
,
$
);
});
// Next for Node.js or CommonJS. jQuery may not be needed as a module.
}
else
if
(
typeof
exports
!==
'
undefined
'
)
{
var
_
=
require
(
'
underscore
'
);
factory
(
root
,
exports
,
_
);
// Finally, as a browser global.
}
else
{
root
.
Backbone
=
factory
(
root
,
{},
root
.
_
,
(
root
.
jQuery
||
root
.
Zepto
||
root
.
ender
||
root
.
$
));
}
}(
this
,
function
(
root
,
Backbone
,
_
,
$
)
{
// Initial Setup
// -------------
// Save a reference to the global object (`window` in the browser, `exports`
// on the server).
var
root
=
this
;
// Save the previous value of the `Backbone` variable, so that it can be
// restored later on, if `noConflict` is used.
var
previousBackbone
=
root
.
Backbone
;
...
...
@@ -24,25 +40,12 @@
var
slice
=
array
.
slice
;
var
splice
=
array
.
splice
;
// The top-level namespace. All public Backbone classes and modules will
// be attached to this. Exported for both the browser and the server.
var
Backbone
;
if
(
typeof
exports
!==
'
undefined
'
)
{
Backbone
=
exports
;
}
else
{
Backbone
=
root
.
Backbone
=
{};
}
// Current version of the library. Keep in sync with `package.json`.
Backbone
.
VERSION
=
'
1.0.0
'
;
// Require Underscore, if we're on the server, and it's not already present.
var
_
=
root
.
_
;
if
(
!
_
&&
(
typeof
require
!==
'
undefined
'
))
_
=
require
(
'
underscore
'
);
Backbone
.
VERSION
=
'
1.1.2
'
;
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
// the `$` variable.
Backbone
.
$
=
root
.
jQuery
||
root
.
Zepto
||
root
.
ender
||
root
.
$
;
Backbone
.
$
=
$
;
// Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
// to its previous owner. Returns a reference to this Backbone object.
...
...
@@ -52,7 +55,7 @@
};
// Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
// will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
// will fake `"P
ATCH"`, `"P
UT"` and `"DELETE"` requests via the `_method` parameter and
// set a `X-Http-Method-Override` header.
Backbone
.
emulateHTTP
=
false
;
...
...
@@ -108,10 +111,9 @@
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
=
{}
;
this
.
_events
=
void
0
;
return
this
;
}
names
=
name
?
[
name
]
:
_
.
keys
(
this
.
_events
);
for
(
i
=
0
,
l
=
names
.
length
;
i
<
l
;
i
++
)
{
name
=
names
[
i
];
...
...
@@ -151,14 +153,15 @@
// Tell this object to stop listening to either specific events ... or
// to every object it's currently listening to.
stopListening
:
function
(
obj
,
name
,
callback
)
{
var
listeners
=
this
.
_listeners
;
if
(
!
listeners
)
return
this
;
var
deleteListener
=
!
name
&&
!
callback
;
if
(
typeof
name
===
'
object
'
)
callback
=
this
;
if
(
obj
)
(
listeners
=
{})[
obj
.
_listenerId
]
=
obj
;
for
(
var
id
in
listeners
)
{
listeners
[
id
].
off
(
name
,
callback
,
this
);
if
(
deleteListener
)
delete
this
.
_listeners
[
id
];
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
;
}
...
...
@@ -204,7 +207,7 @@
case
1
:
while
(
++
i
<
l
)
(
ev
=
events
[
i
]).
callback
.
call
(
ev
.
ctx
,
a1
);
return
;
case
2
:
while
(
++
i
<
l
)
(
ev
=
events
[
i
]).
callback
.
call
(
ev
.
ctx
,
a1
,
a2
);
return
;
case
3
:
while
(
++
i
<
l
)
(
ev
=
events
[
i
]).
callback
.
call
(
ev
.
ctx
,
a1
,
a2
,
a3
);
return
;
default
:
while
(
++
i
<
l
)
(
ev
=
events
[
i
]).
callback
.
apply
(
ev
.
ctx
,
args
);
default
:
while
(
++
i
<
l
)
(
ev
=
events
[
i
]).
callback
.
apply
(
ev
.
ctx
,
args
);
return
;
}
};
...
...
@@ -215,10 +218,10 @@
// listening to.
_
.
each
(
listenMethods
,
function
(
implementation
,
method
)
{
Events
[
method
]
=
function
(
obj
,
name
,
callback
)
{
var
listen
ers
=
this
.
_listeners
||
(
this
.
_listeners
=
{});
var
id
=
obj
.
_listen
erId
||
(
obj
.
_listener
Id
=
_
.
uniqueId
(
'
l
'
));
listen
ers
[
id
]
=
obj
;
if
(
typeof
name
===
'
object
'
)
callback
=
this
;
var
listen
ingTo
=
this
.
_listeningTo
||
(
this
.
_listeningTo
=
{});
var
id
=
obj
.
_listen
Id
||
(
obj
.
_listen
Id
=
_
.
uniqueId
(
'
l
'
));
listen
ingTo
[
id
]
=
obj
;
if
(
!
callback
&&
typeof
name
===
'
object
'
)
callback
=
this
;
obj
[
implementation
](
name
,
callback
,
this
);
return
this
;
};
...
...
@@ -243,24 +246,18 @@
// Create a new model with the specified attributes. A client id (`cid`)
// is automatically generated and assigned for you.
var
Model
=
Backbone
.
Model
=
function
(
attributes
,
options
)
{
var
defaults
;
var
attrs
=
attributes
||
{};
options
||
(
options
=
{});
this
.
cid
=
_
.
uniqueId
(
'
c
'
);
this
.
attributes
=
{};
_
.
extend
(
this
,
_
.
pick
(
options
,
modelOptions
))
;
if
(
options
.
collection
)
this
.
collection
=
options
.
collection
;
if
(
options
.
parse
)
attrs
=
this
.
parse
(
attrs
,
options
)
||
{};
if
(
defaults
=
_
.
result
(
this
,
'
defaults
'
))
{
attrs
=
_
.
defaults
({},
attrs
,
defaults
);
}
attrs
=
_
.
defaults
({},
attrs
,
_
.
result
(
this
,
'
defaults
'
));
this
.
set
(
attrs
,
options
);
this
.
changed
=
{};
this
.
initialize
.
apply
(
this
,
arguments
);
};
// A list of options to be attached directly to the model, if provided.
var
modelOptions
=
[
'
url
'
,
'
urlRoot
'
,
'
collection
'
];
// Attach all inheritable methods to the Model prototype.
_
.
extend
(
Model
.
prototype
,
Events
,
{
...
...
@@ -355,7 +352,7 @@
// Trigger all relevant attribute changes.
if
(
!
silent
)
{
if
(
changes
.
length
)
this
.
_pending
=
true
;
if
(
changes
.
length
)
this
.
_pending
=
options
;
for
(
var
i
=
0
,
l
=
changes
.
length
;
i
<
l
;
i
++
)
{
this
.
trigger
(
'
change:
'
+
changes
[
i
],
this
,
current
[
changes
[
i
]],
options
);
}
...
...
@@ -366,6 +363,7 @@
if
(
changing
)
return
this
;
if
(
!
silent
)
{
while
(
this
.
_pending
)
{
options
=
this
.
_pending
;
this
.
_pending
=
false
;
this
.
trigger
(
'
change
'
,
this
,
options
);
}
...
...
@@ -456,13 +454,16 @@
(
attrs
=
{})[
key
]
=
val
;
}
// If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
if
(
attrs
&&
(
!
options
||
!
options
.
wait
)
&&
!
this
.
set
(
attrs
,
options
))
return
false
;
options
=
_
.
extend
({
validate
:
true
},
options
);
// Do not persist invalid models.
// If we're not waiting and attributes exist, save acts as
// `set(attr).save(null, opts)` with validation. Otherwise, check if
// the model will be valid when the attributes, if any, are set.
if
(
attrs
&&
!
options
.
wait
)
{
if
(
!
this
.
set
(
attrs
,
options
))
return
false
;
}
else
{
if
(
!
this
.
_validate
(
attrs
,
options
))
return
false
;
}
// Set temporary attributes if `{wait: true}`.
if
(
attrs
&&
options
.
wait
)
{
...
...
@@ -530,9 +531,12 @@
// using Backbone's restful methods, override this to change the endpoint
// that will be called.
url
:
function
()
{
var
base
=
_
.
result
(
this
,
'
urlRoot
'
)
||
_
.
result
(
this
.
collection
,
'
url
'
)
||
urlError
();
var
base
=
_
.
result
(
this
,
'
urlRoot
'
)
||
_
.
result
(
this
.
collection
,
'
url
'
)
||
urlError
();
if
(
this
.
isNew
())
return
base
;
return
base
+
(
base
.
charAt
(
base
.
length
-
1
)
===
'
/
'
?
''
:
'
/
'
)
+
encodeURIComponent
(
this
.
id
);
return
base
.
replace
(
/
([^\/])
$/
,
'
$1
/
'
)
+
encodeURIComponent
(
this
.
id
);
},
// **parse** converts a response into the hash of attributes to be `set` on
...
...
@@ -548,7 +552,7 @@
// A model is new if it has never been saved to the server, and lacks an id.
isNew
:
function
()
{
return
this
.
id
==
null
;
return
!
this
.
has
(
this
.
idAttribute
)
;
},
// Check if the model is currently in a valid state.
...
...
@@ -563,7 +567,7 @@
attrs
=
_
.
extend
({},
this
.
attributes
,
attrs
);
var
error
=
this
.
validationError
=
this
.
validate
(
attrs
,
options
)
||
null
;
if
(
!
error
)
return
true
;
this
.
trigger
(
'
invalid
'
,
this
,
error
,
_
.
extend
(
options
||
{}
,
{
validationError
:
error
}));
this
.
trigger
(
'
invalid
'
,
this
,
error
,
_
.
extend
(
options
,
{
validationError
:
error
}));
return
false
;
}
...
...
@@ -596,7 +600,6 @@
// its models in sort order, as they're added and removed.
var
Collection
=
Backbone
.
Collection
=
function
(
models
,
options
)
{
options
||
(
options
=
{});
if
(
options
.
url
)
this
.
url
=
options
.
url
;
if
(
options
.
model
)
this
.
model
=
options
.
model
;
if
(
options
.
comparator
!==
void
0
)
this
.
comparator
=
options
.
comparator
;
this
.
_reset
();
...
...
@@ -606,7 +609,7 @@
// Default options for `Collection#set`.
var
setOptions
=
{
add
:
true
,
remove
:
true
,
merge
:
true
};
var
addOptions
=
{
add
:
true
,
merge
:
false
,
remove
:
false
};
var
addOptions
=
{
add
:
true
,
remove
:
false
};
// Define the Collection's inheritable methods.
_
.
extend
(
Collection
.
prototype
,
Events
,
{
...
...
@@ -632,16 +635,17 @@
// Add a model, or list of models to the set.
add
:
function
(
models
,
options
)
{
return
this
.
set
(
models
,
_
.
defaults
(
options
||
{}
,
addOptions
));
return
this
.
set
(
models
,
_
.
extend
({
merge
:
false
},
options
,
addOptions
));
},
// Remove a model, or a list of models from the set.
remove
:
function
(
models
,
options
)
{
models
=
_
.
isArray
(
models
)
?
models
.
slice
()
:
[
models
];
var
singular
=
!
_
.
isArray
(
models
);
models
=
singular
?
[
models
]
:
_
.
clone
(
models
);
options
||
(
options
=
{});
var
i
,
l
,
index
,
model
;
for
(
i
=
0
,
l
=
models
.
length
;
i
<
l
;
i
++
)
{
model
=
this
.
get
(
models
[
i
]);
model
=
models
[
i
]
=
this
.
get
(
models
[
i
]);
if
(
!
model
)
continue
;
delete
this
.
_byId
[
model
.
id
];
delete
this
.
_byId
[
model
.
cid
];
...
...
@@ -652,9 +656,9 @@
options
.
index
=
index
;
model
.
trigger
(
'
remove
'
,
model
,
this
,
options
);
}
this
.
_removeReference
(
model
);
this
.
_removeReference
(
model
,
options
);
}
return
thi
s
;
return
singular
?
models
[
0
]
:
model
s
;
},
// Update a collection by `set`-ing a new list of models, adding new ones,
...
...
@@ -662,43 +666,57 @@
// already exist in the collection, as necessary. Similar to **Model#set**,
// the core operation for updating the data contained by the collection.
set
:
function
(
models
,
options
)
{
options
=
_
.
defaults
(
options
||
{}
,
setOptions
);
options
=
_
.
defaults
(
{},
options
,
setOptions
);
if
(
options
.
parse
)
models
=
this
.
parse
(
models
,
options
);
if
(
!
_
.
isArray
(
models
))
models
=
models
?
[
models
]
:
[];
var
i
,
l
,
model
,
attrs
,
existing
,
sort
;
var
singular
=
!
_
.
isArray
(
models
);
models
=
singular
?
(
models
?
[
models
]
:
[])
:
_
.
clone
(
models
);
var
i
,
l
,
id
,
model
,
attrs
,
existing
,
sort
;
var
at
=
options
.
at
;
var
targetModel
=
this
.
model
;
var
sortable
=
this
.
comparator
&&
(
at
==
null
)
&&
options
.
sort
!==
false
;
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
// from being added.
for
(
i
=
0
,
l
=
models
.
length
;
i
<
l
;
i
++
)
{
if
(
!
(
model
=
this
.
_prepareModel
(
models
[
i
],
options
)))
continue
;
attrs
=
models
[
i
]
||
{};
if
(
attrs
instanceof
Model
)
{
id
=
model
=
attrs
;
}
else
{
id
=
attrs
[
targetModel
.
prototype
.
idAttribute
||
'
id
'
];
}
// If a duplicate is found, prevent it from being added and
// optionally merge it into the existing model.
if
(
existing
=
this
.
get
(
model
))
{
if
(
options
.
remove
)
modelMap
[
existing
.
cid
]
=
true
;
if
(
options
.
merge
)
{
existing
.
set
(
model
.
attributes
,
options
);
if
(
existing
=
this
.
get
(
id
))
{
if
(
remove
)
modelMap
[
existing
.
cid
]
=
true
;
if
(
merge
)
{
attrs
=
attrs
===
model
?
model
.
attributes
:
attrs
;
if
(
options
.
parse
)
attrs
=
existing
.
parse
(
attrs
,
options
);
existing
.
set
(
attrs
,
options
);
if
(
sortable
&&
!
sort
&&
existing
.
hasChanged
(
sortAttr
))
sort
=
true
;
}
models
[
i
]
=
existing
;
// This is a new model, push it to the `toAdd` list.
}
else
if
(
options
.
add
)
{
// If this is a new, valid model, push it to the `toAdd` list.
}
else
if
(
add
)
{
model
=
models
[
i
]
=
this
.
_prepareModel
(
attrs
,
options
);
if
(
!
model
)
continue
;
toAdd
.
push
(
model
);
// Listen to added models' events, and index models for lookup by
// `id` and by `cid`.
model
.
on
(
'
all
'
,
this
.
_onModelEvent
,
this
);
this
.
_byId
[
model
.
cid
]
=
model
;
if
(
model
.
id
!=
null
)
this
.
_byId
[
model
.
id
]
=
model
;
this
.
_addReference
(
model
,
options
);
}
// 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.
if
(
options
.
remove
)
{
if
(
remove
)
{
for
(
i
=
0
,
l
=
this
.
length
;
i
<
l
;
++
i
)
{
if
(
!
modelMap
[(
model
=
this
.
models
[
i
]).
cid
])
toRemove
.
push
(
model
);
}
...
...
@@ -706,29 +724,35 @@
}
// See if sorting is needed, update `length` and splice in new models.
if
(
toAdd
.
length
)
{
if
(
toAdd
.
length
||
(
order
&&
order
.
length
)
)
{
if
(
sortable
)
sort
=
true
;
this
.
length
+=
toAdd
.
length
;
if
(
at
!=
null
)
{
splice
.
apply
(
this
.
models
,
[
at
,
0
].
concat
(
toAdd
));
for
(
i
=
0
,
l
=
toAdd
.
length
;
i
<
l
;
i
++
)
{
this
.
models
.
splice
(
at
+
i
,
0
,
toAdd
[
i
]);
}
}
else
{
push
.
apply
(
this
.
models
,
toAdd
);
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.
if
(
sort
)
this
.
sort
({
silent
:
true
});
if
(
options
.
silent
)
return
this
;
// Trigger `add` events.
// Unless silenced, it's time to fire all appropriate add/sort events.
if
(
!
options
.
silent
)
{
for
(
i
=
0
,
l
=
toAdd
.
length
;
i
<
l
;
i
++
)
{
(
model
=
toAdd
[
i
]).
trigger
(
'
add
'
,
model
,
this
,
options
);
}
if
(
sort
||
(
order
&&
order
.
length
))
this
.
trigger
(
'
sort
'
,
this
,
options
);
}
// Trigger `sort` if the collection was sorted.
if
(
sort
)
this
.
trigger
(
'
sort
'
,
this
,
options
);
return
this
;
// Return the added (or merged) model (or models).
return
singular
?
models
[
0
]
:
models
;
},
// When you have more items than you want to add or remove individually,
...
...
@@ -738,20 +762,18 @@
reset
:
function
(
models
,
options
)
{
options
||
(
options
=
{});
for
(
var
i
=
0
,
l
=
this
.
models
.
length
;
i
<
l
;
i
++
)
{
this
.
_removeReference
(
this
.
models
[
i
]);
this
.
_removeReference
(
this
.
models
[
i
]
,
options
);
}
options
.
previousModels
=
this
.
models
;
this
.
_reset
();
this
.
add
(
models
,
_
.
extend
({
silent
:
true
},
options
));
models
=
this
.
add
(
models
,
_
.
extend
({
silent
:
true
},
options
));
if
(
!
options
.
silent
)
this
.
trigger
(
'
reset
'
,
this
,
options
);
return
thi
s
;
return
model
s
;
},
// Add a model to the end of the collection.
push
:
function
(
model
,
options
)
{
model
=
this
.
_prepareModel
(
model
,
options
);
this
.
add
(
model
,
_
.
extend
({
at
:
this
.
length
},
options
));
return
model
;
return
this
.
add
(
model
,
_
.
extend
({
at
:
this
.
length
},
options
));
},
// Remove a model from the end of the collection.
...
...
@@ -763,9 +785,7 @@
// Add a model to the beginning of the collection.
unshift
:
function
(
model
,
options
)
{
model
=
this
.
_prepareModel
(
model
,
options
);
this
.
add
(
model
,
_
.
extend
({
at
:
0
},
options
));
return
model
;
return
this
.
add
(
model
,
_
.
extend
({
at
:
0
},
options
));
},
// Remove a model from the beginning of the collection.
...
...
@@ -776,14 +796,14 @@
},
// Slice out a sub-array of models from the collection.
slice
:
function
(
begin
,
end
)
{
return
this
.
models
.
slice
(
begin
,
end
);
slice
:
function
()
{
return
slice
.
apply
(
this
.
models
,
arguments
);
},
// Get a model from the set by id.
get
:
function
(
obj
)
{
if
(
obj
==
null
)
return
void
0
;
return
this
.
_byId
[
obj
.
id
!=
null
?
obj
.
id
:
obj
.
cid
||
obj
];
return
this
.
_byId
[
obj
]
||
this
.
_byId
[
obj
.
id
]
||
this
.
_byId
[
obj
.
cid
];
},
// Get the model at the given index.
...
...
@@ -827,16 +847,6 @@
return
this
;
},
// Figure out the smallest index at which a model should be inserted so as
// to maintain order.
sortedIndex
:
function
(
model
,
value
,
context
)
{
value
||
(
value
=
this
.
comparator
);
var
iterator
=
_
.
isFunction
(
value
)
?
value
:
function
(
model
)
{
return
model
.
get
(
value
);
};
return
_
.
sortedIndex
(
this
.
models
,
model
,
iterator
,
context
);
},
// Pluck an attribute from each model in the collection.
pluck
:
function
(
attr
)
{
return
_
.
invoke
(
this
.
models
,
'
get
'
,
attr
);
...
...
@@ -869,7 +879,7 @@
if
(
!
options
.
wait
)
this
.
add
(
model
,
options
);
var
collection
=
this
;
var
success
=
options
.
success
;
options
.
success
=
function
(
resp
)
{
options
.
success
=
function
(
model
,
resp
)
{
if
(
options
.
wait
)
collection
.
add
(
model
,
options
);
if
(
success
)
success
(
model
,
resp
,
options
);
};
...
...
@@ -899,22 +909,25 @@
// Prepare a hash of attributes (or other model) to be added to this
// collection.
_prepareModel
:
function
(
attrs
,
options
)
{
if
(
attrs
instanceof
Model
)
{
if
(
!
attrs
.
collection
)
attrs
.
collection
=
this
;
return
attrs
;
}
options
||
(
options
=
{});
if
(
attrs
instanceof
Model
)
return
attrs
;
options
=
options
?
_
.
clone
(
options
)
:
{};
options
.
collection
=
this
;
var
model
=
new
this
.
model
(
attrs
,
options
);
if
(
!
model
.
_validate
(
attrs
,
options
))
{
this
.
trigger
(
'
invalid
'
,
this
,
attrs
,
options
);
if
(
!
model
.
validationError
)
return
model
;
this
.
trigger
(
'
invalid
'
,
this
,
model
.
validationError
,
options
);
return
false
;
}
return
model
;
},
// Internal method to create a model's ties to a collection.
_addReference
:
function
(
model
,
options
)
{
this
.
_byId
[
model
.
cid
]
=
model
;
if
(
model
.
id
!=
null
)
this
.
_byId
[
model
.
id
]
=
model
;
if
(
!
model
.
collection
)
model
.
collection
=
this
;
model
.
on
(
'
all
'
,
this
.
_onModelEvent
,
this
);
},
// Internal method to sever a model's ties to a collection.
_removeReference
:
function
(
model
)
{
_removeReference
:
function
(
model
,
options
)
{
if
(
this
===
model
.
collection
)
delete
model
.
collection
;
model
.
off
(
'
all
'
,
this
.
_onModelEvent
,
this
);
},
...
...
@@ -942,8 +955,8 @@
'
inject
'
,
'
reduceRight
'
,
'
foldr
'
,
'
find
'
,
'
detect
'
,
'
filter
'
,
'
select
'
,
'
reject
'
,
'
every
'
,
'
all
'
,
'
some
'
,
'
any
'
,
'
include
'
,
'
contains
'
,
'
invoke
'
,
'
max
'
,
'
min
'
,
'
toArray
'
,
'
size
'
,
'
first
'
,
'
head
'
,
'
take
'
,
'
initial
'
,
'
rest
'
,
'
tail
'
,
'
drop
'
,
'
last
'
,
'
without
'
,
'
indexOf
'
,
'
shuffle
'
,
'
lastIndexOf
'
,
'
isEmpty
'
,
'
chain
'
];
'
tail
'
,
'
drop
'
,
'
last
'
,
'
without
'
,
'
difference
'
,
'
indexOf
'
,
'
shuffle
'
,
'
lastIndexOf
'
,
'
isEmpty
'
,
'
chain
'
,
'
sample
'
];
// Mix in each Underscore method as a proxy to `Collection#models`.
_
.
each
(
methods
,
function
(
method
)
{
...
...
@@ -955,7 +968,7 @@
});
// Underscore methods that take a property name as an argument.
var
attributeMethods
=
[
'
groupBy
'
,
'
countBy
'
,
'
sortBy
'
];
var
attributeMethods
=
[
'
groupBy
'
,
'
countBy
'
,
'
sortBy
'
,
'
indexBy
'
];
// Use attributes instead of properties.
_
.
each
(
attributeMethods
,
function
(
method
)
{
...
...
@@ -982,7 +995,8 @@
// if an existing element is not provided...
var
View
=
Backbone
.
View
=
function
(
options
)
{
this
.
cid
=
_
.
uniqueId
(
'
view
'
);
this
.
_configure
(
options
||
{});
options
||
(
options
=
{});
_
.
extend
(
this
,
_
.
pick
(
options
,
viewOptions
));
this
.
_ensureElement
();
this
.
initialize
.
apply
(
this
,
arguments
);
this
.
delegateEvents
();
...
...
@@ -1001,7 +1015,7 @@
tagName
:
'
div
'
,
// jQuery delegate for element lookup, scoped to DOM elements within the
// current view. This should be prefered to global lookups where possible.
// current view. This should be prefer
r
ed to global lookups where possible.
$
:
function
(
selector
)
{
return
this
.
$el
.
find
(
selector
);
},
...
...
@@ -1041,7 +1055,7 @@
//
// {
// 'mousedown .title': 'edit',
// 'click .button': 'save'
// 'click .button': 'save'
,
// 'click .open': function(e) { ... }
// }
//
...
...
@@ -1079,16 +1093,6 @@
return
this
;
},
// Performs the initial configuration of a View with a set of options.
// Keys with special meaning *(e.g. model, collection, id, className)* are
// attached directly to the view. See `viewOptions` for an exhaustive
// list.
_configure
:
function
(
options
)
{
if
(
this
.
options
)
options
=
_
.
extend
({},
_
.
result
(
this
,
'
options
'
),
options
);
_
.
extend
(
this
,
_
.
pick
(
options
,
viewOptions
));
this
.
options
=
options
;
},
// Ensure that the View has a DOM element to render into.
// If `this.el` is a string, pass it through `$()`, take the first
// matching element, and re-assign it to `el`. Otherwise, create
...
...
@@ -1174,8 +1178,7 @@
// If we're sending a `PATCH` request, and we're in an old Internet Explorer
// that still has ActiveX enabled by default, override jQuery to use that
// for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
if
(
params
.
type
===
'
PATCH
'
&&
window
.
ActiveXObject
&&
!
(
window
.
external
&&
window
.
external
.
msActiveXFilteringEnabled
))
{
if
(
params
.
type
===
'
PATCH
'
&&
noXhrPatch
)
{
params
.
xhr
=
function
()
{
return
new
ActiveXObject
(
"
Microsoft.XMLHTTP
"
);
};
...
...
@@ -1187,6 +1190,10 @@
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.
var
methodMap
=
{
'
create
'
:
'
POST
'
,
...
...
@@ -1244,7 +1251,7 @@
var
router
=
this
;
Backbone
.
history
.
route
(
route
,
function
(
fragment
)
{
var
args
=
router
.
_extractParameters
(
route
,
fragment
);
callback
&&
callback
.
apply
(
router
,
args
);
router
.
execute
(
callback
,
args
);
router
.
trigger
.
apply
(
router
,
[
'
route:
'
+
name
].
concat
(
args
));
router
.
trigger
(
'
route
'
,
name
,
args
);
Backbone
.
history
.
trigger
(
'
route
'
,
router
,
name
,
args
);
...
...
@@ -1252,6 +1259,12 @@
return
this
;
},
// Execute a route handler with the provided parameters. This is an
// excellent place to do pre-route setup or post-route cleanup.
execute
:
function
(
callback
,
args
)
{
if
(
callback
)
callback
.
apply
(
this
,
args
);
},
// Simple proxy to `Backbone.history` to save a fragment into the history.
navigate
:
function
(
fragment
,
options
)
{
Backbone
.
history
.
navigate
(
fragment
,
options
);
...
...
@@ -1275,11 +1288,11 @@
_routeToRegExp
:
function
(
route
)
{
route
=
route
.
replace
(
escapeRegExp
,
'
\\
$&
'
)
.
replace
(
optionalParam
,
'
(?:$1)?
'
)
.
replace
(
namedParam
,
function
(
match
,
optional
){
return
optional
?
match
:
'
([^
\
/
]+)
'
;
.
replace
(
namedParam
,
function
(
match
,
optional
)
{
return
optional
?
match
:
'
([^
/?
]+)
'
;
})
.
replace
(
splatParam
,
'
(
.
*?)
'
);
return
new
RegExp
(
'
^
'
+
route
+
'
$
'
);
.
replace
(
splatParam
,
'
(
[^?]
*?)
'
);
return
new
RegExp
(
'
^
'
+
route
+
'
(?:
\\
?([
\\
s
\\
S]*))?
$
'
);
},
// Given a route, and a URL fragment that it matches, return the array of
...
...
@@ -1287,7 +1300,9 @@
// treated as `null` to normalize cross-browser behavior.
_extractParameters
:
function
(
route
,
fragment
)
{
var
params
=
route
.
exec
(
fragment
).
slice
(
1
);
return
_
.
map
(
params
,
function
(
param
)
{
return
_
.
map
(
params
,
function
(
param
,
i
)
{
// Don't decode the search params.
if
(
i
===
params
.
length
-
1
)
return
param
||
null
;
return
param
?
decodeURIComponent
(
param
)
:
null
;
});
}
...
...
@@ -1325,6 +1340,9 @@
// Cached regex for removing a trailing slash.
var
trailingSlash
=
/
\/
$/
;
// Cached regex for stripping urls of hash.
var
pathStripper
=
/#.*$/
;
// Has the history handling already been started?
History
.
started
=
false
;
...
...
@@ -1335,6 +1353,11 @@
// twenty times a second.
interval
:
50
,
// Are we at the app root?
atRoot
:
function
()
{
return
this
.
location
.
pathname
.
replace
(
/
[^\/]
$/
,
'
$&/
'
)
===
this
.
root
;
},
// Gets the true hash value. Cannot use location.hash directly due to bug
// in Firefox where location.hash will always be decoded.
getHash
:
function
(
window
)
{
...
...
@@ -1347,9 +1370,9 @@
getFragment
:
function
(
fragment
,
forcePushState
)
{
if
(
fragment
==
null
)
{
if
(
this
.
_hasPushState
||
!
this
.
_wantsHashChange
||
forcePushState
)
{
fragment
=
this
.
location
.
pathname
;
fragment
=
decodeURI
(
this
.
location
.
pathname
+
this
.
location
.
search
)
;
var
root
=
this
.
root
.
replace
(
trailingSlash
,
''
);
if
(
!
fragment
.
indexOf
(
root
))
fragment
=
fragment
.
s
ubstr
(
root
.
length
);
if
(
!
fragment
.
indexOf
(
root
))
fragment
=
fragment
.
s
lice
(
root
.
length
);
}
else
{
fragment
=
this
.
getHash
();
}
...
...
@@ -1365,7 +1388,7 @@
// Figure out the initial configuration. Do we need an iframe?
// Is pushState desired ... is it available?
this
.
options
=
_
.
extend
({
},
{
root
:
'
/
'
},
this
.
options
,
options
);
this
.
options
=
_
.
extend
({
root
:
'
/
'
},
this
.
options
,
options
);
this
.
root
=
this
.
options
.
root
;
this
.
_wantsHashChange
=
this
.
options
.
hashChange
!==
false
;
this
.
_wantsPushState
=
!!
this
.
options
.
pushState
;
...
...
@@ -1378,7 +1401,8 @@
this
.
root
=
(
'
/
'
+
this
.
root
+
'
/
'
).
replace
(
rootStripper
,
'
/
'
);
if
(
oldIE
&&
this
.
_wantsHashChange
)
{
this
.
iframe
=
Backbone
.
$
(
'
<iframe src="javascript:0" tabindex="-1" />
'
).
hide
().
appendTo
(
'
body
'
)[
0
].
contentWindow
;
var
frame
=
Backbone
.
$
(
'
<iframe src="javascript:0" tabindex="-1">
'
);
this
.
iframe
=
frame
.
hide
().
appendTo
(
'
body
'
)[
0
].
contentWindow
;
this
.
navigate
(
fragment
);
}
...
...
@@ -1396,21 +1420,26 @@
// opened by a non-pushState browser.
this
.
fragment
=
fragment
;
var
loc
=
this
.
location
;
var
atRoot
=
loc
.
pathname
.
replace
(
/
[^\/]
$/
,
'
$&/
'
)
===
this
.
root
;
// 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...
if
(
this
.
_wantsHashChange
&&
this
.
_wantsPushState
&&
!
this
.
_hasPushState
&&
!
atRoot
)
{
// Transition from hashChange to pushState or vice versa if both are
// requested.
if
(
this
.
_wantsHashChange
&&
this
.
_wantsPushState
)
{
// 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...
if
(
!
this
.
_hasPushState
&&
!
this
.
atRoot
())
{
this
.
fragment
=
this
.
getFragment
(
null
,
true
);
this
.
location
.
replace
(
this
.
root
+
this
.
location
.
search
+
'
#
'
+
this
.
fragment
);
this
.
location
.
replace
(
this
.
root
+
'
#
'
+
this
.
fragment
);
// Return immediately as browser will do redirect to new url
return
true
;
// 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...
}
else
if
(
this
.
_wantsPushState
&&
this
.
_hasPushState
&&
atRoot
&&
loc
.
hash
)
{
}
else
if
(
this
.
_hasPushState
&&
this
.
atRoot
()
&&
loc
.
hash
)
{
this
.
fragment
=
this
.
getHash
().
replace
(
routeStripper
,
''
);
this
.
history
.
replaceState
({},
document
.
title
,
this
.
root
+
this
.
fragment
+
loc
.
search
);
this
.
history
.
replaceState
({},
document
.
title
,
this
.
root
+
this
.
fragment
);
}
}
if
(
!
this
.
options
.
silent
)
return
this
.
loadUrl
();
...
...
@@ -1420,7 +1449,7 @@
// but possibly useful for unit testing Routers.
stop
:
function
()
{
Backbone
.
$
(
window
).
off
(
'
popstate
'
,
this
.
checkUrl
).
off
(
'
hashchange
'
,
this
.
checkUrl
);
clearInterval
(
this
.
_checkUrlInterval
);
if
(
this
.
_checkUrlInterval
)
clearInterval
(
this
.
_checkUrlInterval
);
History
.
started
=
false
;
},
...
...
@@ -1439,21 +1468,20 @@
}
if
(
current
===
this
.
fragment
)
return
false
;
if
(
this
.
iframe
)
this
.
navigate
(
current
);
this
.
loadUrl
()
||
this
.
loadUrl
(
this
.
getHash
())
;
this
.
loadUrl
();
},
// Attempt to load the current URL fragment. If a route succeeds with a
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
loadUrl
:
function
(
fragment
Override
)
{
var
fragment
=
this
.
fragment
=
this
.
getFragment
(
fragmentOverride
);
var
matched
=
_
.
any
(
this
.
handlers
,
function
(
handler
)
{
loadUrl
:
function
(
fragment
)
{
fragment
=
this
.
fragment
=
this
.
getFragment
(
fragment
);
return
_
.
any
(
this
.
handlers
,
function
(
handler
)
{
if
(
handler
.
route
.
test
(
fragment
))
{
handler
.
callback
(
fragment
);
return
true
;
}
});
return
matched
;
},
// Save a fragment into the hash history, or replace the URL state if the
...
...
@@ -1465,11 +1493,18 @@
// you wish to modify the current URL without adding an entry to the history.
navigate
:
function
(
fragment
,
options
)
{
if
(
!
History
.
started
)
return
false
;
if
(
!
options
||
options
===
true
)
options
=
{
trigger
:
options
};
fragment
=
this
.
getFragment
(
fragment
||
''
);
if
(
!
options
||
options
===
true
)
options
=
{
trigger
:
!!
options
};
var
url
=
this
.
root
+
(
fragment
=
this
.
getFragment
(
fragment
||
''
));
// Strip the hash for matching.
fragment
=
fragment
.
replace
(
pathStripper
,
''
);
if
(
this
.
fragment
===
fragment
)
return
;
this
.
fragment
=
fragment
;
var
url
=
this
.
root
+
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
(
this
.
_hasPushState
)
{
...
...
@@ -1492,7 +1527,7 @@
}
else
{
return
this
.
location
.
assign
(
url
);
}
if
(
options
.
trigger
)
this
.
loadUrl
(
fragment
);
if
(
options
.
trigger
)
return
this
.
loadUrl
(
fragment
);
},
// Update the hash location, either replacing the current entry, or adding
...
...
@@ -1560,7 +1595,7 @@
};
// Wrap an optional error callback with a fallback error event.
var
wrapError
=
function
(
model
,
options
)
{
var
wrapError
=
function
(
model
,
options
)
{
var
error
=
options
.
error
;
options
.
error
=
function
(
resp
)
{
if
(
error
)
error
(
model
,
resp
,
options
);
...
...
@@ -1568,4 +1603,6 @@
};
};
}).
call
(
this
);
return
Backbone
;
}));
examples/backbone_marionette/
bower_components/jquery
/jquery.js
→
examples/backbone_marionette/
node_modules/jquery/dist
/jquery.js
View file @
25e31ff1
This source diff could not be displayed because it is too large. You can
view the blob
instead.
examples/backbone_marionette/
bower_components/todomvc-common/base
.css
→
examples/backbone_marionette/
node_modules/todomvc-app-css/index
.css
View file @
25e31ff1
...
...
@@ -12,25 +12,30 @@ button {
font-size
:
100%
;
vertical-align
:
baseline
;
font-family
:
inherit
;
font-weight
:
inherit
;
color
:
inherit
;
-webkit-appearance
:
none
;
-ms-appearance
:
none
;
-o-appearance
:
none
;
appearance
:
none
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
}
body
{
font
:
14px
'Helvetica Neue'
,
Helvetica
,
Arial
,
sans-serif
;
line-height
:
1.4em
;
background
:
#
eaeaea
url('bg.png')
;
background
:
#
f5f5f5
;
color
:
#4d4d4d
;
width
:
550px
;
min-width
:
230px
;
max-width
:
550px
;
margin
:
0
auto
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
-o-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
font-weight
:
300
;
}
button
,
...
...
@@ -38,78 +43,50 @@ input[type="checkbox"] {
outline
:
none
;
}
.hidden
{
display
:
none
;
}
#todoapp
{
background
:
#fff
;
background
:
rgba
(
255
,
255
,
255
,
0.9
);
margin
:
130px
0
40px
0
;
border
:
1px
solid
#ccc
;
position
:
relative
;
border-top-left-radius
:
2px
;
border-top-right-radius
:
2px
;
box-shadow
:
0
2px
6px
0
rgba
(
0
,
0
,
0
,
0.2
),
0
25px
50px
0
rgba
(
0
,
0
,
0
,
0.15
);
}
#todoapp
:before
{
content
:
''
;
border-left
:
1px
solid
#f5d6d6
;
border-right
:
1px
solid
#f5d6d6
;
width
:
2px
;
position
:
absolute
;
top
:
0
;
left
:
40px
;
height
:
100%
;
box-shadow
:
0
2px
4px
0
rgba
(
0
,
0
,
0
,
0.2
),
0
25px
50px
0
rgba
(
0
,
0
,
0
,
0.1
);
}
#todoapp
input
::-webkit-input-placeholder
{
font-style
:
italic
;
font-weight
:
300
;
color
:
#e6e6e6
;
}
#todoapp
input
::-moz-placeholder
{
font-style
:
italic
;
color
:
#a9a9a9
;
font-weight
:
300
;
color
:
#e6e6e6
;
}
#todoapp
input
::input-placeholder
{
font-style
:
italic
;
font-weight
:
300
;
color
:
#e6e6e6
;
}
#todoapp
h1
{
position
:
absolute
;
top
:
-1
20
px
;
top
:
-1
55
px
;
width
:
100%
;
font-size
:
7
0px
;
font-weight
:
bold
;
font-size
:
10
0px
;
font-weight
:
100
;
text-align
:
center
;
color
:
#b3b3b3
;
color
:
rgba
(
255
,
255
,
255
,
0.3
);
text-shadow
:
-1px
-1px
rgba
(
0
,
0
,
0
,
0.2
);
color
:
rgba
(
175
,
47
,
47
,
0.15
);
-webkit-text-rendering
:
optimizeLegibility
;
-moz-text-rendering
:
optimizeLegibility
;
-ms-text-rendering
:
optimizeLegibility
;
-o-text-rendering
:
optimizeLegibility
;
text-rendering
:
optimizeLegibility
;
}
#header
{
padding-top
:
15px
;
border-radius
:
inherit
;
}
#header
:before
{
content
:
''
;
position
:
absolute
;
top
:
0
;
right
:
0
;
left
:
0
;
height
:
15px
;
z-index
:
2
;
border-bottom
:
1px
solid
#6c615c
;
background
:
#8d7d77
;
background
:
-webkit-gradient
(
linear
,
left
top
,
left
bottom
,
from
(
rgba
(
132
,
110
,
100
,
0.8
)),
to
(
rgba
(
101
,
84
,
76
,
0.8
)));
background
:
-webkit-linear-gradient
(
top
,
rgba
(
132
,
110
,
100
,
0.8
),
rgba
(
101
,
84
,
76
,
0.8
));
background
:
linear-gradient
(
top
,
rgba
(
132
,
110
,
100
,
0.8
),
rgba
(
101
,
84
,
76
,
0.8
));
filter
:
progid
:
DXImageTransform
.
Microsoft
.
gradient
(
GradientType
=
0
,
StartColorStr
=
'#9d8b83'
,
EndColorStr
=
'#847670'
);
border-top-left-radius
:
1px
;
border-top-right-radius
:
1px
;
}
#new-todo
,
.edit
{
position
:
relative
;
...
...
@@ -117,6 +94,7 @@ input[type="checkbox"] {
width
:
100%
;
font-size
:
24px
;
font-family
:
inherit
;
font-weight
:
inherit
;
line-height
:
1.4em
;
border
:
0
;
outline
:
none
;
...
...
@@ -124,29 +102,25 @@ input[type="checkbox"] {
padding
:
6px
;
border
:
1px
solid
#999
;
box-shadow
:
inset
0
-1px
5px
0
rgba
(
0
,
0
,
0
,
0.2
);
-moz-box-sizing
:
border-box
;
-ms-box-sizing
:
border-box
;
-o-box-sizing
:
border-box
;
box-sizing
:
border-box
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
-o-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
}
#new-todo
{
padding
:
16px
16px
16px
60px
;
border
:
none
;
background
:
rgba
(
0
,
0
,
0
,
0.02
);
z-index
:
2
;
box-shadow
:
none
;
background
:
rgba
(
0
,
0
,
0
,
0.003
);
box-shadow
:
inset
0
-2px
1px
rgba
(
0
,
0
,
0
,
0.03
);
}
#main
{
position
:
relative
;
z-index
:
2
;
border-top
:
1px
dotted
#adadad
;
border-top
:
1px
solid
#e6e6e6
;
}
label
[
for
=
'toggle-all'
]
{
...
...
@@ -155,19 +129,19 @@ label[for='toggle-all'] {
#toggle-all
{
position
:
absolute
;
top
:
-42px
;
left
:
-4px
;
width
:
40px
;
top
:
-55px
;
left
:
-12px
;
width
:
60px
;
height
:
34px
;
text-align
:
center
;
/* Mobile Safari */
border
:
none
;
border
:
none
;
/* Mobile Safari */
}
#toggle-all
:before
{
content
:
'
»
'
;
font-size
:
2
8
px
;
color
:
#
d9d9d9
;
padding
:
0
25px
7px
;
content
:
'
❯
'
;
font-size
:
2
2
px
;
color
:
#
e6e6e6
;
padding
:
10px
27px
10px
2
7px
;
}
#toggle-all
:checked:before
{
...
...
@@ -183,7 +157,7 @@ label[for='toggle-all'] {
#todo-list
li
{
position
:
relative
;
font-size
:
24px
;
border-bottom
:
1px
dotted
#ccc
;
border-bottom
:
1px
solid
#ededed
;
}
#todo-list
li
:last-child
{
...
...
@@ -215,28 +189,18 @@ label[for='toggle-all'] {
top
:
0
;
bottom
:
0
;
margin
:
auto
0
;
/* Mobile Safari */
border
:
none
;
border
:
none
;
/* Mobile Safari */
-webkit-appearance
:
none
;
-ms-appearance
:
none
;
-o-appearance
:
none
;
appearance
:
none
;
}
#todo-list
li
.toggle
:after
{
content
:
'✔'
;
/* 40 + a couple of pixels visual adjustment */
line-height
:
43px
;
font-size
:
20px
;
color
:
#d9d9d9
;
text-shadow
:
0
-1px
0
#bfbfbf
;
content
:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>')
;
}
#todo-list
li
.toggle
:checked:after
{
color
:
#85ada7
;
text-shadow
:
0
1px
0
#669991
;
bottom
:
1px
;
position
:
relative
;
content
:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>')
;
}
#todo-list
li
label
{
...
...
@@ -246,12 +210,11 @@ label[for='toggle-all'] {
margin-left
:
45px
;
display
:
block
;
line-height
:
1.2
;
-webkit-transition
:
color
0.4s
;
transition
:
color
0.4s
;
}
#todo-list
li
.completed
label
{
color
:
#
a9a9a
9
;
color
:
#
d9d9d
9
;
text-decoration
:
line-through
;
}
...
...
@@ -264,21 +227,18 @@ label[for='toggle-all'] {
width
:
40px
;
height
:
40px
;
margin
:
auto
0
;
font-size
:
22
px
;
color
:
#
a88a8
a
;
-webkit-transition
:
all
0.2s
;
transition
:
all
0.2s
;
font-size
:
30
px
;
color
:
#
cc9a9
a
;
margin-bottom
:
11px
;
transition
:
color
0.2s
ease-out
;
}
#todo-list
li
.destroy
:hover
{
text-shadow
:
0
0
1px
#000
,
0
0
10px
rgba
(
199
,
107
,
107
,
0.8
);
-webkit-transform
:
scale
(
1.3
);
transform
:
scale
(
1.3
);
color
:
#af5b5e
;
}
#todo-list
li
.destroy
:after
{
content
:
'
✖
'
;
content
:
'
×
'
;
}
#todo-list
li
:hover
.destroy
{
...
...
@@ -295,29 +255,25 @@ label[for='toggle-all'] {
#footer
{
color
:
#777
;
padding
:
0
15px
;
position
:
absolute
;
right
:
0
;
bottom
:
-31px
;
left
:
0
;
padding
:
10px
15px
;
height
:
20px
;
z-index
:
1
;
text-align
:
center
;
border-top
:
1px
solid
#e6e6e6
;
}
#footer
:before
{
content
:
''
;
position
:
absolute
;
right
:
0
;
bottom
:
31px
;
bottom
:
0
;
left
:
0
;
height
:
50px
;
z-index
:
-1
;
box-shadow
:
0
1px
1px
rgba
(
0
,
0
,
0
,
0.
3
),
0
6px
0
-3px
rgba
(
255
,
255
,
255
,
0.8
)
,
0
7px
1px
-3px
rgba
(
0
,
0
,
0
,
0.3
),
0
43px
0
-6px
rgba
(
255
,
255
,
255
,
0.8
)
,
0
44
px
2px
-6px
rgba
(
0
,
0
,
0
,
0.2
);
overflow
:
hidden
;
box-shadow
:
0
1px
1px
rgba
(
0
,
0
,
0
,
0.
2
),
0
8px
0
-3px
#f6f6f6
,
0
9px
1px
-3px
rgba
(
0
,
0
,
0
,
0.2
),
0
16px
0
-6px
#f6f6f6
,
0
17
px
2px
-6px
rgba
(
0
,
0
,
0
,
0.2
);
}
#todo-count
{
...
...
@@ -325,6 +281,10 @@ label[for='toggle-all'] {
text-align
:
left
;
}
#todo-count
strong
{
font-weight
:
300
;
}
#filters
{
margin
:
0
;
padding
:
0
;
...
...
@@ -339,49 +299,72 @@ label[for='toggle-all'] {
}
#filters
li
a
{
color
:
#83756f
;
margin
:
2px
;
color
:
inherit
;
margin
:
3px
;
padding
:
3px
7px
;
text-decoration
:
none
;
border
:
1px
solid
transparent
;
border-radius
:
3px
;
}
#filters
li
a
.selected
,
#filters
li
a
:hover
{
border-color
:
rgba
(
175
,
47
,
47
,
0.1
);
}
#filters
li
a
.selected
{
font-weight
:
bold
;
border-color
:
rgba
(
175
,
47
,
47
,
0.2
)
;
}
#clear-completed
{
#clear-completed
,
html
#clear-completed
:active
{
float
:
right
;
position
:
relative
;
line-height
:
20px
;
text-decoration
:
none
;
background
:
rgba
(
0
,
0
,
0
,
0.1
);
font-size
:
11px
;
padding
:
0
10px
;
border-radius
:
3px
;
box-shadow
:
0
-1px
0
0
rgba
(
0
,
0
,
0
,
0.2
);
cursor
:
pointer
;
visibility
:
hidden
;
position
:
relative
;
}
#clear-completed
::after
{
visibility
:
visible
;
content
:
'Clear completed'
;
position
:
absolute
;
right
:
0
;
white-space
:
nowrap
;
}
#clear-completed
:hover
{
background
:
rgba
(
0
,
0
,
0
,
0.15
);
box-shadow
:
0
-1px
0
0
rgba
(
0
,
0
,
0
,
0.3
);
#clear-completed
:hover::after
{
text-decoration
:
underline
;
}
#info
{
margin
:
65px
auto
0
;
color
:
#
a6a6a6
;
font-size
:
1
2
px
;
text-shadow
:
0
1px
0
rgba
(
255
,
255
,
255
,
0.
7
);
color
:
#
bfbfbf
;
font-size
:
1
0
px
;
text-shadow
:
0
1px
0
rgba
(
255
,
255
,
255
,
0.
5
);
text-align
:
center
;
}
#info
p
{
line-height
:
1
;
}
#info
a
{
color
:
inherit
;
text-decoration
:
none
;
font-weight
:
400
;
}
#info
a
:hover
{
text-decoration
:
underline
;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
and Opera
Can't use it globally since it destroys checkboxes in Firefox
*/
@media
screen
and
(
-webkit-min-device-pixel-ratio
:
0
)
{
#toggle-all
,
#todo-list
li
.toggle
{
...
...
@@ -393,10 +376,6 @@ label[for='toggle-all'] {
}
#toggle-all
{
top
:
-56px
;
left
:
-15px
;
width
:
65px
;
height
:
41px
;
-webkit-transform
:
rotate
(
90deg
);
transform
:
rotate
(
90deg
);
-webkit-appearance
:
none
;
...
...
@@ -404,151 +383,12 @@ label[for='toggle-all'] {
}
}
.hidden
{
display
:
none
;
}
hr
{
margin
:
20px
0
;
border
:
0
;
border-top
:
1px
dashed
#C5C5C5
;
border-bottom
:
1px
dashed
#F7F7F7
;
}
.learn
a
{
font-weight
:
normal
;
text-decoration
:
none
;
color
:
#b83f45
;
}
.learn
a
:hover
{
text-decoration
:
underline
;
color
:
#787e7e
;
}
.learn
h3
,
.learn
h4
,
.learn
h5
{
margin
:
10px
0
;
font-weight
:
500
;
line-height
:
1.2
;
color
:
#000
;
}
.learn
h3
{
font-size
:
24px
;
}
.learn
h4
{
font-size
:
18px
;
}
.learn
h5
{
margin-bottom
:
0
;
font-size
:
14px
;
}
.learn
ul
{
padding
:
0
;
margin
:
0
0
30px
25px
;
}
.learn
li
{
line-height
:
20px
;
}
.learn
p
{
font-size
:
15px
;
font-weight
:
300
;
line-height
:
1.3
;
margin-top
:
0
;
margin-bottom
:
0
;
}
.quote
{
border
:
none
;
margin
:
20px
0
60px
0
;
}
.quote
p
{
font-style
:
italic
;
}
.quote
p
:before
{
content
:
'“'
;
font-size
:
50px
;
opacity
:
.15
;
position
:
absolute
;
top
:
-20px
;
left
:
3px
;
}
.quote
p
:after
{
content
:
'”'
;
font-size
:
50px
;
opacity
:
.15
;
position
:
absolute
;
bottom
:
-42px
;
right
:
3px
;
}
.quote
footer
{
position
:
absolute
;
bottom
:
-40px
;
right
:
0
;
}
.quote
footer
img
{
border-radius
:
3px
;
}
.quote
footer
a
{
margin-left
:
5px
;
vertical-align
:
middle
;
}
.speech-bubble
{
position
:
relative
;
padding
:
10px
;
background
:
rgba
(
0
,
0
,
0
,
.04
);
border-radius
:
5px
;
}
.speech-bubble
:after
{
content
:
''
;
position
:
absolute
;
top
:
100%
;
right
:
30px
;
border
:
13px
solid
transparent
;
border-top-color
:
rgba
(
0
,
0
,
0
,
.04
);
}
.learn-bar
>
.learn
{
position
:
absolute
;
width
:
272px
;
top
:
8px
;
left
:
-300px
;
padding
:
10px
;
border-radius
:
5px
;
background-color
:
rgba
(
255
,
255
,
255
,
.6
);
-webkit-transition-property
:
left
;
transition-property
:
left
;
-webkit-transition-duration
:
500ms
;
transition-duration
:
500ms
;
}
@media
(
min-width
:
899px
)
{
.learn-bar
{
width
:
auto
;
margin
:
0
0
0
300px
;
}
.learn-bar
>
.learn
{
left
:
8px
;
@media
(
max-width
:
430px
)
{
#footer
{
height
:
50px
;
}
.learn-bar
#todoapp
{
width
:
550px
;
margin
:
130px
auto
40px
auto
;
#filters
{
bottom
:
10px
;
}
}
examples/backbone_marionette/node_modules/todomvc-common/base.css
0 → 100644
View file @
25e31ff1
hr
{
margin
:
20px
0
;
border
:
0
;
border-top
:
1px
dashed
#c5c5c5
;
border-bottom
:
1px
dashed
#f7f7f7
;
}
.learn
a
{
font-weight
:
normal
;
text-decoration
:
none
;
color
:
#b83f45
;
}
.learn
a
:hover
{
text-decoration
:
underline
;
color
:
#787e7e
;
}
.learn
h3
,
.learn
h4
,
.learn
h5
{
margin
:
10px
0
;
font-weight
:
500
;
line-height
:
1.2
;
color
:
#000
;
}
.learn
h3
{
font-size
:
24px
;
}
.learn
h4
{
font-size
:
18px
;
}
.learn
h5
{
margin-bottom
:
0
;
font-size
:
14px
;
}
.learn
ul
{
padding
:
0
;
margin
:
0
0
30px
25px
;
}
.learn
li
{
line-height
:
20px
;
}
.learn
p
{
font-size
:
15px
;
font-weight
:
300
;
line-height
:
1.3
;
margin-top
:
0
;
margin-bottom
:
0
;
}
#issue-count
{
display
:
none
;
}
.quote
{
border
:
none
;
margin
:
20px
0
60px
0
;
}
.quote
p
{
font-style
:
italic
;
}
.quote
p
:before
{
content
:
'“'
;
font-size
:
50px
;
opacity
:
.15
;
position
:
absolute
;
top
:
-20px
;
left
:
3px
;
}
.quote
p
:after
{
content
:
'”'
;
font-size
:
50px
;
opacity
:
.15
;
position
:
absolute
;
bottom
:
-42px
;
right
:
3px
;
}
.quote
footer
{
position
:
absolute
;
bottom
:
-40px
;
right
:
0
;
}
.quote
footer
img
{
border-radius
:
3px
;
}
.quote
footer
a
{
margin-left
:
5px
;
vertical-align
:
middle
;
}
.speech-bubble
{
position
:
relative
;
padding
:
10px
;
background
:
rgba
(
0
,
0
,
0
,
.04
);
border-radius
:
5px
;
}
.speech-bubble
:after
{
content
:
''
;
position
:
absolute
;
top
:
100%
;
right
:
30px
;
border
:
13px
solid
transparent
;
border-top-color
:
rgba
(
0
,
0
,
0
,
.04
);
}
.learn-bar
>
.learn
{
position
:
absolute
;
width
:
272px
;
top
:
8px
;
left
:
-300px
;
padding
:
10px
;
border-radius
:
5px
;
background-color
:
rgba
(
255
,
255
,
255
,
.6
);
transition-property
:
left
;
transition-duration
:
500ms
;
}
@media
(
min-width
:
899px
)
{
.learn-bar
{
width
:
auto
;
padding-left
:
300px
;
}
.learn-bar
>
.learn
{
left
:
8px
;
}
}
examples/backbone_marionette/
bower_component
s/todomvc-common/base.js
→
examples/backbone_marionette/
node_module
s/todomvc-common/base.js
View file @
25e31ff1
/* global _ */
(
function
()
{
'
use strict
'
;
/* jshint ignore:start */
// Underscore's Template Module
// Courtesy of underscorejs.org
var
_
=
(
function
(
_
)
{
...
...
@@ -114,6 +116,7 @@
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
'
));
}
/* jshint ignore:end */
function
redirect
()
{
if
(
location
.
hostname
===
'
tastejs.github.io
'
)
{
...
...
@@ -175,13 +178,17 @@
if
(
learnJSON
.
backend
)
{
this
.
frameworkJSON
=
learnJSON
.
backend
;
this
.
frameworkJSON
.
issueLabel
=
framework
;
this
.
append
({
backend
:
true
});
}
else
if
(
learnJSON
[
framework
])
{
this
.
frameworkJSON
=
learnJSON
[
framework
];
this
.
frameworkJSON
.
issueLabel
=
framework
;
this
.
append
();
}
this
.
fetchIssueCount
();
}
Learn
.
prototype
.
append
=
function
(
opts
)
{
...
...
@@ -212,6 +219,26 @@
document
.
body
.
insertAdjacentHTML
(
'
afterBegin
'
,
aside
.
outerHTML
);
};
Learn
.
prototype
.
fetchIssueCount
=
function
()
{
var
issueLink
=
document
.
getElementById
(
'
issue-count-link
'
);
if
(
issueLink
)
{
var
url
=
issueLink
.
href
.
replace
(
'
https://github.com
'
,
'
https://api.github.com/repos
'
);
var
xhr
=
new
XMLHttpRequest
();
xhr
.
open
(
'
GET
'
,
url
,
true
);
xhr
.
onload
=
function
(
e
)
{
var
parsedResponse
=
JSON
.
parse
(
e
.
target
.
responseText
);
if
(
parsedResponse
instanceof
Array
)
{
var
count
=
parsedResponse
.
length
if
(
count
!==
0
)
{
issueLink
.
innerHTML
=
'
This app has
'
+
count
+
'
open issues
'
;
document
.
getElementById
(
'
issue-count
'
).
style
.
display
=
'
inline
'
;
}
}
};
xhr
.
send
();
}
};
redirect
();
getFile
(
'
learn.json
'
,
Learn
);
})();
examples/backbone_marionette/
bower_component
s/underscore/underscore.js
→
examples/backbone_marionette/
node_module
s/underscore/underscore.js
View file @
25e31ff1
// Underscore.js 1.
4.4
// Underscore.js 1.
7.0
// http://underscorejs.org
// (c) 2009-201
3 Jeremy Ashkenas, DocumentCloud Inc.
// (c) 2009-201
4 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(
function
()
{
...
...
@@ -8,20 +8,18 @@
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `
global
` on the server.
// Establish the root object, `window` in the browser, or `
exports
` on the server.
var
root
=
this
;
// Save the previous value of the `_` variable.
var
previousUnderscore
=
root
.
_
;
// Establish the object that gets returned to break out of a loop iteration.
var
breaker
=
{};
// Save bytes in the minified (but not gzipped) version:
var
ArrayProto
=
Array
.
prototype
,
ObjProto
=
Object
.
prototype
,
FuncProto
=
Function
.
prototype
;
// Create quick reference variables for speed access to core prototypes.
var
push
=
ArrayProto
.
push
,
var
push
=
ArrayProto
.
push
,
slice
=
ArrayProto
.
slice
,
concat
=
ArrayProto
.
concat
,
toString
=
ObjProto
.
toString
,
...
...
@@ -30,15 +28,6 @@
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach
=
ArrayProto
.
forEach
,
nativeMap
=
ArrayProto
.
map
,
nativeReduce
=
ArrayProto
.
reduce
,
nativeReduceRight
=
ArrayProto
.
reduceRight
,
nativeFilter
=
ArrayProto
.
filter
,
nativeEvery
=
ArrayProto
.
every
,
nativeSome
=
ArrayProto
.
some
,
nativeIndexOf
=
ArrayProto
.
indexOf
,
nativeLastIndexOf
=
ArrayProto
.
lastIndexOf
,
nativeIsArray
=
Array
.
isArray
,
nativeKeys
=
Object
.
keys
,
nativeBind
=
FuncProto
.
bind
;
...
...
@@ -52,8 +41,7 @@
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
// the browser, add `_` as a global object.
if
(
typeof
exports
!==
'
undefined
'
)
{
if
(
typeof
module
!==
'
undefined
'
&&
module
.
exports
)
{
exports
=
module
.
exports
=
_
;
...
...
@@ -64,98 +52,125 @@
}
// Current version.
_
.
VERSION
=
'
1.4.4
'
;
_
.
VERSION
=
'
1.7.0
'
;
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
var
createCallback
=
function
(
func
,
context
,
argCount
)
{
if
(
context
===
void
0
)
return
func
;
switch
(
argCount
==
null
?
3
:
argCount
)
{
case
1
:
return
function
(
value
)
{
return
func
.
call
(
context
,
value
);
};
case
2
:
return
function
(
value
,
other
)
{
return
func
.
call
(
context
,
value
,
other
);
};
case
3
:
return
function
(
value
,
index
,
collection
)
{
return
func
.
call
(
context
,
value
,
index
,
collection
);
};
case
4
:
return
function
(
accumulator
,
value
,
index
,
collection
)
{
return
func
.
call
(
context
,
accumulator
,
value
,
index
,
collection
);
};
}
return
function
()
{
return
func
.
apply
(
context
,
arguments
);
};
};
// A mostly-internal function to generate callbacks that can be applied
// to each element in a collection, returning the desired result — either
// identity, an arbitrary callback, a property matcher, or a property accessor.
_
.
iteratee
=
function
(
value
,
context
,
argCount
)
{
if
(
value
==
null
)
return
_
.
identity
;
if
(
_
.
isFunction
(
value
))
return
createCallback
(
value
,
context
,
argCount
);
if
(
_
.
isObject
(
value
))
return
_
.
matches
(
value
);
return
_
.
property
(
value
);
};
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles
objects with the built-in `forEach`, arrays, and raw objects.
//
Delegates to **ECMAScript 5**'s native `forEach` if availabl
e.
var
each
=
_
.
each
=
_
.
forEach
=
function
(
obj
,
iterator
,
context
)
{
if
(
obj
==
null
)
return
;
i
f
(
nativeForEach
&&
obj
.
forEach
===
nativeForEach
)
{
obj
.
forEach
(
iterator
,
context
)
;
}
else
if
(
obj
.
length
===
+
obj
.
length
)
{
for
(
var
i
=
0
,
l
=
obj
.
length
;
i
<
l
;
i
++
)
{
i
f
(
iterator
.
call
(
context
,
obj
[
i
],
i
,
obj
)
===
breaker
)
return
;
// Handles
raw objects in addition to array-likes. Treats all
//
sparse array-likes as if they were dens
e.
_
.
each
=
_
.
forEach
=
function
(
obj
,
iteratee
,
context
)
{
if
(
obj
==
null
)
return
obj
;
i
teratee
=
createCallback
(
iteratee
,
context
);
var
i
,
length
=
obj
.
length
;
if
(
length
===
+
length
)
{
for
(
i
=
0
;
i
<
length
;
i
++
)
{
i
teratee
(
obj
[
i
],
i
,
obj
)
;
}
}
else
{
for
(
var
key
in
obj
)
{
if
(
_
.
has
(
obj
,
key
))
{
if
(
iterator
.
call
(
context
,
obj
[
key
],
key
,
obj
)
===
breaker
)
return
;
}
var
keys
=
_
.
keys
(
obj
);
for
(
i
=
0
,
length
=
keys
.
length
;
i
<
length
;
i
++
)
{
iteratee
(
obj
[
keys
[
i
]],
keys
[
i
],
obj
);
}
}
return
obj
;
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_
.
map
=
_
.
collect
=
function
(
obj
,
iterator
,
context
)
{
var
results
=
[];
if
(
obj
==
null
)
return
results
;
if
(
nativeMap
&&
obj
.
map
===
nativeMap
)
return
obj
.
map
(
iterator
,
context
);
each
(
obj
,
function
(
value
,
index
,
list
)
{
results
[
results
.
length
]
=
iterator
.
call
(
context
,
value
,
index
,
list
);
});
// Return the results of applying the iteratee to each element.
_
.
map
=
_
.
collect
=
function
(
obj
,
iteratee
,
context
)
{
if
(
obj
==
null
)
return
[];
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
,
results
=
Array
(
length
),
currentKey
;
for
(
var
index
=
0
;
index
<
length
;
index
++
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
results
[
index
]
=
iteratee
(
obj
[
currentKey
],
currentKey
,
obj
);
}
return
results
;
};
var
reduceError
=
'
Reduce of empty array with no initial value
'
;
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_
.
reduce
=
_
.
foldl
=
_
.
inject
=
function
(
obj
,
iterator
,
memo
,
context
)
{
var
initial
=
arguments
.
length
>
2
;
// or `foldl`.
_
.
reduce
=
_
.
foldl
=
_
.
inject
=
function
(
obj
,
iteratee
,
memo
,
context
)
{
if
(
obj
==
null
)
obj
=
[];
if
(
nativeReduce
&&
obj
.
reduce
===
nativeReduce
)
{
if
(
context
)
iterator
=
_
.
bind
(
iterator
,
context
);
return
initial
?
obj
.
reduce
(
iterator
,
memo
)
:
obj
.
reduce
(
iterator
);
}
each
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
!
initial
)
{
memo
=
value
;
initial
=
true
;
}
else
{
memo
=
iterator
.
call
(
context
,
memo
,
value
,
index
,
list
);
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
);
}
});
if
(
!
initial
)
throw
new
TypeError
(
reduceError
);
return
memo
;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_
.
reduceRight
=
_
.
foldr
=
function
(
obj
,
iterator
,
memo
,
context
)
{
var
initial
=
arguments
.
length
>
2
;
_
.
reduceRight
=
_
.
foldr
=
function
(
obj
,
iteratee
,
memo
,
context
)
{
if
(
obj
==
null
)
obj
=
[];
if
(
nativeReduceRight
&&
obj
.
reduceRight
===
nativeReduceRight
)
{
if
(
context
)
iterator
=
_
.
bind
(
iterator
,
context
);
return
initial
?
obj
.
reduceRight
(
iterator
,
memo
)
:
obj
.
reduceRight
(
iterator
);
}
var
length
=
obj
.
length
;
if
(
length
!==
+
length
)
{
var
keys
=
_
.
keys
(
obj
);
length
=
keys
.
length
;
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
];
}
each
(
obj
,
function
(
value
,
index
,
list
)
{
index
=
keys
?
keys
[
--
length
]
:
--
length
;
if
(
!
initial
)
{
memo
=
obj
[
index
];
initial
=
true
;
}
else
{
memo
=
iterator
.
call
(
context
,
memo
,
obj
[
index
],
index
,
list
);
while
(
index
--
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
memo
=
iteratee
(
memo
,
obj
[
currentKey
],
currentKey
,
obj
);
}
});
if
(
!
initial
)
throw
new
TypeError
(
reduceError
);
return
memo
;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_
.
find
=
_
.
detect
=
function
(
obj
,
iterator
,
context
)
{
_
.
find
=
_
.
detect
=
function
(
obj
,
predicate
,
context
)
{
var
result
;
any
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
iterator
.
call
(
context
,
value
,
index
,
list
))
{
predicate
=
_
.
iteratee
(
predicate
,
context
);
_
.
some
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
predicate
(
value
,
index
,
list
))
{
result
=
value
;
return
true
;
}
...
...
@@ -164,61 +179,58 @@
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_
.
filter
=
_
.
select
=
function
(
obj
,
iterator
,
context
)
{
_
.
filter
=
_
.
select
=
function
(
obj
,
predicate
,
context
)
{
var
results
=
[];
if
(
obj
==
null
)
return
results
;
if
(
nativeFilter
&&
obj
.
filter
===
nativeFilter
)
return
obj
.
filter
(
iterator
,
context
);
each
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
iterator
.
call
(
context
,
value
,
index
,
list
))
results
[
results
.
length
]
=
value
;
predicate
=
_
.
iteratee
(
predicate
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
predicate
(
value
,
index
,
list
))
results
.
push
(
value
)
;
});
return
results
;
};
// Return all the elements for which a truth test fails.
_
.
reject
=
function
(
obj
,
iterator
,
context
)
{
return
_
.
filter
(
obj
,
function
(
value
,
index
,
list
)
{
return
!
iterator
.
call
(
context
,
value
,
index
,
list
);
},
context
);
_
.
reject
=
function
(
obj
,
predicate
,
context
)
{
return
_
.
filter
(
obj
,
_
.
negate
(
_
.
iteratee
(
predicate
)),
context
);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_
.
every
=
_
.
all
=
function
(
obj
,
iterator
,
context
)
{
iterator
||
(
iterator
=
_
.
identity
);
var
result
=
true
;
if
(
obj
==
null
)
return
result
;
if
(
nativeEvery
&&
obj
.
every
===
nativeEvery
)
return
obj
.
every
(
iterator
,
context
);
each
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
!
(
result
=
result
&&
iterator
.
call
(
context
,
value
,
index
,
list
)))
return
breaker
;
});
return
!!
result
;
_
.
every
=
_
.
all
=
function
(
obj
,
predicate
,
context
)
{
if
(
obj
==
null
)
return
true
;
predicate
=
_
.
iteratee
(
predicate
,
context
);
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
,
index
,
currentKey
;
for
(
index
=
0
;
index
<
length
;
index
++
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
if
(
!
predicate
(
obj
[
currentKey
],
currentKey
,
obj
))
return
false
;
}
return
true
;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var
any
=
_
.
some
=
_
.
any
=
function
(
obj
,
iterator
,
context
)
{
iterator
||
(
iterator
=
_
.
identity
);
var
result
=
false
;
if
(
obj
==
null
)
return
result
;
if
(
nativeSome
&&
obj
.
some
===
nativeSome
)
return
obj
.
some
(
iterator
,
context
);
each
(
obj
,
function
(
value
,
index
,
list
)
{
if
(
result
||
(
result
=
iterator
.
call
(
context
,
value
,
index
,
list
)))
return
breaker
;
});
return
!!
result
;
_
.
some
=
_
.
any
=
function
(
obj
,
predicate
,
context
)
{
if
(
obj
==
null
)
return
false
;
predicate
=
_
.
iteratee
(
predicate
,
context
);
var
keys
=
obj
.
length
!==
+
obj
.
length
&&
_
.
keys
(
obj
),
length
=
(
keys
||
obj
).
length
,
index
,
currentKey
;
for
(
index
=
0
;
index
<
length
;
index
++
)
{
currentKey
=
keys
?
keys
[
index
]
:
index
;
if
(
predicate
(
obj
[
currentKey
],
currentKey
,
obj
))
return
true
;
}
return
false
;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_
.
contains
=
_
.
include
=
function
(
obj
,
target
)
{
if
(
obj
==
null
)
return
false
;
if
(
nativeIndexOf
&&
obj
.
indexOf
===
nativeIndexOf
)
return
obj
.
indexOf
(
target
)
!=
-
1
;
return
any
(
obj
,
function
(
value
)
{
return
value
===
target
;
});
if
(
obj
.
length
!==
+
obj
.
length
)
obj
=
_
.
values
(
obj
);
return
_
.
indexOf
(
obj
,
target
)
>=
0
;
};
// Invoke a method (with arguments) on every item in a collection.
...
...
@@ -232,83 +244,104 @@
// Convenience version of a common use case of `map`: fetching a property.
_
.
pluck
=
function
(
obj
,
key
)
{
return
_
.
map
(
obj
,
function
(
value
){
return
value
[
key
];
}
);
return
_
.
map
(
obj
,
_
.
property
(
key
)
);
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_
.
where
=
function
(
obj
,
attrs
,
first
)
{
if
(
_
.
isEmpty
(
attrs
))
return
first
?
null
:
[];
return
_
[
first
?
'
find
'
:
'
filter
'
](
obj
,
function
(
value
)
{
for
(
var
key
in
attrs
)
{
if
(
attrs
[
key
]
!==
value
[
key
])
return
false
;
}
return
true
;
});
_
.
where
=
function
(
obj
,
attrs
)
{
return
_
.
filter
(
obj
,
_
.
matches
(
attrs
));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_
.
findWhere
=
function
(
obj
,
attrs
)
{
return
_
.
where
(
obj
,
attrs
,
true
);
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
_
.
max
=
function
(
obj
,
iterator
,
context
)
{
if
(
!
iterator
&&
_
.
isArray
(
obj
)
&&
obj
[
0
]
===
+
obj
[
0
]
&&
obj
.
length
<
65535
)
{
return
Math
.
max
.
apply
(
Math
,
obj
);
return
_
.
find
(
obj
,
_
.
matches
(
attrs
));
};
// Return the maximum element (or element-based computation).
_
.
max
=
function
(
obj
,
iteratee
,
context
)
{
var
result
=
-
Infinity
,
lastComputed
=
-
Infinity
,
value
,
computed
;
if
(
iteratee
==
null
&&
obj
!=
null
)
{
obj
=
obj
.
length
===
+
obj
.
length
?
obj
:
_
.
values
(
obj
);
for
(
var
i
=
0
,
length
=
obj
.
length
;
i
<
length
;
i
++
)
{
value
=
obj
[
i
];
if
(
value
>
result
)
{
result
=
value
;
}
}
}
else
{
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
computed
=
iteratee
(
value
,
index
,
list
);
if
(
computed
>
lastComputed
||
computed
===
-
Infinity
&&
result
===
-
Infinity
)
{
result
=
value
;
lastComputed
=
computed
;
}
if
(
!
iterator
&&
_
.
isEmpty
(
obj
))
return
-
Infinity
;
var
result
=
{
computed
:
-
Infinity
,
value
:
-
Infinity
};
each
(
obj
,
function
(
value
,
index
,
list
)
{
var
computed
=
iterator
?
iterator
.
call
(
context
,
value
,
index
,
list
)
:
value
;
computed
>=
result
.
computed
&&
(
result
=
{
value
:
value
,
computed
:
computed
});
});
return
result
.
value
;
}
return
result
;
};
// Return the minimum element (or element-based computation).
_
.
min
=
function
(
obj
,
iterator
,
context
)
{
if
(
!
iterator
&&
_
.
isArray
(
obj
)
&&
obj
[
0
]
===
+
obj
[
0
]
&&
obj
.
length
<
65535
)
{
return
Math
.
min
.
apply
(
Math
,
obj
);
}
if
(
!
iterator
&&
_
.
isEmpty
(
obj
))
return
Infinity
;
var
result
=
{
computed
:
Infinity
,
value
:
Infinity
};
each
(
obj
,
function
(
value
,
index
,
list
)
{
var
computed
=
iterator
?
iterator
.
call
(
context
,
value
,
index
,
list
)
:
value
;
computed
<
result
.
computed
&&
(
result
=
{
value
:
value
,
computed
:
computed
});
_
.
min
=
function
(
obj
,
iteratee
,
context
)
{
var
result
=
Infinity
,
lastComputed
=
Infinity
,
value
,
computed
;
if
(
iteratee
==
null
&&
obj
!=
null
)
{
obj
=
obj
.
length
===
+
obj
.
length
?
obj
:
_
.
values
(
obj
);
for
(
var
i
=
0
,
length
=
obj
.
length
;
i
<
length
;
i
++
)
{
value
=
obj
[
i
];
if
(
value
<
result
)
{
result
=
value
;
}
}
}
else
{
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
,
list
)
{
computed
=
iteratee
(
value
,
index
,
list
);
if
(
computed
<
lastComputed
||
computed
===
Infinity
&&
result
===
Infinity
)
{
result
=
value
;
lastComputed
=
computed
;
}
});
return
result
.
value
;
}
return
result
;
};
// Shuffle an array.
// Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_
.
shuffle
=
function
(
obj
)
{
var
rand
;
var
index
=
0
;
var
shuffled
=
[]
;
each
(
obj
,
function
(
value
)
{
rand
=
_
.
random
(
index
++
);
shuffled
[
index
-
1
]
=
shuffled
[
rand
];
shuffled
[
rand
]
=
value
;
}
);
var
set
=
obj
&&
obj
.
length
===
+
obj
.
length
?
obj
:
_
.
values
(
obj
)
;
var
length
=
set
.
length
;
var
shuffled
=
Array
(
length
)
;
for
(
var
index
=
0
,
rand
;
index
<
length
;
index
++
)
{
rand
=
_
.
random
(
0
,
index
);
if
(
rand
!==
index
)
shuffled
[
index
]
=
shuffled
[
rand
];
shuffled
[
rand
]
=
set
[
index
]
;
}
return
shuffled
;
};
// An internal function to generate lookup iterators.
var
lookupIterator
=
function
(
value
)
{
return
_
.
isFunction
(
value
)
?
value
:
function
(
obj
){
return
obj
[
value
];
};
// Sample **n** random values from a collection.
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_
.
sample
=
function
(
obj
,
n
,
guard
)
{
if
(
n
==
null
||
guard
)
{
if
(
obj
.
length
!==
+
obj
.
length
)
obj
=
_
.
values
(
obj
);
return
obj
[
_
.
random
(
obj
.
length
-
1
)];
}
return
_
.
shuffle
(
obj
).
slice
(
0
,
Math
.
max
(
0
,
n
));
};
// Sort the object's values by a criterion produced by an iterat
or
.
_
.
sortBy
=
function
(
obj
,
valu
e
,
context
)
{
var
iterator
=
lookupIterator
(
value
);
// Sort the object's values by a criterion produced by an iterat
ee
.
_
.
sortBy
=
function
(
obj
,
iterate
e
,
context
)
{
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
return
_
.
pluck
(
_
.
map
(
obj
,
function
(
value
,
index
,
list
)
{
return
{
value
:
value
,
index
:
index
,
criteria
:
iterator
.
call
(
context
,
value
,
index
,
list
)
value
:
value
,
index
:
index
,
criteria
:
iteratee
(
value
,
index
,
list
)
};
}).
sort
(
function
(
left
,
right
)
{
var
a
=
left
.
criteria
;
...
...
@@ -317,53 +350,56 @@
if
(
a
>
b
||
a
===
void
0
)
return
1
;
if
(
a
<
b
||
b
===
void
0
)
return
-
1
;
}
return
left
.
index
<
right
.
index
?
-
1
:
1
;
return
left
.
index
-
right
.
index
;
}),
'
value
'
);
};
// An internal function used for aggregate "group by" operations.
var
group
=
function
(
obj
,
value
,
context
,
behavior
)
{
var
group
=
function
(
behavior
)
{
return
function
(
obj
,
iteratee
,
context
)
{
var
result
=
{};
var
iterator
=
lookupIterator
(
value
||
_
.
identity
);
each
(
obj
,
function
(
value
,
index
)
{
var
key
=
iterator
.
call
(
context
,
value
,
index
,
obj
);
behavior
(
result
,
key
,
value
);
iteratee
=
_
.
iteratee
(
iteratee
,
context
);
_
.
each
(
obj
,
function
(
value
,
index
)
{
var
key
=
iteratee
(
value
,
index
,
obj
);
behavior
(
result
,
value
,
key
);
});
return
result
;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_
.
groupBy
=
function
(
obj
,
value
,
context
)
{
return
group
(
obj
,
value
,
context
,
function
(
result
,
key
,
value
)
{
(
_
.
has
(
result
,
key
)
?
result
[
key
]
:
(
result
[
key
]
=
[])).
push
(
value
);
_
.
groupBy
=
group
(
function
(
result
,
value
,
key
)
{
if
(
_
.
has
(
result
,
key
))
result
[
key
].
push
(
value
);
else
result
[
key
]
=
[
value
];
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_
.
indexBy
=
group
(
function
(
result
,
value
,
key
)
{
result
[
key
]
=
value
;
});
};
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_
.
countBy
=
function
(
obj
,
value
,
context
)
{
return
group
(
obj
,
value
,
context
,
function
(
result
,
key
)
{
if
(
!
_
.
has
(
result
,
key
))
result
[
key
]
=
0
;
result
[
key
]
++
;
_
.
countBy
=
group
(
function
(
result
,
value
,
key
)
{
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
,
iterat
or
,
context
)
{
iterat
or
=
iterator
==
null
?
_
.
identity
:
lookupIterator
(
iterator
);
var
value
=
iterat
or
.
call
(
context
,
obj
);
_
.
sortedIndex
=
function
(
array
,
obj
,
iterat
ee
,
context
)
{
iterat
ee
=
_
.
iteratee
(
iteratee
,
context
,
1
);
var
value
=
iterat
ee
(
obj
);
var
low
=
0
,
high
=
array
.
length
;
while
(
low
<
high
)
{
var
mid
=
(
low
+
high
)
>>>
1
;
i
terator
.
call
(
context
,
array
[
mid
])
<
value
?
low
=
mid
+
1
:
high
=
mid
;
var
mid
=
low
+
high
>>>
1
;
i
f
(
iteratee
(
array
[
mid
])
<
value
)
low
=
mid
+
1
;
else
high
=
mid
;
}
return
low
;
};
// Safely c
onvert anything iterable into a real, live array
.
// Safely c
reate a real, live array from anything iterable
.
_
.
toArray
=
function
(
obj
)
{
if
(
!
obj
)
return
[];
if
(
_
.
isArray
(
obj
))
return
slice
.
call
(
obj
);
...
...
@@ -374,7 +410,18 @@
// Return the number of elements in an object.
_
.
size
=
function
(
obj
)
{
if
(
obj
==
null
)
return
0
;
return
(
obj
.
length
===
+
obj
.
length
)
?
obj
.
length
:
_
.
keys
(
obj
).
length
;
return
obj
.
length
===
+
obj
.
length
?
obj
.
length
:
_
.
keys
(
obj
).
length
;
};
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_
.
partition
=
function
(
obj
,
predicate
,
context
)
{
predicate
=
_
.
iteratee
(
predicate
,
context
);
var
pass
=
[],
fail
=
[];
_
.
each
(
obj
,
function
(
value
,
key
,
obj
)
{
(
predicate
(
value
,
key
,
obj
)
?
pass
:
fail
).
push
(
value
);
});
return
[
pass
,
fail
];
};
// Array Functions
...
...
@@ -385,7 +432,9 @@
// allows it to work with `_.map`.
_
.
first
=
_
.
head
=
_
.
take
=
function
(
array
,
n
,
guard
)
{
if
(
array
==
null
)
return
void
0
;
return
(
n
!=
null
)
&&
!
guard
?
slice
.
call
(
array
,
0
,
n
)
:
array
[
0
];
if
(
n
==
null
||
guard
)
return
array
[
0
];
if
(
n
<
0
)
return
[];
return
slice
.
call
(
array
,
0
,
n
);
};
// Returns everything but the last entry of the array. Especially useful on
...
...
@@ -393,18 +442,15 @@
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_
.
initial
=
function
(
array
,
n
,
guard
)
{
return
slice
.
call
(
array
,
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
// values in the array. The **guard** check allows it to work with `_.map`.
_
.
last
=
function
(
array
,
n
,
guard
)
{
if
(
array
==
null
)
return
void
0
;
if
(
(
n
!=
null
)
&&
!
guard
)
{
if
(
n
==
null
||
guard
)
return
array
[
array
.
length
-
1
];
return
slice
.
call
(
array
,
Math
.
max
(
array
.
length
-
n
,
0
));
}
else
{
return
array
[
array
.
length
-
1
];
}
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
...
...
@@ -412,7 +458,7 @@
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_
.
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
);
};
// Trim out all falsy values from an array.
...
...
@@ -421,20 +467,26 @@
};
// Internal implementation of a recursive `flatten` function.
var
flatten
=
function
(
input
,
shallow
,
output
)
{
each
(
input
,
function
(
value
)
{
if
(
_
.
isArray
(
value
))
{
shallow
?
push
.
apply
(
output
,
value
)
:
flatten
(
value
,
shallow
,
output
);
var
flatten
=
function
(
input
,
shallow
,
strict
,
output
)
{
if
(
shallow
&&
_
.
every
(
input
,
_
.
isArray
))
{
return
concat
.
apply
(
output
,
input
);
}
for
(
var
i
=
0
,
length
=
input
.
length
;
i
<
length
;
i
++
)
{
var
value
=
input
[
i
];
if
(
!
_
.
isArray
(
value
)
&&
!
_
.
isArguments
(
value
))
{
if
(
!
strict
)
output
.
push
(
value
);
}
else
if
(
shallow
)
{
push
.
apply
(
output
,
value
);
}
else
{
output
.
push
(
value
);
flatten
(
value
,
shallow
,
strict
,
output
);
}
}
});
return
output
;
};
//
Return a completely flattened version of an array
.
//
Flatten out an array, either recursively (by default), or just one level
.
_
.
flatten
=
function
(
array
,
shallow
)
{
return
flatten
(
array
,
shallow
,
[]);
return
flatten
(
array
,
shallow
,
false
,
[]);
};
// Return a version of the array that does not contain the specified value(s).
...
...
@@ -445,56 +497,74 @@
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_
.
uniq
=
_
.
unique
=
function
(
array
,
isSorted
,
iterator
,
context
)
{
if
(
_
.
isFunction
(
isSorted
))
{
context
=
iterator
;
iterator
=
isSorted
;
_
.
uniq
=
_
.
unique
=
function
(
array
,
isSorted
,
iteratee
,
context
)
{
if
(
array
==
null
)
return
[];
if
(
!
_
.
isBoolean
(
isSorted
))
{
context
=
iteratee
;
iteratee
=
isSorted
;
isSorted
=
false
;
}
var
initial
=
iterator
?
_
.
map
(
array
,
iterator
,
context
)
:
array
;
var
result
s
=
[];
if
(
iteratee
!=
null
)
iteratee
=
_
.
iteratee
(
iteratee
,
context
)
;
var
result
=
[];
var
seen
=
[];
each
(
initial
,
function
(
value
,
index
)
{
if
(
isSorted
?
(
!
index
||
seen
[
seen
.
length
-
1
]
!==
value
)
:
!
_
.
contains
(
seen
,
value
))
{
seen
.
push
(
value
);
results
.
push
(
array
[
index
]);
for
(
var
i
=
0
,
length
=
array
.
length
;
i
<
length
;
i
++
)
{
var
value
=
array
[
i
];
if
(
isSorted
)
{
if
(
!
i
||
seen
!==
value
)
result
.
push
(
value
);
seen
=
value
;
}
else
if
(
iteratee
)
{
var
computed
=
iteratee
(
value
,
i
,
array
);
if
(
_
.
indexOf
(
seen
,
computed
)
<
0
)
{
seen
.
push
(
computed
);
result
.
push
(
value
);
}
});
return
results
;
}
else
if
(
_
.
indexOf
(
result
,
value
)
<
0
)
{
result
.
push
(
value
);
}
}
return
result
;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_
.
union
=
function
()
{
return
_
.
uniq
(
concat
.
apply
(
ArrayProto
,
arguments
));
return
_
.
uniq
(
flatten
(
arguments
,
true
,
true
,
[]
));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_
.
intersection
=
function
(
array
)
{
var
rest
=
slice
.
call
(
arguments
,
1
);
return
_
.
filter
(
_
.
uniq
(
array
),
function
(
item
)
{
return
_
.
every
(
rest
,
function
(
other
)
{
return
_
.
indexOf
(
other
,
item
)
>=
0
;
});
});
if
(
array
==
null
)
return
[];
var
result
=
[];
var
argsLength
=
arguments
.
length
;
for
(
var
i
=
0
,
length
=
array
.
length
;
i
<
length
;
i
++
)
{
var
item
=
array
[
i
];
if
(
_
.
contains
(
result
,
item
))
continue
;
for
(
var
j
=
1
;
j
<
argsLength
;
j
++
)
{
if
(
!
_
.
contains
(
arguments
[
j
],
item
))
break
;
}
if
(
j
===
argsLength
)
result
.
push
(
item
);
}
return
result
;
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_
.
difference
=
function
(
array
)
{
var
rest
=
concat
.
apply
(
ArrayProto
,
slice
.
call
(
arguments
,
1
));
return
_
.
filter
(
array
,
function
(
value
){
return
!
_
.
contains
(
rest
,
value
);
});
var
rest
=
flatten
(
slice
.
call
(
arguments
,
1
),
true
,
true
,
[]);
return
_
.
filter
(
array
,
function
(
value
){
return
!
_
.
contains
(
rest
,
value
);
});
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_
.
zip
=
function
()
{
var
args
=
slice
.
call
(
arguments
)
;
var
length
=
_
.
max
(
_
.
pluck
(
args
,
'
length
'
))
;
var
results
=
new
Array
(
length
);
_
.
zip
=
function
(
array
)
{
if
(
array
==
null
)
return
[]
;
var
length
=
_
.
max
(
arguments
,
'
length
'
).
length
;
var
results
=
Array
(
length
);
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
results
[
i
]
=
_
.
pluck
(
arg
s
,
""
+
i
);
results
[
i
]
=
_
.
pluck
(
arg
uments
,
i
);
}
return
results
;
};
...
...
@@ -505,7 +575,7 @@
_
.
object
=
function
(
list
,
values
)
{
if
(
list
==
null
)
return
{};
var
result
=
{};
for
(
var
i
=
0
,
l
=
list
.
length
;
i
<
l
;
i
++
)
{
for
(
var
i
=
0
,
l
ength
=
list
.
length
;
i
<
length
;
i
++
)
{
if
(
values
)
{
result
[
list
[
i
]]
=
values
[
i
];
}
else
{
...
...
@@ -515,37 +585,32 @@
return
result
;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// 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
=
function
(
array
,
item
,
isSorted
)
{
if
(
array
==
null
)
return
-
1
;
var
i
=
0
,
l
=
array
.
length
;
var
i
=
0
,
l
ength
=
array
.
length
;
if
(
isSorted
)
{
if
(
typeof
isSorted
==
'
number
'
)
{
i
=
(
isSorted
<
0
?
Math
.
max
(
0
,
l
+
isSorted
)
:
isSorted
)
;
i
=
isSorted
<
0
?
Math
.
max
(
0
,
length
+
isSorted
)
:
isSorted
;
}
else
{
i
=
_
.
sortedIndex
(
array
,
item
);
return
array
[
i
]
===
item
?
i
:
-
1
;
}
}
if
(
nativeIndexOf
&&
array
.
indexOf
===
nativeIndexOf
)
return
array
.
indexOf
(
item
,
isSorted
);
for
(;
i
<
l
;
i
++
)
if
(
array
[
i
]
===
item
)
return
i
;
for
(;
i
<
length
;
i
++
)
if
(
array
[
i
]
===
item
)
return
i
;
return
-
1
;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_
.
lastIndexOf
=
function
(
array
,
item
,
from
)
{
if
(
array
==
null
)
return
-
1
;
var
hasIndex
=
from
!=
null
;
if
(
nativeLastIndexOf
&&
array
.
lastIndexOf
===
nativeLastIndexOf
)
{
return
hasIndex
?
array
.
lastIndexOf
(
item
,
from
)
:
array
.
lastIndexOf
(
item
);
var
idx
=
array
.
length
;
if
(
typeof
from
==
'
number
'
)
{
idx
=
from
<
0
?
idx
+
from
+
1
:
Math
.
min
(
idx
,
from
+
1
);
}
var
i
=
(
hasIndex
?
from
:
array
.
length
);
while
(
i
--
)
if
(
array
[
i
]
===
item
)
return
i
;
while
(
--
idx
>=
0
)
if
(
array
[
idx
]
===
item
)
return
idx
;
return
-
1
;
};
...
...
@@ -557,15 +622,13 @@
stop
=
start
||
0
;
start
=
0
;
}
step
=
arguments
[
2
]
||
1
;
step
=
step
||
1
;
var
len
=
Math
.
max
(
Math
.
ceil
((
stop
-
start
)
/
step
),
0
);
var
idx
=
0
;
var
range
=
new
Array
(
len
);
var
length
=
Math
.
max
(
Math
.
ceil
((
stop
-
start
)
/
step
),
0
);
var
range
=
Array
(
length
);
while
(
idx
<
len
)
{
range
[
idx
++
]
=
start
;
start
+=
step
;
for
(
var
idx
=
0
;
idx
<
length
;
idx
++
,
start
+=
step
)
{
range
[
idx
]
=
start
;
}
return
range
;
...
...
@@ -574,50 +637,77 @@
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var
Ctor
=
function
(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_
.
bind
=
function
(
func
,
context
)
{
if
(
func
.
bind
===
nativeBind
&&
nativeBind
)
return
nativeBind
.
apply
(
func
,
slice
.
call
(
arguments
,
1
));
var
args
=
slice
.
call
(
arguments
,
2
);
return
function
()
{
return
func
.
apply
(
context
,
args
.
concat
(
slice
.
call
(
arguments
)));
};
var
args
,
bound
;
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
'
);
args
=
slice
.
call
(
arguments
,
2
);
bound
=
function
()
{
if
(
!
(
this
instanceof
bound
))
return
func
.
apply
(
context
,
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
;
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context.
// arguments pre-filled, without changing its dynamic `this` context. _ acts
// as a placeholder, allowing any combination of arguments to be pre-filled.
_
.
partial
=
function
(
func
)
{
var
a
rgs
=
slice
.
call
(
arguments
,
1
);
var
boundA
rgs
=
slice
.
call
(
arguments
,
1
);
return
function
()
{
return
func
.
apply
(
this
,
args
.
concat
(
slice
.
call
(
arguments
)));
var
position
=
0
;
var
args
=
boundArgs
.
slice
();
for
(
var
i
=
0
,
length
=
args
.
length
;
i
<
length
;
i
++
)
{
if
(
args
[
i
]
===
_
)
args
[
i
]
=
arguments
[
position
++
];
}
while
(
position
<
arguments
.
length
)
args
.
push
(
arguments
[
position
++
]);
return
func
.
apply
(
this
,
args
);
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
// Bind a number of an object's methods to that object. Remaining arguments
// are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it.
_
.
bindAll
=
function
(
obj
)
{
var
funcs
=
slice
.
call
(
arguments
,
1
);
if
(
funcs
.
length
===
0
)
funcs
=
_
.
functions
(
obj
);
each
(
funcs
,
function
(
f
)
{
obj
[
f
]
=
_
.
bind
(
obj
[
f
],
obj
);
});
var
i
,
length
=
arguments
.
length
,
key
;
if
(
length
<=
1
)
throw
new
Error
(
'
bindAll must be passed function names
'
);
for
(
i
=
1
;
i
<
length
;
i
++
)
{
key
=
arguments
[
i
];
obj
[
key
]
=
_
.
bind
(
obj
[
key
],
obj
);
}
return
obj
;
};
// Memoize an expensive function by storing its results.
_
.
memoize
=
function
(
func
,
hasher
)
{
var
memo
=
{};
hasher
||
(
hasher
=
_
.
identity
)
;
return
function
()
{
var
key
=
hasher
.
apply
(
this
,
arguments
);
return
_
.
has
(
memo
,
key
)
?
memo
[
key
]
:
(
memo
[
key
]
=
func
.
apply
(
this
,
arguments
))
;
var
memo
ize
=
function
(
key
)
{
var
cache
=
memoize
.
cache
;
var
address
=
hasher
?
hasher
.
apply
(
this
,
arguments
)
:
key
;
if
(
!
_
.
has
(
cache
,
address
))
cache
[
address
]
=
func
.
apply
(
this
,
arguments
);
return
cache
[
address
]
;
};
memoize
.
cache
=
{};
return
memoize
;
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_
.
delay
=
function
(
func
,
wait
)
{
var
args
=
slice
.
call
(
arguments
,
2
);
return
setTimeout
(
function
(){
return
func
.
apply
(
null
,
args
);
},
wait
);
return
setTimeout
(
function
(){
return
func
.
apply
(
null
,
args
);
},
wait
);
};
// Defers a function, scheduling it to run after the current call stack has
...
...
@@ -627,26 +717,34 @@
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_
.
throttle
=
function
(
func
,
wait
)
{
var
context
,
args
,
timeout
,
result
;
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_
.
throttle
=
function
(
func
,
wait
,
options
)
{
var
context
,
args
,
result
;
var
timeout
=
null
;
var
previous
=
0
;
if
(
!
options
)
options
=
{};
var
later
=
function
()
{
previous
=
new
Date
;
previous
=
options
.
leading
===
false
?
0
:
_
.
now
()
;
timeout
=
null
;
result
=
func
.
apply
(
context
,
args
);
if
(
!
timeout
)
context
=
args
=
null
;
};
return
function
()
{
var
now
=
new
Date
;
var
now
=
_
.
now
();
if
(
!
previous
&&
options
.
leading
===
false
)
previous
=
now
;
var
remaining
=
wait
-
(
now
-
previous
);
context
=
this
;
args
=
arguments
;
if
(
remaining
<=
0
)
{
if
(
remaining
<=
0
||
remaining
>
wait
)
{
clearTimeout
(
timeout
);
timeout
=
null
;
previous
=
now
;
result
=
func
.
apply
(
context
,
args
);
}
else
if
(
!
timeout
)
{
if
(
!
timeout
)
context
=
args
=
null
;
}
else
if
(
!
timeout
&&
options
.
trailing
!==
false
)
{
timeout
=
setTimeout
(
later
,
remaining
);
}
return
result
;
...
...
@@ -658,31 +756,34 @@
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_
.
debounce
=
function
(
func
,
wait
,
immediate
)
{
var
timeout
,
result
;
return
function
()
{
var
context
=
this
,
args
=
arguments
;
var
timeout
,
args
,
context
,
timestamp
,
result
;
var
later
=
function
()
{
var
last
=
_
.
now
()
-
timestamp
;
if
(
last
<
wait
&&
last
>
0
)
{
timeout
=
setTimeout
(
later
,
wait
-
last
);
}
else
{
timeout
=
null
;
if
(
!
immediate
)
result
=
func
.
apply
(
context
,
args
);
};
var
callNow
=
immediate
&&
!
timeout
;
clearTimeout
(
timeout
);
timeout
=
setTimeout
(
later
,
wait
);
if
(
callNow
)
result
=
func
.
apply
(
context
,
args
);
return
result
;
};
if
(
!
immediate
)
{
result
=
func
.
apply
(
context
,
args
);
if
(
!
timeout
)
context
=
args
=
null
;
}
}
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_
.
once
=
function
(
func
)
{
var
ran
=
false
,
memo
;
return
function
()
{
if
(
ran
)
return
memo
;
ran
=
true
;
memo
=
func
.
apply
(
this
,
arguments
);
func
=
null
;
return
memo
;
context
=
this
;
args
=
arguments
;
timestamp
=
_
.
now
();
var
callNow
=
immediate
&&
!
timeout
;
if
(
!
timeout
)
timeout
=
setTimeout
(
later
,
wait
);
if
(
callNow
)
{
result
=
func
.
apply
(
context
,
args
);
context
=
args
=
null
;
}
return
result
;
};
};
...
...
@@ -690,29 +791,31 @@
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_
.
wrap
=
function
(
func
,
wrapper
)
{
return
_
.
partial
(
wrapper
,
func
);
};
// Returns a negated version of the passed-in predicate.
_
.
negate
=
function
(
predicate
)
{
return
function
()
{
var
args
=
[
func
];
push
.
apply
(
args
,
arguments
);
return
wrapper
.
apply
(
this
,
args
);
return
!
predicate
.
apply
(
this
,
arguments
);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_
.
compose
=
function
()
{
var
funcs
=
arguments
;
return
function
()
{
var
args
=
arguments
;
for
(
var
i
=
funcs
.
length
-
1
;
i
>=
0
;
i
--
)
{
args
=
[
funcs
[
i
].
apply
(
this
,
args
)];
}
return
args
[
0
];
var
start
=
args
.
length
-
1
;
return
function
()
{
var
i
=
start
;
var
result
=
args
[
start
].
apply
(
this
,
arguments
);
while
(
i
--
)
result
=
args
[
i
].
call
(
this
,
result
);
return
result
;
};
};
// Returns a function that will only be executed after being called N times.
_
.
after
=
function
(
times
,
func
)
{
if
(
times
<=
0
)
return
func
();
return
function
()
{
if
(
--
times
<
1
)
{
return
func
.
apply
(
this
,
arguments
);
...
...
@@ -720,36 +823,65 @@
};
};
// Returns a function that will only be executed before being called N times.
_
.
before
=
function
(
times
,
func
)
{
var
memo
;
return
function
()
{
if
(
--
times
>
0
)
{
memo
=
func
.
apply
(
this
,
arguments
);
}
else
{
func
=
null
;
}
return
memo
;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_
.
once
=
_
.
partial
(
_
.
before
,
2
);
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_
.
keys
=
nativeKeys
||
function
(
obj
)
{
if
(
obj
!==
Object
(
obj
))
throw
new
TypeError
(
'
Invalid object
'
);
_
.
keys
=
function
(
obj
)
{
if
(
!
_
.
isObject
(
obj
))
return
[];
if
(
nativeKeys
)
return
nativeKeys
(
obj
);
var
keys
=
[];
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
keys
[
keys
.
length
]
=
key
;
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
keys
.
push
(
key
)
;
return
keys
;
};
// Retrieve the values of an object's properties.
_
.
values
=
function
(
obj
)
{
var
values
=
[];
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
values
.
push
(
obj
[
key
]);
var
keys
=
_
.
keys
(
obj
);
var
length
=
keys
.
length
;
var
values
=
Array
(
length
);
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
values
[
i
]
=
obj
[
keys
[
i
]];
}
return
values
;
};
// Convert an object into a list of `[key, value]` pairs.
_
.
pairs
=
function
(
obj
)
{
var
pairs
=
[];
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
pairs
.
push
([
key
,
obj
[
key
]]);
var
keys
=
_
.
keys
(
obj
);
var
length
=
keys
.
length
;
var
pairs
=
Array
(
length
);
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
pairs
[
i
]
=
[
keys
[
i
],
obj
[
keys
[
i
]]];
}
return
pairs
;
};
// Invert the keys and values of an object. The values must be serializable.
_
.
invert
=
function
(
obj
)
{
var
result
=
{};
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
result
[
obj
[
key
]]
=
key
;
var
keys
=
_
.
keys
(
obj
);
for
(
var
i
=
0
,
length
=
keys
.
length
;
i
<
length
;
i
++
)
{
result
[
obj
[
keys
[
i
]]]
=
keys
[
i
];
}
return
result
;
};
...
...
@@ -765,45 +897,62 @@
// Extend a given object with all the properties in passed-in object(s).
_
.
extend
=
function
(
obj
)
{
each
(
slice
.
call
(
arguments
,
1
),
function
(
source
)
{
if
(
source
)
{
for
(
var
prop
in
source
)
{
if
(
!
_
.
isObject
(
obj
))
return
obj
;
var
source
,
prop
;
for
(
var
i
=
1
,
length
=
arguments
.
length
;
i
<
length
;
i
++
)
{
source
=
arguments
[
i
];
for
(
prop
in
source
)
{
if
(
hasOwnProperty
.
call
(
source
,
prop
))
{
obj
[
prop
]
=
source
[
prop
];
}
}
}
);
}
return
obj
;
};
// Return a copy of the object only containing the whitelisted properties.
_
.
pick
=
function
(
obj
)
{
var
copy
=
{};
var
keys
=
concat
.
apply
(
ArrayProto
,
slice
.
call
(
arguments
,
1
));
each
(
keys
,
function
(
key
)
{
if
(
key
in
obj
)
copy
[
key
]
=
obj
[
key
];
});
return
copy
;
_
.
pick
=
function
(
obj
,
iteratee
,
context
)
{
var
result
=
{},
key
;
if
(
obj
==
null
)
return
result
;
if
(
_
.
isFunction
(
iteratee
))
{
iteratee
=
createCallback
(
iteratee
,
context
);
for
(
key
in
obj
)
{
var
value
=
obj
[
key
];
if
(
iteratee
(
value
,
key
,
obj
))
result
[
key
]
=
value
;
}
}
else
{
var
keys
=
concat
.
apply
([],
slice
.
call
(
arguments
,
1
));
obj
=
new
Object
(
obj
);
for
(
var
i
=
0
,
length
=
keys
.
length
;
i
<
length
;
i
++
)
{
key
=
keys
[
i
];
if
(
key
in
obj
)
result
[
key
]
=
obj
[
key
];
}
}
return
result
;
};
// Return a copy of the object without the blacklisted properties.
_
.
omit
=
function
(
obj
)
{
var
copy
=
{};
var
keys
=
concat
.
apply
(
ArrayProto
,
slice
.
call
(
arguments
,
1
));
for
(
var
key
in
obj
)
{
if
(
!
_
.
contains
(
keys
,
key
))
copy
[
key
]
=
obj
[
key
];
_
.
omit
=
function
(
obj
,
iteratee
,
context
)
{
if
(
_
.
isFunction
(
iteratee
))
{
iteratee
=
_
.
negate
(
iteratee
);
}
else
{
var
keys
=
_
.
map
(
concat
.
apply
([],
slice
.
call
(
arguments
,
1
)),
String
);
iteratee
=
function
(
value
,
key
)
{
return
!
_
.
contains
(
keys
,
key
);
};
}
return
copy
;
return
_
.
pick
(
obj
,
iteratee
,
context
)
;
};
// Fill in a given object with default properties.
_
.
defaults
=
function
(
obj
)
{
each
(
slice
.
call
(
arguments
,
1
),
function
(
source
)
{
if
(
source
)
{
if
(
!
_
.
isObject
(
obj
))
return
obj
;
for
(
var
i
=
1
,
length
=
arguments
.
length
;
i
<
length
;
i
++
)
{
var
source
=
arguments
[
i
];
for
(
var
prop
in
source
)
{
if
(
obj
[
prop
]
==
null
)
obj
[
prop
]
=
source
[
prop
];
if
(
obj
[
prop
]
===
void
0
)
obj
[
prop
]
=
source
[
prop
];
}
}
});
return
obj
;
};
...
...
@@ -824,8 +973,8 @@
// Internal recursive comparison function for `isEqual`.
var
eq
=
function
(
a
,
b
,
aStack
,
bStack
)
{
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the
Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal
.
if
(
a
===
b
)
return
a
!==
0
||
1
/
a
==
1
/
b
;
// See the
[Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal)
.
if
(
a
===
b
)
return
a
!==
0
||
1
/
a
==
=
1
/
b
;
// A strict comparison is necessary because `null == undefined`.
if
(
a
==
null
||
b
==
null
)
return
a
===
b
;
// Unwrap any wrapped objects.
...
...
@@ -833,29 +982,27 @@
if
(
b
instanceof
_
)
b
=
b
.
_wrapped
;
// Compare `[[Class]]` names.
var
className
=
toString
.
call
(
a
);
if
(
className
!=
toString
.
call
(
b
))
return
false
;
if
(
className
!=
=
toString
.
call
(
b
))
return
false
;
switch
(
className
)
{
// Strings, numbers, dates, and booleans are compared by value.
// Strings, numbers, regular expressions, dates, and booleans are compared by value.
case
'
[object RegExp]
'
:
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
case
'
[object String]
'
:
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return
a
==
String
(
b
)
;
return
''
+
a
===
''
+
b
;
case
'
[object Number]
'
:
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return
a
!=
+
a
?
b
!=
+
b
:
(
a
==
0
?
1
/
a
==
1
/
b
:
a
==
+
b
);
// `NaN`s are equivalent, but non-reflexive.
// Object(NaN) is equivalent to NaN
if
(
+
a
!==
+
a
)
return
+
b
!==
+
b
;
// An `egal` comparison is performed for other numeric values.
return
+
a
===
0
?
1
/
+
a
===
1
/
b
:
+
a
===
+
b
;
case
'
[object Date]
'
:
case
'
[object Boolean]
'
:
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return
+
a
==
+
b
;
// RegExps are compared by their source patterns and flags.
case
'
[object RegExp]
'
:
return
a
.
source
==
b
.
source
&&
a
.
global
==
b
.
global
&&
a
.
multiline
==
b
.
multiline
&&
a
.
ignoreCase
==
b
.
ignoreCase
;
return
+
a
===
+
b
;
}
if
(
typeof
a
!=
'
object
'
||
typeof
b
!=
'
object
'
)
return
false
;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
...
...
@@ -864,17 +1011,29 @@
while
(
length
--
)
{
// Linear search. Performance is inversely proportional to the number of
// 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.
aStack
.
push
(
a
);
bStack
.
push
(
b
);
var
size
=
0
,
result
=
true
;
var
size
,
result
;
// Recursively compare objects and arrays.
if
(
className
==
'
[object Array]
'
)
{
if
(
className
==
=
'
[object Array]
'
)
{
// Compare array lengths to determine if a deep comparison is necessary.
size
=
a
.
length
;
result
=
size
==
b
.
length
;
result
=
size
==
=
b
.
length
;
if
(
result
)
{
// Deep compare the contents, ignoring non-numeric properties.
while
(
size
--
)
{
...
...
@@ -882,28 +1041,17 @@
}
}
}
else
{
// Objects with different constructors are not equivalent, but `Object`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
)))
{
return
false
;
}
// Deep compare objects.
for
(
var
key
in
a
)
{
if
(
_
.
has
(
a
,
key
))
{
// Count the expected number of properties.
size
++
;
// Deep compare each member.
if
(
!
(
result
=
_
.
has
(
b
,
key
)
&&
eq
(
a
[
key
],
b
[
key
],
aStack
,
bStack
)))
break
;
}
}
// Ensure that both objects contain the same number of properties.
var
keys
=
_
.
keys
(
a
),
key
;
size
=
keys
.
length
;
// Ensure that both objects contain the same number of properties before comparing deep equality.
result
=
_
.
keys
(
b
).
length
===
size
;
if
(
result
)
{
for
(
key
in
b
)
{
if
(
_
.
has
(
b
,
key
)
&&
!
(
size
--
))
break
;
while
(
size
--
)
{
// Deep compare each member
key
=
keys
[
size
];
if
(
!
(
result
=
_
.
has
(
b
,
key
)
&&
eq
(
a
[
key
],
b
[
key
],
aStack
,
bStack
)))
break
;
}
result
=
!
size
;
}
}
// Remove the first object from the stack of traversed objects.
...
...
@@ -921,7 +1069,7 @@
// An "empty" object has no enumerable own-properties.
_
.
isEmpty
=
function
(
obj
)
{
if
(
obj
==
null
)
return
true
;
if
(
_
.
isArray
(
obj
)
||
_
.
isString
(
obj
))
return
obj
.
length
===
0
;
if
(
_
.
isArray
(
obj
)
||
_
.
isString
(
obj
)
||
_
.
isArguments
(
obj
)
)
return
obj
.
length
===
0
;
for
(
var
key
in
obj
)
if
(
_
.
has
(
obj
,
key
))
return
false
;
return
true
;
};
...
...
@@ -934,18 +1082,19 @@
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_
.
isArray
=
nativeIsArray
||
function
(
obj
)
{
return
toString
.
call
(
obj
)
==
'
[object Array]
'
;
return
toString
.
call
(
obj
)
==
=
'
[object Array]
'
;
};
// Is a given variable an object?
_
.
isObject
=
function
(
obj
)
{
return
obj
===
Object
(
obj
);
var
type
=
typeof
obj
;
return
type
===
'
function
'
||
type
===
'
object
'
&&
!!
obj
;
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each
([
'
Arguments
'
,
'
Function
'
,
'
String
'
,
'
Number
'
,
'
Date
'
,
'
RegExp
'
],
function
(
name
)
{
_
.
each
([
'
Arguments
'
,
'
Function
'
,
'
String
'
,
'
Number
'
,
'
Date
'
,
'
RegExp
'
],
function
(
name
)
{
_
[
'
is
'
+
name
]
=
function
(
obj
)
{
return
toString
.
call
(
obj
)
==
'
[object
'
+
name
+
'
]
'
;
return
toString
.
call
(
obj
)
==
=
'
[object
'
+
name
+
'
]
'
;
};
});
...
...
@@ -953,14 +1102,14 @@
// there isn't any inspectable "Arguments" type.
if
(
!
_
.
isArguments
(
arguments
))
{
_
.
isArguments
=
function
(
obj
)
{
return
!!
(
obj
&&
_
.
has
(
obj
,
'
callee
'
)
);
return
_
.
has
(
obj
,
'
callee
'
);
};
}
// Optimize `isFunction` if appropriate.
if
(
typeof
(
/./
)
!==
'
function
'
)
{
// Optimize `isFunction` if appropriate.
Work around an IE 11 bug.
if
(
typeof
/./
!==
'
function
'
)
{
_
.
isFunction
=
function
(
obj
)
{
return
typeof
obj
==
=
'
function
'
;
return
typeof
obj
==
'
function
'
||
false
;
};
}
...
...
@@ -971,12 +1120,12 @@
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_
.
isNaN
=
function
(
obj
)
{
return
_
.
isNumber
(
obj
)
&&
obj
!=
+
obj
;
return
_
.
isNumber
(
obj
)
&&
obj
!=
=
+
obj
;
};
// Is a given value a boolean?
_
.
isBoolean
=
function
(
obj
)
{
return
obj
===
true
||
obj
===
false
||
toString
.
call
(
obj
)
==
'
[object Boolean]
'
;
return
obj
===
true
||
obj
===
false
||
toString
.
call
(
obj
)
==
=
'
[object Boolean]
'
;
};
// Is a given value equal to null?
...
...
@@ -992,7 +1141,7 @@
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_
.
has
=
function
(
obj
,
key
)
{
return
hasOwnProperty
.
call
(
obj
,
key
);
return
obj
!=
null
&&
hasOwnProperty
.
call
(
obj
,
key
);
};
// Utility Functions
...
...
@@ -1005,15 +1154,44 @@
return
this
;
};
// Keep the identity function around for default iterat
or
s.
// Keep the identity function around for default iterat
ee
s.
_
.
identity
=
function
(
value
)
{
return
value
;
};
_
.
constant
=
function
(
value
)
{
return
function
()
{
return
value
;
};
};
_
.
noop
=
function
(){};
_
.
property
=
function
(
key
)
{
return
function
(
obj
)
{
return
obj
[
key
];
};
};
// Returns a predicate for checking whether an object has a given set of `key:value` pairs.
_
.
matches
=
function
(
attrs
)
{
var
pairs
=
_
.
pairs
(
attrs
),
length
=
pairs
.
length
;
return
function
(
obj
)
{
if
(
obj
==
null
)
return
!
length
;
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.
_
.
times
=
function
(
n
,
iterator
,
context
)
{
var
accum
=
Array
(
n
);
for
(
var
i
=
0
;
i
<
n
;
i
++
)
accum
[
i
]
=
iterator
.
call
(
context
,
i
);
_
.
times
=
function
(
n
,
iteratee
,
context
)
{
var
accum
=
Array
(
Math
.
max
(
0
,
n
));
iteratee
=
createCallback
(
iteratee
,
context
,
1
);
for
(
var
i
=
0
;
i
<
n
;
i
++
)
accum
[
i
]
=
iteratee
(
i
);
return
accum
;
};
...
...
@@ -1026,53 +1204,45 @@
return
min
+
Math
.
floor
(
Math
.
random
()
*
(
max
-
min
+
1
));
};
// A (possibly faster) way to get the current timestamp as an integer.
_
.
now
=
Date
.
now
||
function
()
{
return
new
Date
().
getTime
();
};
// List of HTML entities for escaping.
var
entityMap
=
{
escape
:
{
var
escapeMap
=
{
'
&
'
:
'
&
'
,
'
<
'
:
'
<
'
,
'
>
'
:
'
>
'
,
'
"
'
:
'
"
'
,
"
'
"
:
'
'
'
,
'
/
'
:
'
/
'
}
};
entityMap
.
unescape
=
_
.
invert
(
entityMap
.
escape
);
// Regexes containing the keys and values listed immediately above.
var
entityRegexes
=
{
escape
:
new
RegExp
(
'
[
'
+
_
.
keys
(
entityMap
.
escape
).
join
(
''
)
+
'
]
'
,
'
g
'
),
unescape
:
new
RegExp
(
'
(
'
+
_
.
keys
(
entityMap
.
unescape
).
join
(
'
|
'
)
+
'
)
'
,
'
g
'
)
'
`
'
:
'
`
'
};
var
unescapeMap
=
_
.
invert
(
escapeMap
);
// Functions for escaping and unescaping strings to/from HTML interpolation.
_
.
each
([
'
escape
'
,
'
unescape
'
],
function
(
method
)
{
_
[
method
]
=
function
(
string
)
{
if
(
string
==
null
)
return
''
;
return
(
''
+
string
).
replace
(
entityRegexes
[
method
],
function
(
match
)
{
return
entityMap
[
method
][
match
];
});
var
createEscaper
=
function
(
map
)
{
var
escaper
=
function
(
match
)
{
return
map
[
match
];
};
});
// Regexes for identifying a key that needs to be escaped
var
source
=
'
(?:
'
+
_
.
keys
(
map
).
join
(
'
|
'
)
+
'
)
'
;
var
testRegexp
=
RegExp
(
source
);
var
replaceRegexp
=
RegExp
(
source
,
'
g
'
);
return
function
(
string
)
{
string
=
string
==
null
?
''
:
''
+
string
;
return
testRegexp
.
test
(
string
)
?
string
.
replace
(
replaceRegexp
,
escaper
)
:
string
;
};
};
_
.
escape
=
createEscaper
(
escapeMap
);
_
.
unescape
=
createEscaper
(
unescapeMap
);
// If the value of the named
property is a function then invoke it;
// otherwise, return it.
// If the value of the named
`property` is a function then invoke it with the
//
`object` as context;
otherwise, return it.
_
.
result
=
function
(
object
,
property
)
{
if
(
object
==
null
)
return
null
;
if
(
object
==
null
)
return
void
0
;
var
value
=
object
[
property
];
return
_
.
isFunction
(
value
)
?
value
.
call
(
object
)
:
value
;
};
// Add your own custom functions to the Underscore object.
_
.
mixin
=
function
(
obj
)
{
each
(
_
.
functions
(
obj
),
function
(
name
){
var
func
=
_
[
name
]
=
obj
[
name
];
_
.
prototype
[
name
]
=
function
()
{
var
args
=
[
this
.
_wrapped
];
push
.
apply
(
args
,
arguments
);
return
result
.
call
(
this
,
func
.
apply
(
_
,
args
));
};
});
return
_
.
isFunction
(
value
)
?
object
[
property
]()
:
value
;
};
// Generate a unique integer id (unique within the entire client session).
...
...
@@ -1103,22 +1273,26 @@
'
\\
'
:
'
\\
'
,
'
\r
'
:
'
r
'
,
'
\n
'
:
'
n
'
,
'
\t
'
:
'
t
'
,
'
\
u2028
'
:
'
u2028
'
,
'
\
u2029
'
:
'
u2029
'
};
var
escaper
=
/
\\
|'|
\r
|
\n
|
\t
|
\u
2028|
\u
2029/g
;
var
escaper
=
/
\\
|'|
\r
|
\n
|
\u
2028|
\u
2029/g
;
var
escapeChar
=
function
(
match
)
{
return
'
\\
'
+
escapes
[
match
];
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_
.
template
=
function
(
text
,
data
,
settings
)
{
var
render
;
// NB: `oldSettings` only exists for backwards compatibility.
_
.
template
=
function
(
text
,
settings
,
oldSettings
)
{
if
(
!
settings
&&
oldSettings
)
settings
=
oldSettings
;
settings
=
_
.
defaults
({},
settings
,
_
.
templateSettings
);
// Combine delimiters into one regular expression via alternation.
var
matcher
=
new
RegExp
([
var
matcher
=
RegExp
([
(
settings
.
escape
||
noMatch
).
source
,
(
settings
.
interpolate
||
noMatch
).
source
,
(
settings
.
evaluate
||
noMatch
).
source
...
...
@@ -1128,19 +1302,18 @@
var
index
=
0
;
var
source
=
"
__p+='
"
;
text
.
replace
(
matcher
,
function
(
match
,
escape
,
interpolate
,
evaluate
,
offset
)
{
source
+=
text
.
slice
(
index
,
offset
)
.
replace
(
escaper
,
function
(
match
)
{
return
'
\\
'
+
escapes
[
match
];
})
;
source
+=
text
.
slice
(
index
,
offset
)
.
replace
(
escaper
,
escapeChar
);
index
=
offset
+
match
.
length
;
if
(
escape
)
{
source
+=
"
'+
\n
((__t=(
"
+
escape
+
"
))==null?'':_.escape(__t))+
\n
'
"
;
}
if
(
interpolate
)
{
}
else
if
(
interpolate
)
{
source
+=
"
'+
\n
((__t=(
"
+
interpolate
+
"
))==null?'':__t)+
\n
'
"
;
}
if
(
evaluate
)
{
}
else
if
(
evaluate
)
{
source
+=
"
';
\n
"
+
evaluate
+
"
\n
__p+='
"
;
}
index
=
offset
+
match
.
length
;
// Adobe VMs need the match returned to produce the correct offest.
return
match
;
});
source
+=
"
';
\n
"
;
...
...
@@ -1150,29 +1323,31 @@
source
=
"
var __t,__p='',__j=Array.prototype.join,
"
+
"
print=function(){__p+=__j.call(arguments,'');};
\n
"
+
source
+
"
return __p;
\n
"
;
source
+
'
return __p;
\n
'
;
try
{
render
=
new
Function
(
settings
.
variable
||
'
obj
'
,
'
_
'
,
source
);
var
render
=
new
Function
(
settings
.
variable
||
'
obj
'
,
'
_
'
,
source
);
}
catch
(
e
)
{
e
.
source
=
source
;
throw
e
;
}
if
(
data
)
return
render
(
data
,
_
);
var
template
=
function
(
data
)
{
return
render
.
call
(
this
,
data
,
_
);
};
// Provide the compiled function source as a convenience for precompilation.
template
.
source
=
'
function(
'
+
(
settings
.
variable
||
'
obj
'
)
+
'
){
\n
'
+
source
+
'
}
'
;
// Provide the compiled source as a convenience for precompilation.
var
argument
=
settings
.
variable
||
'
obj
'
;
template
.
source
=
'
function(
'
+
argument
+
'
){
\n
'
+
source
+
'
}
'
;
return
template
;
};
// Add a "chain" function
, which will delegate to the wrapper
.
// Add a "chain" function
. Start chaining a wrapped Underscore object
.
_
.
chain
=
function
(
obj
)
{
return
_
(
obj
).
chain
();
var
instance
=
_
(
obj
);
instance
.
_chain
=
true
;
return
instance
;
};
// OOP
...
...
@@ -1186,41 +1361,55 @@
return
this
.
_chain
?
_
(
obj
).
chain
()
:
obj
;
};
// Add your own custom functions to the Underscore object.
_
.
mixin
=
function
(
obj
)
{
_
.
each
(
_
.
functions
(
obj
),
function
(
name
)
{
var
func
=
_
[
name
]
=
obj
[
name
];
_
.
prototype
[
name
]
=
function
()
{
var
args
=
[
this
.
_wrapped
];
push
.
apply
(
args
,
arguments
);
return
result
.
call
(
this
,
func
.
apply
(
_
,
args
));
};
});
};
// Add all of the Underscore functions to the wrapper object.
_
.
mixin
(
_
);
// Add all mutator Array functions to the wrapper.
each
([
'
pop
'
,
'
push
'
,
'
reverse
'
,
'
shift
'
,
'
sort
'
,
'
splice
'
,
'
unshift
'
],
function
(
name
)
{
_
.
each
([
'
pop
'
,
'
push
'
,
'
reverse
'
,
'
shift
'
,
'
sort
'
,
'
splice
'
,
'
unshift
'
],
function
(
name
)
{
var
method
=
ArrayProto
[
name
];
_
.
prototype
[
name
]
=
function
()
{
var
obj
=
this
.
_wrapped
;
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
);
};
});
// Add all accessor Array functions to the wrapper.
each
([
'
concat
'
,
'
join
'
,
'
slice
'
],
function
(
name
)
{
_
.
each
([
'
concat
'
,
'
join
'
,
'
slice
'
],
function
(
name
)
{
var
method
=
ArrayProto
[
name
];
_
.
prototype
[
name
]
=
function
()
{
return
result
.
call
(
this
,
method
.
apply
(
this
.
_wrapped
,
arguments
));
};
});
_
.
extend
(
_
.
prototype
,
{
// Start chaining a wrapped Underscore object.
chain
:
function
()
{
this
.
_chain
=
true
;
return
this
;
},
// Extracts the result from a wrapped and chained object.
value
:
function
()
{
_
.
prototype
.
value
=
function
()
{
return
this
.
_wrapped
;
}
};
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
define
(
'
underscore
'
,
[],
function
()
{
return
_
;
});
}
).
call
(
this
);
}
}
.
call
(
this
)
);
examples/backbone_marionette/package.json
0 → 100644
View file @
25e31ff1
{
"private"
:
true
,
"dependencies"
:
{
"backbone"
:
"^1.1.2"
,
"backbone.localstorage"
:
"^1.1.6"
,
"backbone.marionette"
:
"^2.3.2"
,
"jquery"
:
"^1.11.2"
,
"todomvc-app-css"
:
"^1.0.1"
,
"todomvc-common"
:
"^1.0.1"
,
"underscore"
:
"^1.6.0"
}
}
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