Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
todomvc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Eugene Shen
todomvc
Commits
28b669e4
Commit
28b669e4
authored
Mar 16, 2015
by
Sam Saccone
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1164 from yavorsky/master
Update exoskeleton to 0.7.0
parents
f90608ba
8e9e5c77
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
902 additions
and
806 deletions
+902
-806
examples/exoskeleton/.gitignore
examples/exoskeleton/.gitignore
+3
-0
examples/exoskeleton/index.html
examples/exoskeleton/index.html
+2
-0
examples/exoskeleton/js/views/app-view.js
examples/exoskeleton/js/views/app-view.js
+13
-7
examples/exoskeleton/js/views/todo-view.js
examples/exoskeleton/js/views/todo-view.js
+1
-1
examples/exoskeleton/node_modules/backbone.nativeview/backbone.nativeview.js
...n/node_modules/backbone.nativeview/backbone.nativeview.js
+169
-0
examples/exoskeleton/node_modules/exoskeleton/exoskeleton.js
examples/exoskeleton/node_modules/exoskeleton/exoskeleton.js
+712
-797
examples/exoskeleton/package.json
examples/exoskeleton/package.json
+2
-1
No files found.
examples/exoskeleton/.gitignore
View file @
28b669e4
...
@@ -4,6 +4,9 @@ node_modules/exoskeleton/*
...
@@ -4,6 +4,9 @@ node_modules/exoskeleton/*
node_modules/backbone.localstorage/*
node_modules/backbone.localstorage/*
!node_modules/backbone.localstorage/backbone.localStorage.js
!node_modules/backbone.localstorage/backbone.localStorage.js
node_modules/backbone.nativeview/*
!node_modules/backbone.nativeview/backbone.nativeview.js
node_modules/todomvc-app-css/*
node_modules/todomvc-app-css/*
!node_modules/todomvc-app-css/index.css
!node_modules/todomvc-app-css/index.css
...
...
examples/exoskeleton/index.html
View file @
28b669e4
...
@@ -53,6 +53,8 @@
...
@@ -53,6 +53,8 @@
<script
src=
"node_modules/exoskeleton/exoskeleton.js"
></script>
<script
src=
"node_modules/exoskeleton/exoskeleton.js"
></script>
<script
src=
"node_modules/microtemplates/index.js"
></script>
<script
src=
"node_modules/microtemplates/index.js"
></script>
<script
src=
"node_modules/backbone.localstorage/backbone.localStorage.js"
></script>
<script
src=
"node_modules/backbone.localstorage/backbone.localStorage.js"
></script>
<script
src=
"node_modules/backbone.nativeview/backbone.nativeview.js"
></script>
<script>
Backbone
.
View
=
Backbone
.
NativeView
;
</script>
<script
src=
"js/models/todo.js"
></script>
<script
src=
"js/models/todo.js"
></script>
<script
src=
"js/collections/todos.js"
></script>
<script
src=
"js/collections/todos.js"
></script>
<script
src=
"js/views/todo-view.js"
></script>
<script
src=
"js/views/todo-view.js"
></script>
...
...
examples/exoskeleton/js/views/app-view.js
View file @
28b669e4
...
@@ -8,6 +8,12 @@ var app = app || {};
...
@@ -8,6 +8,12 @@ var app = app || {};
el
.
style
.
display
=
toggle
?
''
:
'
none
'
;
el
.
style
.
display
=
toggle
?
''
:
'
none
'
;
};
};
var
matchesSelector
=
function
(
node
,
selector
)
{
return
[].
some
.
call
(
document
.
querySelectorAll
(
selector
),
function
(
el
)
{
return
el
===
node
;
});
};
// The Application
// The Application
// ---------------
// ---------------
...
@@ -32,10 +38,10 @@ var app = app || {};
...
@@ -32,10 +38,10 @@ var app = app || {};
// collection, when items are added or changed. Kick things off by
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
// loading any preexisting todos that might be saved in *localStorage*.
initialize
:
function
()
{
initialize
:
function
()
{
this
.
allCheckbox
=
this
.
find
(
'
#toggle-all
'
);
this
.
allCheckbox
=
this
.
$
(
'
#toggle-all
'
).
item
(
0
);
this
.
input
=
this
.
find
(
'
#new-todo
'
);
this
.
input
=
this
.
$
(
'
#new-todo
'
).
item
(
0
);
this
.
footer
=
this
.
find
(
'
#footer
'
);
this
.
footer
=
this
.
$
(
'
#footer
'
).
item
(
0
);
this
.
main
=
this
.
find
(
'
#main
'
);
this
.
main
=
this
.
$
(
'
#main
'
).
item
(
0
);
this
.
listenTo
(
app
.
todos
,
'
add
'
,
this
.
addOne
);
this
.
listenTo
(
app
.
todos
,
'
add
'
,
this
.
addOne
);
this
.
listenTo
(
app
.
todos
,
'
reset
'
,
this
.
addAll
);
this
.
listenTo
(
app
.
todos
,
'
reset
'
,
this
.
addAll
);
...
@@ -66,9 +72,9 @@ var app = app || {};
...
@@ -66,9 +72,9 @@ var app = app || {};
remaining
:
remaining
remaining
:
remaining
});
});
this
.
findAll
(
'
#filters li a
'
).
forEach
(
function
(
el
)
{
[].
forEach
.
call
(
this
.
$
(
'
#filters li a
'
),
function
(
el
)
{
el
.
classList
.
remove
(
'
selected
'
);
el
.
classList
.
remove
(
'
selected
'
);
if
(
Backbone
.
utils
.
matchesSelector
(
el
,
selector
))
{
if
(
matchesSelector
(
el
,
selector
))
{
el
.
classList
.
add
(
'
selected
'
);
el
.
classList
.
add
(
'
selected
'
);
}
}
});
});
...
@@ -90,7 +96,7 @@ var app = app || {};
...
@@ -90,7 +96,7 @@ var app = app || {};
// Add all items in the **Todos** collection at once.
// Add all items in the **Todos** collection at once.
addAll
:
function
()
{
addAll
:
function
()
{
this
.
find
(
'
#todo-list
'
).
innerHTML
=
''
;
this
.
$
(
'
#todo-list
'
).
item
(
0
).
innerHTML
=
''
;
app
.
todos
.
forEach
(
this
.
addOne
,
this
);
app
.
todos
.
forEach
(
this
.
addOne
,
this
);
},
},
...
...
examples/exoskeleton/js/views/todo-view.js
View file @
28b669e4
...
@@ -41,7 +41,7 @@ var app = app || {};
...
@@ -41,7 +41,7 @@ var app = app || {};
var
method
=
this
.
model
.
get
(
'
completed
'
)
?
'
add
'
:
'
remove
'
;
var
method
=
this
.
model
.
get
(
'
completed
'
)
?
'
add
'
:
'
remove
'
;
this
.
el
.
classList
[
method
](
'
completed
'
);
this
.
el
.
classList
[
method
](
'
completed
'
);
this
.
toggleVisible
();
this
.
toggleVisible
();
this
.
input
=
this
.
find
(
'
.edit
'
);
this
.
input
=
this
.
$
(
'
.edit
'
).
item
(
0
);
return
this
;
return
this
;
},
},
...
...
examples/exoskeleton/node_modules/backbone.nativeview/backbone.nativeview.js
0 → 100644
View file @
28b669e4
// Backbone.NativeView.js 0.3.2
// ---------------
// (c) 2014 Adam Krebs, Jimmy Yuen Ho Wong
// Backbone.NativeView may be freely distributed under the MIT license.
// For all details and documentation:
// https://github.com/akre54/Backbone.NativeView
(
function
(
factory
)
{
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
define
([
'
backbone
'
],
factory
);
}
else
if
(
typeof
exports
===
'
object
'
)
{
factory
(
require
(
'
backbone
'
));
}
else
{
factory
(
Backbone
);
}
}(
function
(
Backbone
)
{
// Cached regex to match an opening '<' of an HTML tag, possibly left-padded
// with whitespace.
var
paddedLt
=
/^
\s
*</
;
// Caches a local reference to `Element.prototype` for faster access.
var
ElementProto
=
(
typeof
Element
!==
'
undefined
'
&&
Element
.
prototype
)
||
{};
// Cross-browser event listener shims
var
elementAddEventListener
=
ElementProto
.
addEventListener
||
function
(
eventName
,
listener
)
{
return
this
.
attachEvent
(
'
on
'
+
eventName
,
listener
);
}
var
elementRemoveEventListener
=
ElementProto
.
removeEventListener
||
function
(
eventName
,
listener
)
{
return
this
.
detachEvent
(
'
on
'
+
eventName
,
listener
);
}
var
indexOf
=
function
(
array
,
item
)
{
for
(
var
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
if
(
array
[
i
]
===
item
)
return
i
;
return
-
1
;
}
// Find the right `Element#matches` for IE>=9 and modern browsers.
var
matchesSelector
=
ElementProto
.
matches
||
ElementProto
.
webkitMatchesSelector
||
ElementProto
.
mozMatchesSelector
||
ElementProto
.
msMatchesSelector
||
ElementProto
.
oMatchesSelector
||
// Make our own `Element#matches` for IE8
function
(
selector
)
{
// Use querySelectorAll to find all elements matching the selector,
// then check if the given element is included in that list.
// Executing the query on the parentNode reduces the resulting nodeList,
// (document doesn't have a parentNode).
var
nodeList
=
(
this
.
parentNode
||
document
).
querySelectorAll
(
selector
)
||
[];
return
!!~
indexOf
(
nodeList
,
this
);
};
// Cache Backbone.View for later access in constructor
var
BBView
=
Backbone
.
View
;
// To extend an existing view to use native methods, extend the View prototype
// with the mixin: _.extend(MyView.prototype, Backbone.NativeViewMixin);
Backbone
.
NativeViewMixin
=
{
_domEvents
:
null
,
constructor
:
function
()
{
this
.
_domEvents
=
[];
return
BBView
.
apply
(
this
,
arguments
);
},
$
:
function
(
selector
)
{
return
this
.
el
.
querySelectorAll
(
selector
);
},
_removeElement
:
function
()
{
this
.
undelegateEvents
();
if
(
this
.
el
.
parentNode
)
this
.
el
.
parentNode
.
removeChild
(
this
.
el
);
},
// Apply the `element` to the view. `element` can be a CSS selector,
// a string of HTML, or an Element node.
_setElement
:
function
(
element
)
{
if
(
typeof
element
==
'
string
'
)
{
if
(
paddedLt
.
test
(
element
))
{
var
el
=
document
.
createElement
(
'
div
'
);
el
.
innerHTML
=
element
;
this
.
el
=
el
.
firstChild
;
}
else
{
this
.
el
=
document
.
querySelector
(
element
);
}
}
else
{
this
.
el
=
element
;
}
},
// Set a hash of attributes to the view's `el`. We use the "prop" version
// if available, falling back to `setAttribute` for the catch-all.
_setAttributes
:
function
(
attrs
)
{
for
(
var
attr
in
attrs
)
{
attr
in
this
.
el
?
this
.
el
[
attr
]
=
attrs
[
attr
]
:
this
.
el
.
setAttribute
(
attr
,
attrs
[
attr
]);
}
},
// Make a event delegation handler for the given `eventName` and `selector`
// and attach it to `this.el`.
// If selector is empty, the listener will be bound to `this.el`. If not, a
// new handler that will recursively traverse up the event target's DOM
// hierarchy looking for a node that matches the selector. If one is found,
// the event's `delegateTarget` property is set to it and the return the
// result of calling bound `listener` with the parameters given to the
// handler.
delegate
:
function
(
eventName
,
selector
,
listener
)
{
if
(
typeof
selector
===
'
function
'
)
{
listener
=
selector
;
selector
=
null
;
}
var
root
=
this
.
el
;
var
handler
=
selector
?
function
(
e
)
{
var
node
=
e
.
target
||
e
.
srcElement
;
for
(;
node
&&
node
!=
root
;
node
=
node
.
parentNode
)
{
if
(
matchesSelector
.
call
(
node
,
selector
))
{
e
.
delegateTarget
=
node
;
listener
(
e
);
}
}
}
:
listener
;
elementAddEventListener
.
call
(
this
.
el
,
eventName
,
handler
,
false
);
this
.
_domEvents
.
push
({
eventName
:
eventName
,
handler
:
handler
,
listener
:
listener
,
selector
:
selector
});
return
handler
;
},
// Remove a single delegated event. Either `eventName` or `selector` must
// be included, `selector` and `listener` are optional.
undelegate
:
function
(
eventName
,
selector
,
listener
)
{
if
(
typeof
selector
===
'
function
'
)
{
listener
=
selector
;
selector
=
null
;
}
if
(
this
.
el
)
{
var
handlers
=
this
.
_domEvents
.
slice
();
for
(
var
i
=
0
,
len
=
handlers
.
length
;
i
<
len
;
i
++
)
{
var
item
=
handlers
[
i
];
var
match
=
item
.
eventName
===
eventName
&&
(
listener
?
item
.
listener
===
listener
:
true
)
&&
(
selector
?
item
.
selector
===
selector
:
true
);
if
(
!
match
)
continue
;
elementRemoveEventListener
.
call
(
this
.
el
,
item
.
eventName
,
item
.
handler
,
false
);
this
.
_domEvents
.
splice
(
indexOf
(
handlers
,
item
),
1
);
}
}
return
this
;
},
// Remove all events created with `delegate` from `el`
undelegateEvents
:
function
()
{
if
(
this
.
el
)
{
for
(
var
i
=
0
,
len
=
this
.
_domEvents
.
length
;
i
<
len
;
i
++
)
{
var
item
=
this
.
_domEvents
[
i
];
elementRemoveEventListener
.
call
(
this
.
el
,
item
.
eventName
,
item
.
handler
,
false
);
};
this
.
_domEvents
.
length
=
0
;
}
return
this
;
}
};
Backbone
.
NativeView
=
Backbone
.
View
.
extend
(
Backbone
.
NativeViewMixin
);
return
Backbone
.
NativeView
;
}));
examples/exoskeleton/node_modules/exoskeleton/exoskeleton.js
View file @
28b669e4
/*!
/*!
* Exoskeleton.js 0.
3
.0
* Exoskeleton.js 0.
7
.0
* (c) 2013 Paul Miller <http://paulmillr.com>
* (c) 2013 Paul Miller <http://paulmillr.com>
* Based on Backbone.js
* Based on Backbone.js
* (c) 2010-2013 Jeremy Ashkenas, DocumentCloud
* (c) 2010-2013 Jeremy Ashkenas, DocumentCloud
* Exoskeleton may be freely distributed under the MIT license.
* Exoskeleton may be freely distributed under the MIT license.
* For all details and documentation: <http://exos
kel.at
>
* For all details and documentation: <http://exos
js.com
>
*/
*/
(
function
(
factory
)
{
(
function
(
root
,
factory
)
{
// Set up Backbone appropriately for the environment.
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
define
([
'
underscore
'
,
'
jquery
'
],
factory
);
define
([
'
underscore
'
,
'
jquery
'
,
'
exports
'
],
function
(
_
,
$
,
exports
)
{
}
else
if
(
typeof
exports
===
'
object
'
)
{
root
.
Backbone
=
root
.
Exoskeleton
=
factory
(
root
,
exports
,
_
,
$
);
factory
(
require
(
'
underscore
'
),
require
(
'
jquery
'
));
});
}
else
if
(
typeof
exports
!==
'
undefined
'
)
{
var
_
,
$
;
try
{
_
=
require
(
'
underscore
'
);
}
catch
(
e
)
{
}
try
{
$
=
require
(
'
jquery
'
);
}
catch
(
e
)
{
}
factory
(
root
,
exports
,
_
,
$
);
}
else
{
}
else
{
factory
(
this
.
_
,
this
.
jQuery
||
this
.
Zepto
||
this
.
ender
||
this
.
$
);
root
.
Backbone
=
root
.
Exoskeleton
=
factory
(
root
,
{},
root
.
_
,
(
root
.
jQuery
||
root
.
Zepto
||
root
.
ender
||
root
.
$
)
);
}
}
})(
function
(
_
,
$
)
{
})(
this
,
function
(
root
,
Backbone
,
_
,
$
)
{
'
use strict
'
;
'
use strict
'
;
// Initial Setup
// Initial Setup
// -------------
// -------------
// Save a reference to the global object (`window` in the browser, `exports`
// on the server).
var
root
=
(
typeof
window
===
'
undefined
'
)
?
exports
:
window
;
// Save the previous value of the `Backbone` variable, so that it can be
// Save the previous value of the `Backbone` variable, so that it can be
// restored later on, if `noConflict` is used.
// restored later on, if `noConflict` is used.
var
previousBackbone
=
root
.
Backbone
;
var
previousBackbone
=
root
.
Backbone
;
var
previousExoskeleton
=
root
.
Exoskeleton
;
// 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
=
{};
}
// Underscore replacement.
// Underscore replacement.
var
utils
=
_
=
Backbone
.
utils
=
_
||
{}
;
var
utils
=
Backbone
.
utils
=
_
=
(
_
||
{})
;
// Hold onto a local reference to `$`. Can be changed at any point.
// Hold onto a local reference to `$`. Can be changed at any point.
Backbone
.
$
=
$
;
Backbone
.
$
=
$
;
...
@@ -48,7 +43,7 @@
...
@@ -48,7 +43,7 @@
var
array
=
[];
var
array
=
[];
var
push
=
array
.
push
;
var
push
=
array
.
push
;
var
slice
=
array
.
slice
;
var
slice
=
array
.
slice
;
var
splice
=
array
.
splice
;
var
toString
=
({}).
toString
;
// Current version of the library. Keep in sync with `package.json`.
// Current version of the library. Keep in sync with `package.json`.
// Backbone.VERSION = '1.0.0';
// Backbone.VERSION = '1.0.0';
...
@@ -57,20 +52,10 @@
...
@@ -57,20 +52,10 @@
// to its previous owner. Returns a reference to this Backbone object.
// to its previous owner. Returns a reference to this Backbone object.
Backbone
.
noConflict
=
function
()
{
Backbone
.
noConflict
=
function
()
{
root
.
Backbone
=
previousBackbone
;
root
.
Backbone
=
previousBackbone
;
root
.
Exoskeleton
=
previousExoskeleton
;
return
this
;
return
this
;
};
};
// Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
// will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
// set a `X-Http-Method-Override` header.
Backbone
.
emulateHTTP
=
false
;
// Turn on `emulateJSON` to support legacy servers that can't deal with direct
// `application/json` requests ... will encode the body as
// `application/x-www-form-urlencoded` instead and will send the model in a
// form param named `model`.
Backbone
.
emulateJSON
=
false
;
// Helpers
// Helpers
// -------
// -------
...
@@ -84,7 +69,7 @@
...
@@ -84,7 +69,7 @@
// The constructor function for the new subclass is either defined by you
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent's constructor.
// by us to simply call the parent's constructor.
if
(
protoProps
&&
hasOwnProperty
.
call
(
protoProps
,
'
constructor
'
))
{
if
(
protoProps
&&
_
.
has
(
protoProps
,
'
constructor
'
))
{
child
=
protoProps
.
constructor
;
child
=
protoProps
.
constructor
;
}
else
{
}
else
{
child
=
function
(){
return
parent
.
apply
(
this
,
arguments
);
};
child
=
function
(){
return
parent
.
apply
(
this
,
arguments
);
};
...
@@ -123,195 +108,177 @@
...
@@ -123,195 +108,177 @@
model
.
trigger
(
'
error
'
,
model
,
resp
,
options
);
model
.
trigger
(
'
error
'
,
model
,
resp
,
options
);
};
};
};
};
utils
.
result
=
function
result
(
object
,
property
)
{
var
value
=
object
?
object
[
property
]
:
undefined
;
return
typeof
value
===
'
function
'
?
object
[
property
]()
:
value
;
};
utils
.
defaults
=
function
defaults
(
obj
,
from1
,
from2
)
{
// Checker for utility methods. Useful for custom builds.
[].
slice
.
call
(
arguments
,
1
).
forEach
(
function
(
item
)
{
var
utilExists
=
function
(
method
)
{
for
(
var
key
in
item
)
if
(
obj
[
key
]
===
undefined
)
return
typeof
_
[
method
]
===
'
function
'
;
obj
[
key
]
=
item
[
key
];
};
});
return
obj
;
};
utils
.
extend
=
function
extend
(
obj
)
{
utils
.
result
=
function
result
(
object
,
property
)
{
[].
slice
.
call
(
arguments
,
1
).
forEach
(
function
(
item
)
{
var
value
=
object
?
object
[
property
]
:
undefined
;
for
(
var
key
in
item
)
obj
[
key
]
=
item
[
key
];
return
typeof
value
===
'
function
'
?
object
[
property
]()
:
value
;
});
};
return
obj
;
};
var
htmlEscapes
=
{
utils
.
defaults
=
function
defaults
(
obj
)
{
'
&
'
:
'
&
'
,
slice
.
call
(
arguments
,
1
).
forEach
(
function
(
item
)
{
'
<
'
:
'
<
'
,
for
(
var
key
in
item
)
if
(
obj
[
key
]
===
undefined
)
'
>
'
:
'
>
'
,
obj
[
key
]
=
item
[
key
];
'
"
'
:
'
"
'
,
});
"
'
"
:
'
'
'
return
obj
;
};
};
utils
.
escape
=
function
escape
(
string
)
{
utils
.
extend
=
function
extend
(
obj
)
{
return
string
==
null
?
''
:
String
(
string
).
replace
(
/
[
&<>"'
]
/g
,
function
(
match
)
{
slice
.
call
(
arguments
,
1
).
forEach
(
function
(
item
)
{
return
htmlEscapes
[
match
];
for
(
var
key
in
item
)
obj
[
key
]
=
item
[
key
];
});
});
};
return
obj
;
};
utils
.
sortedIndex
=
function
sortedIndex
(
array
,
obj
,
iterator
,
context
)
{
var
htmlEscapes
=
{
iterator
=
iterator
==
null
?
Function
.
prototype
:
'
&
'
:
'
&
'
,
(
typeof
iterator
===
'
function
'
?
iterator
:
function
(
obj
){
return
obj
[
iterator
];
});
'
<
'
:
'
<
'
,
var
value
=
iterator
.
call
(
context
,
obj
);
'
>
'
:
'
>
'
,
var
low
=
0
,
high
=
array
.
length
;
'
"
'
:
'
"
'
,
while
(
low
<
high
)
{
"
'
"
:
'
'
'
var
mid
=
(
low
+
high
)
>>>
1
;
};
iterator
.
call
(
context
,
array
[
mid
])
<
value
?
low
=
mid
+
1
:
high
=
mid
;
}
return
low
;
};
utils
.
sortBy
=
function
(
obj
,
value
,
context
)
{
utils
.
escape
=
function
escape
(
string
)
{
var
iterator
=
typeof
value
===
'
function
'
?
value
:
function
(
obj
){
return
obj
[
value
];
};
return
string
==
null
?
''
:
String
(
string
).
replace
(
/
[
&<>"'
]
/g
,
function
(
match
)
{
return
obj
return
htmlEscapes
[
match
];
.
map
(
function
(
value
,
index
,
list
)
{
return
{
value
:
value
,
index
:
index
,
criteria
:
iterator
.
call
(
context
,
value
,
index
,
list
)
};
})
.
sort
(
function
(
left
,
right
)
{
var
a
=
left
.
criteria
;
var
b
=
right
.
criteria
;
if
(
a
!==
b
)
{
if
(
a
>
b
||
a
===
void
0
)
return
1
;
if
(
a
<
b
||
b
===
void
0
)
return
-
1
;
}
return
left
.
index
-
right
.
index
;
})
.
map
(
function
(
item
)
{
return
item
.
value
;
});
});
};
};
/** Used to generate unique IDs */
utils
.
sortBy
=
function
(
obj
,
value
,
context
)
{
var
idCounter
=
0
;
var
iterator
=
typeof
value
===
'
function
'
?
value
:
function
(
obj
){
return
obj
[
value
];
};
return
obj
.
map
(
function
(
value
,
index
,
list
)
{
return
{
value
:
value
,
index
:
index
,
criteria
:
iterator
.
call
(
context
,
value
,
index
,
list
)
};
})
.
sort
(
function
(
left
,
right
)
{
var
a
=
left
.
criteria
;
var
b
=
right
.
criteria
;
if
(
a
!==
b
)
{
if
(
a
>
b
||
a
===
void
0
)
return
1
;
if
(
a
<
b
||
b
===
void
0
)
return
-
1
;
}
return
left
.
index
-
right
.
index
;
})
.
map
(
function
(
item
)
{
return
item
.
value
;
});
};
utils
.
uniqueId
=
function
uniqueId
(
prefix
)
{
/** Used to generate unique IDs */
var
id
=
++
idCounter
+
''
;
var
idCounter
=
0
;
return
prefix
?
prefix
+
id
:
id
;
};
var
eq
=
function
(
a
,
b
,
aStack
,
bStack
)
{
utils
.
uniqueId
=
function
uniqueId
(
prefix
)
{
// Identical objects are equal. `0 === -0`, but they aren't identical.
var
id
=
++
idCounter
+
''
;
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
return
prefix
?
prefix
+
id
:
id
;
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
;
utils
.
has
=
function
(
obj
,
key
)
{
// Unwrap any wrapped objects.
return
Object
.
hasOwnProperty
.
call
(
obj
,
key
);
//if (a instanceof _) a = a._wrapped;
};
//if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var
eq
=
function
(
a
,
b
,
aStack
,
bStack
)
{
var
className
=
toString
.
call
(
a
);
// Identical objects are equal. `0 === -0`, but they aren't identical.
if
(
className
!=
toString
.
call
(
b
))
return
false
;
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
switch
(
className
)
{
if
(
a
===
b
)
return
a
!==
0
||
1
/
a
==
1
/
b
;
// Strings, numbers, dates, and booleans are compared by value.
// A strict comparison is necessary because `null == undefined`.
case
'
[object String]
'
:
if
(
a
==
null
||
b
==
null
)
return
a
===
b
;
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// Unwrap any wrapped objects.
// equivalent to `new String("5")`.
//if (a instanceof _) a = a._wrapped;
return
a
==
String
(
b
);
//if (b instanceof _) b = b._wrapped;
case
'
[object Number]
'
:
// Compare `[[Class]]` names.
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
var
className
=
toString
.
call
(
a
);
// other numeric values.
if
(
className
!=
toString
.
call
(
b
))
return
false
;
return
a
!==
+
a
?
b
!==
+
b
:
(
a
===
0
?
1
/
a
===
1
/
b
:
a
===
+
b
);
switch
(
className
)
{
case
'
[object Date]
'
:
// Strings, numbers, dates, and booleans are compared by value.
case
'
[object Boolean]
'
:
case
'
[object String]
'
:
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// millisecond representations. Note that invalid dates with millisecond representations
// equivalent to `new String("5")`.
// of `NaN` are not equivalent.
return
a
==
String
(
b
);
return
+
a
==
+
b
;
case
'
[object Number]
'
:
// RegExps are compared by their source patterns and flags.
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
case
'
[object RegExp]
'
:
// other numeric values.
return
a
.
source
==
b
.
source
&&
return
a
!==
+
a
?
b
!==
+
b
:
(
a
===
0
?
1
/
a
===
1
/
b
:
a
===
+
b
);
a
.
global
==
b
.
global
&&
case
'
[object Date]
'
:
a
.
multiline
==
b
.
multiline
&&
case
'
[object Boolean]
'
:
a
.
ignoreCase
==
b
.
ignoreCase
;
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
}
// millisecond representations. Note that invalid dates with millisecond representations
if
(
typeof
a
!=
'
object
'
||
typeof
b
!=
'
object
'
)
return
false
;
// of `NaN` are not equivalent.
// Assume equality for cyclic structures. The algorithm for detecting cyclic
return
+
a
==
+
b
;
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// RegExps are compared by their source patterns and flags.
var
length
=
aStack
.
length
;
case
'
[object RegExp]
'
:
while
(
length
--
)
{
return
a
.
source
==
b
.
source
&&
// Linear search. Performance is inversely proportional to the number of
a
.
global
==
b
.
global
&&
// unique nested structures.
a
.
multiline
==
b
.
multiline
&&
if
(
aStack
[
length
]
==
a
)
return
bStack
[
length
]
==
b
;
a
.
ignoreCase
==
b
.
ignoreCase
;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var
aCtor
=
a
.
constructor
,
bCtor
=
b
.
constructor
;
if
(
aCtor
!==
bCtor
&&
!
(
typeof
aCtor
===
'
function
'
&&
(
aCtor
instanceof
aCtor
)
&&
typeof
bCtor
===
'
function
'
&&
(
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
;
// Recursively compare objects and arrays.
if
(
className
==
'
[object Array]
'
)
{
// Compare array lengths to determine if a deep comparison is necessary.
size
=
a
.
length
;
result
=
size
==
b
.
length
;
if
(
result
)
{
// Deep compare the contents, ignoring non-numeric properties.
while
(
size
--
)
{
if
(
!
(
result
=
eq
(
a
[
size
],
b
[
size
],
aStack
,
bStack
)))
break
;
}
}
}
}
else
{
if
(
typeof
a
!=
'
object
'
||
typeof
b
!=
'
object
'
)
return
false
;
// Deep compare objects.
// Assume equality for cyclic structures. The algorithm for detecting cyclic
for
(
var
key
in
a
)
{
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
if
(
hasOwnProperty
.
call
(
a
,
key
))
{
var
length
=
aStack
.
length
;
// Count the expected number of properties.
while
(
length
--
)
{
size
++
;
// Linear search. Performance is inversely proportional to the number of
// Deep compare each member.
// unique nested structures.
if
(
!
(
result
=
hasOwnProperty
.
call
(
b
,
key
)
&&
eq
(
a
[
key
],
b
[
key
],
aStack
,
bStack
)))
break
;
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
&&
!
(
typeof
aCtor
===
'
function
'
&&
(
aCtor
instanceof
aCtor
)
&&
typeof
bCtor
===
'
function
'
&&
(
bCtor
instanceof
bCtor
)))
{
return
false
;
}
}
// Ensure that both objects contain the same number of properties.
// Add the first object to the stack of traversed objects.
if
(
result
)
{
aStack
.
push
(
a
);
for
(
key
in
b
)
{
bStack
.
push
(
b
);
if
(
hasOwnProperty
.
call
(
b
,
key
)
&&
!
(
size
--
))
break
;
var
size
=
0
,
result
=
true
;
// Recursively compare objects and arrays.
if
(
className
===
'
[object Array]
'
)
{
// Compare array lengths to determine if a deep comparison is necessary.
size
=
a
.
length
;
result
=
size
===
b
.
length
;
if
(
result
)
{
// Deep compare the contents, ignoring non-numeric properties.
while
(
size
--
)
{
if
(
!
(
result
=
eq
(
a
[
size
],
b
[
size
],
aStack
,
bStack
)))
break
;
}
}
}
else
{
// 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.
if
(
result
)
{
for
(
key
in
b
)
{
if
(
_
.
has
(
b
,
key
)
&&
!
(
size
--
))
break
;
}
result
=
!
size
;
}
}
result
=
!
size
;
}
}
}
// Remove the first object from the stack of traversed objects.
// Remove the first object from the stack of traversed objects.
aStack
.
pop
();
aStack
.
pop
();
bStack
.
pop
();
bStack
.
pop
();
return
result
;
return
result
;
};
};
// Perform a deep comparison to check if two objects are equal.
// Perform a deep comparison to check if two objects are equal.
utils
.
isEqual
=
function
(
a
,
b
)
{
utils
.
isEqual
=
function
(
a
,
b
)
{
return
eq
(
a
,
b
,
[],
[]);
return
eq
(
a
,
b
,
[],
[]);
};
utils
.
matchesSelector
=
(
function
()
{
// Suffix.
var
sfx
=
'
MatchesSelector
'
;
var
tag
=
document
.
createElement
(
'
div
'
);
var
name
;
[
'
matches
'
,
'
webkit
'
+
sfx
,
'
moz
'
+
sfx
,
'
ms
'
+
sfx
].
some
(
function
(
item
)
{
var
valid
=
(
item
in
tag
);
name
=
item
;
return
valid
;
});
if
(
!
name
)
{
throw
new
Error
(
'
Element#matches is not supported
'
);
}
return
function
(
element
,
selector
)
{
return
element
[
name
](
selector
)
};
};
})();
// Backbone.Events
// Backbone.Events
// ---------------
// ---------------
...
@@ -343,7 +310,6 @@ var Events = Backbone.Events = {
...
@@ -343,7 +310,6 @@ var Events = Backbone.Events = {
if
(
!
eventsApi
(
this
,
'
once
'
,
name
,
[
callback
,
context
])
||
!
callback
)
return
this
;
if
(
!
eventsApi
(
this
,
'
once
'
,
name
,
[
callback
,
context
])
||
!
callback
)
return
this
;
var
self
=
this
;
var
self
=
this
;
var
ran
;
var
ran
;
var
once
=
function
()
{
var
once
=
function
()
{
if
(
ran
)
return
;
if
(
ran
)
return
;
ran
=
true
;
ran
=
true
;
...
@@ -362,10 +328,9 @@ var Events = Backbone.Events = {
...
@@ -362,10 +328,9 @@ var Events = Backbone.Events = {
var
retain
,
ev
,
events
,
names
,
i
,
l
,
j
,
k
;
var
retain
,
ev
,
events
,
names
,
i
,
l
,
j
,
k
;
if
(
!
this
.
_events
||
!
eventsApi
(
this
,
'
off
'
,
name
,
[
callback
,
context
]))
return
this
;
if
(
!
this
.
_events
||
!
eventsApi
(
this
,
'
off
'
,
name
,
[
callback
,
context
]))
return
this
;
if
(
!
name
&&
!
callback
&&
!
context
)
{
if
(
!
name
&&
!
callback
&&
!
context
)
{
this
.
_events
=
{}
;
this
.
_events
=
void
0
;
return
this
;
return
this
;
}
}
names
=
name
?
[
name
]
:
Object
.
keys
(
this
.
_events
);
names
=
name
?
[
name
]
:
Object
.
keys
(
this
.
_events
);
for
(
i
=
0
,
l
=
names
.
length
;
i
<
l
;
i
++
)
{
for
(
i
=
0
,
l
=
names
.
length
;
i
<
l
;
i
++
)
{
name
=
names
[
i
];
name
=
names
[
i
];
...
@@ -459,7 +424,7 @@ var triggerEvents = function(events, args) {
...
@@ -459,7 +424,7 @@ var triggerEvents = function(events, args) {
case
1
:
while
(
++
i
<
l
)
(
ev
=
events
[
i
]).
callback
.
call
(
ev
.
ctx
,
a1
);
return
;
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
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
;
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
;
}
}
};
};
...
@@ -483,6 +448,11 @@ Object.keys(listenMethods).forEach(function(method) {
...
@@ -483,6 +448,11 @@ Object.keys(listenMethods).forEach(function(method) {
// Aliases for backwards compatibility.
// Aliases for backwards compatibility.
Events
.
bind
=
Events
.
on
;
Events
.
bind
=
Events
.
on
;
Events
.
unbind
=
Events
.
off
;
Events
.
unbind
=
Events
.
off
;
// Allow the `Backbone` object to serve as a global event bus, for folks who
// want global "pubsub" in a convenient place.
_
.
extend
(
Backbone
,
Events
);
// Backbone.Model
// Backbone.Model
// --------------
// --------------
...
@@ -525,7 +495,7 @@ _.extend(Model.prototype, Events, {
...
@@ -525,7 +495,7 @@ _.extend(Model.prototype, Events, {
// Return a copy of the model's `attributes` object.
// Return a copy of the model's `attributes` object.
toJSON
:
function
(
options
)
{
toJSON
:
function
(
options
)
{
return
_
.
extend
(
Object
.
create
(
null
)
,
this
.
attributes
);
return
_
.
extend
(
{}
,
this
.
attributes
);
},
},
// Proxy `Backbone.sync` by default -- but override this if you need
// Proxy `Backbone.sync` by default -- but override this if you need
...
@@ -600,7 +570,7 @@ _.extend(Model.prototype, Events, {
...
@@ -600,7 +570,7 @@ _.extend(Model.prototype, Events, {
// Trigger all relevant attribute changes.
// Trigger all relevant attribute changes.
if
(
!
silent
)
{
if
(
!
silent
)
{
if
(
changes
.
length
)
this
.
_pending
=
true
;
if
(
changes
.
length
)
this
.
_pending
=
options
;
for
(
var
i
=
0
,
l
=
changes
.
length
;
i
<
l
;
i
++
)
{
for
(
var
i
=
0
,
l
=
changes
.
length
;
i
<
l
;
i
++
)
{
this
.
trigger
(
'
change:
'
+
changes
[
i
],
this
,
current
[
changes
[
i
]],
options
);
this
.
trigger
(
'
change:
'
+
changes
[
i
],
this
,
current
[
changes
[
i
]],
options
);
}
}
...
@@ -611,6 +581,7 @@ _.extend(Model.prototype, Events, {
...
@@ -611,6 +581,7 @@ _.extend(Model.prototype, Events, {
if
(
changing
)
return
this
;
if
(
changing
)
return
this
;
if
(
!
silent
)
{
if
(
!
silent
)
{
while
(
this
.
_pending
)
{
while
(
this
.
_pending
)
{
options
=
this
.
_pending
;
this
.
_pending
=
false
;
this
.
_pending
=
false
;
this
.
trigger
(
'
change
'
,
this
,
options
);
this
.
trigger
(
'
change
'
,
this
,
options
);
}
}
...
@@ -637,7 +608,7 @@ _.extend(Model.prototype, Events, {
...
@@ -637,7 +608,7 @@ _.extend(Model.prototype, Events, {
// If you specify an attribute name, determine if that attribute has changed.
// If you specify an attribute name, determine if that attribute has changed.
hasChanged
:
function
(
attr
)
{
hasChanged
:
function
(
attr
)
{
if
(
attr
==
null
)
return
!!
Object
.
keys
(
this
.
changed
).
length
;
if
(
attr
==
null
)
return
!!
Object
.
keys
(
this
.
changed
).
length
;
return
hasOwnProperty
.
call
(
this
.
changed
,
attr
);
return
_
.
has
(
this
.
changed
,
attr
);
},
},
// Return an object containing all the attributes that have changed, or
// Return an object containing all the attributes that have changed, or
...
@@ -778,9 +749,12 @@ _.extend(Model.prototype, Events, {
...
@@ -778,9 +749,12 @@ _.extend(Model.prototype, Events, {
// using Backbone's restful methods, override this to change the endpoint
// using Backbone's restful methods, override this to change the endpoint
// that will be called.
// that will be called.
url
:
function
()
{
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
;
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
// **parse** converts a response into the hash of attributes to be `set` on
...
@@ -796,7 +770,7 @@ _.extend(Model.prototype, Events, {
...
@@ -796,7 +770,7 @@ _.extend(Model.prototype, Events, {
// A model is new if it has never been saved to the server, and lacks an id.
// A model is new if it has never been saved to the server, and lacks an id.
isNew
:
function
()
{
isNew
:
function
()
{
return
this
.
id
==
null
;
return
!
this
.
has
(
this
.
idAttribute
)
;
},
},
// Check if the model is currently in a valid state.
// Check if the model is currently in a valid state.
...
@@ -822,7 +796,7 @@ if (_.keys) {
...
@@ -822,7 +796,7 @@ if (_.keys) {
var
modelMethods
=
[
'
keys
'
,
'
values
'
,
'
pairs
'
,
'
invert
'
,
'
pick
'
,
'
omit
'
];
var
modelMethods
=
[
'
keys
'
,
'
values
'
,
'
pairs
'
,
'
invert
'
,
'
pick
'
,
'
omit
'
];
// Mix in each Underscore method as a proxy to `Model#attributes`.
// Mix in each Underscore method as a proxy to `Model#attributes`.
modelMethods
.
forEach
(
function
(
method
)
{
modelMethods
.
f
ilter
(
utilExists
).
f
orEach
(
function
(
method
)
{
Model
.
prototype
[
method
]
=
function
()
{
Model
.
prototype
[
method
]
=
function
()
{
var
args
=
slice
.
call
(
arguments
);
var
args
=
slice
.
call
(
arguments
);
args
.
unshift
(
this
.
attributes
);
args
.
unshift
(
this
.
attributes
);
...
@@ -830,6 +804,7 @@ if (_.keys) {
...
@@ -830,6 +804,7 @@ if (_.keys) {
};
};
});
});
}
}
// Backbone.Collection
// Backbone.Collection
// -------------------
// -------------------
...
@@ -901,7 +876,7 @@ _.extend(Collection.prototype, Events, {
...
@@ -901,7 +876,7 @@ _.extend(Collection.prototype, Events, {
options
.
index
=
index
;
options
.
index
=
index
;
model
.
trigger
(
'
remove
'
,
model
,
this
,
options
);
model
.
trigger
(
'
remove
'
,
model
,
this
,
options
);
}
}
this
.
_removeReference
(
model
);
this
.
_removeReference
(
model
,
options
);
}
}
return
singular
?
models
[
0
]
:
models
;
return
singular
?
models
[
0
]
:
models
;
},
},
...
@@ -927,11 +902,11 @@ _.extend(Collection.prototype, Events, {
...
@@ -927,11 +902,11 @@ _.extend(Collection.prototype, Events, {
// Turn bare objects into model references, and prevent invalid models
// Turn bare objects into model references, and prevent invalid models
// from being added.
// from being added.
for
(
i
=
0
,
l
=
models
.
length
;
i
<
l
;
i
++
)
{
for
(
i
=
0
,
l
=
models
.
length
;
i
<
l
;
i
++
)
{
attrs
=
models
[
i
];
attrs
=
models
[
i
]
||
{}
;
if
(
attrs
instanceof
Model
)
{
if
(
attrs
instanceof
Model
)
{
id
=
model
=
attrs
;
id
=
model
=
attrs
;
}
else
{
}
else
{
id
=
attrs
[
targetModel
.
prototype
.
idAttribute
];
id
=
attrs
[
targetModel
.
prototype
.
idAttribute
||
'
id
'
];
}
}
// If a duplicate is found, prevent it from being added and
// If a duplicate is found, prevent it from being added and
...
@@ -951,14 +926,13 @@ _.extend(Collection.prototype, Events, {
...
@@ -951,14 +926,13 @@ _.extend(Collection.prototype, Events, {
model
=
models
[
i
]
=
this
.
_prepareModel
(
attrs
,
options
);
model
=
models
[
i
]
=
this
.
_prepareModel
(
attrs
,
options
);
if
(
!
model
)
continue
;
if
(
!
model
)
continue
;
toAdd
.
push
(
model
);
toAdd
.
push
(
model
);
this
.
_addReference
(
model
,
options
);
// 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
;
}
}
if
(
order
)
order
.
push
(
existing
||
model
);
// Do not add multiple models with the same `id`.
model
=
existing
||
model
;
if
(
order
&&
(
model
.
isNew
()
||
!
modelMap
[
model
.
id
]))
order
.
push
(
model
);
modelMap
[
model
.
id
]
=
true
;
}
}
// Remove nonexistent models if appropriate.
// Remove nonexistent models if appropriate.
...
@@ -1008,7 +982,7 @@ _.extend(Collection.prototype, Events, {
...
@@ -1008,7 +982,7 @@ _.extend(Collection.prototype, Events, {
reset
:
function
(
models
,
options
)
{
reset
:
function
(
models
,
options
)
{
options
||
(
options
=
{});
options
||
(
options
=
{});
for
(
var
i
=
0
,
l
=
this
.
models
.
length
;
i
<
l
;
i
++
)
{
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
;
options
.
previousModels
=
this
.
models
;
this
.
_reset
();
this
.
_reset
();
...
@@ -1049,7 +1023,7 @@ _.extend(Collection.prototype, Events, {
...
@@ -1049,7 +1023,7 @@ _.extend(Collection.prototype, Events, {
// Get a model from the set by id.
// Get a model from the set by id.
get
:
function
(
obj
)
{
get
:
function
(
obj
)
{
if
(
obj
==
null
)
return
void
0
;
if
(
obj
==
null
)
return
void
0
;
return
this
.
_byId
[
obj
.
id
]
||
this
.
_byId
[
obj
.
cid
]
||
this
.
_byId
[
obj
];
return
this
.
_byId
[
obj
]
||
this
.
_byId
[
obj
.
id
]
||
this
.
_byId
[
obj
.
cid
];
},
},
// Get the model at the given index.
// Get the model at the given index.
...
@@ -1127,7 +1101,7 @@ _.extend(Collection.prototype, Events, {
...
@@ -1127,7 +1101,7 @@ _.extend(Collection.prototype, Events, {
if
(
!
options
.
wait
)
this
.
add
(
model
,
options
);
if
(
!
options
.
wait
)
this
.
add
(
model
,
options
);
var
collection
=
this
;
var
collection
=
this
;
var
success
=
options
.
success
;
var
success
=
options
.
success
;
options
.
success
=
function
(
model
,
resp
,
options
)
{
options
.
success
=
function
(
model
,
resp
)
{
if
(
options
.
wait
)
collection
.
add
(
model
,
options
);
if
(
options
.
wait
)
collection
.
add
(
model
,
options
);
if
(
success
)
success
(
model
,
resp
,
options
);
if
(
success
)
success
(
model
,
resp
,
options
);
};
};
...
@@ -1151,17 +1125,14 @@ _.extend(Collection.prototype, Events, {
...
@@ -1151,17 +1125,14 @@ _.extend(Collection.prototype, Events, {
_reset
:
function
()
{
_reset
:
function
()
{
this
.
length
=
0
;
this
.
length
=
0
;
this
.
models
=
[];
this
.
models
=
[];
this
.
_byId
=
{}
;
this
.
_byId
=
Object
.
create
(
null
)
;
},
},
// Prepare a hash of attributes (or other model) to be added to this
// Prepare a hash of attributes (or other model) to be added to this
// collection.
// collection.
_prepareModel
:
function
(
attrs
,
options
)
{
_prepareModel
:
function
(
attrs
,
options
)
{
if
(
attrs
instanceof
Collection
.
prototype
.
model
)
{
if
(
attrs
instanceof
Model
)
return
attrs
;
if
(
!
attrs
.
collection
)
attrs
.
collection
=
this
;
options
=
_
.
extend
({},
options
);
return
attrs
;
}
options
=
options
?
_
.
extend
({},
options
)
:
{};
options
.
collection
=
this
;
options
.
collection
=
this
;
var
model
=
new
this
.
model
(
attrs
,
options
);
var
model
=
new
this
.
model
(
attrs
,
options
);
if
(
!
model
.
validationError
)
return
model
;
if
(
!
model
.
validationError
)
return
model
;
...
@@ -1169,8 +1140,16 @@ _.extend(Collection.prototype, Events, {
...
@@ -1169,8 +1140,16 @@ _.extend(Collection.prototype, Events, {
return
false
;
return
false
;
},
},
// 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.
// 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
;
if
(
this
===
model
.
collection
)
delete
model
.
collection
;
model
.
off
(
'
all
'
,
this
.
_onModelEvent
,
this
);
model
.
off
(
'
all
'
,
this
.
_onModelEvent
,
this
);
},
},
...
@@ -1191,7 +1170,7 @@ _.extend(Collection.prototype, Events, {
...
@@ -1191,7 +1170,7 @@ _.extend(Collection.prototype, Events, {
});
});
if
(
_
.
each
)
{
if
(
utilExists
(
'
each
'
)
)
{
// Underscore methods that we want to implement on the Collection.
// Underscore methods that we want to implement on the Collection.
// 90% of the core usefulness of Backbone Collections is actually implemented
// 90% of the core usefulness of Backbone Collections is actually implemented
// right here:
// right here:
...
@@ -1203,7 +1182,7 @@ if (_.each) {
...
@@ -1203,7 +1182,7 @@ if (_.each) {
'
lastIndexOf
'
,
'
isEmpty
'
,
'
chain
'
];
'
lastIndexOf
'
,
'
isEmpty
'
,
'
chain
'
];
// Mix in each Underscore method as a proxy to `Collection#models`.
// Mix in each Underscore method as a proxy to `Collection#models`.
methods
.
forEach
(
function
(
method
)
{
methods
.
f
ilter
(
utilExists
).
f
orEach
(
function
(
method
)
{
Collection
.
prototype
[
method
]
=
function
()
{
Collection
.
prototype
[
method
]
=
function
()
{
var
args
=
slice
.
call
(
arguments
);
var
args
=
slice
.
call
(
arguments
);
args
.
unshift
(
this
.
models
);
args
.
unshift
(
this
.
models
);
...
@@ -1215,7 +1194,7 @@ if (_.each) {
...
@@ -1215,7 +1194,7 @@ if (_.each) {
var
attributeMethods
=
[
'
groupBy
'
,
'
countBy
'
,
'
sortBy
'
];
var
attributeMethods
=
[
'
groupBy
'
,
'
countBy
'
,
'
sortBy
'
];
// Use attributes instead of properties.
// Use attributes instead of properties.
attributeMethods
.
forEach
(
function
(
method
)
{
attributeMethods
.
f
ilter
(
utilExists
).
f
orEach
(
function
(
method
)
{
Collection
.
prototype
[
method
]
=
function
(
value
,
context
)
{
Collection
.
prototype
[
method
]
=
function
(
value
,
context
)
{
var
iterator
=
typeof
value
===
'
function
'
?
value
:
function
(
model
)
{
var
iterator
=
typeof
value
===
'
function
'
?
value
:
function
(
model
)
{
return
model
.
get
(
value
);
return
model
.
get
(
value
);
...
@@ -1226,12 +1205,23 @@ if (_.each) {
...
@@ -1226,12 +1205,23 @@ if (_.each) {
}
else
{
}
else
{
[
'
forEach
'
,
'
map
'
,
'
filter
'
,
'
some
'
,
'
every
'
,
'
reduce
'
,
'
reduceRight
'
,
[
'
forEach
'
,
'
map
'
,
'
filter
'
,
'
some
'
,
'
every
'
,
'
reduce
'
,
'
reduceRight
'
,
'
indexOf
'
,
'
lastIndexOf
'
].
forEach
(
function
(
method
)
{
'
indexOf
'
,
'
lastIndexOf
'
].
forEach
(
function
(
method
)
{
var
fn
=
Array
.
prototype
[
method
];
Collection
.
prototype
[
method
]
=
function
(
arg
,
context
)
{
Collection
.
prototype
[
method
]
=
function
(
arg
,
context
)
{
return
fn
.
call
(
this
.
models
,
arg
,
context
);
return
this
.
models
[
method
](
arg
,
context
);
};
};
});
});
// Exoskeleton-specific:
Collection
.
prototype
.
find
=
function
(
iterator
,
context
)
{
var
result
;
this
.
some
(
function
(
value
,
index
,
list
)
{
if
(
iterator
.
call
(
context
,
value
,
index
,
list
))
{
result
=
value
;
return
true
;
}
});
return
result
;
};
// Underscore methods that take a property name as an argument.
// Underscore methods that take a property name as an argument.
[
'
sortBy
'
].
forEach
(
function
(
method
)
{
[
'
sortBy
'
].
forEach
(
function
(
method
)
{
Collection
.
prototype
[
method
]
=
function
(
value
,
context
)
{
Collection
.
prototype
[
method
]
=
function
(
value
,
context
)
{
...
@@ -1242,614 +1232,539 @@ if (_.each) {
...
@@ -1242,614 +1232,539 @@ if (_.each) {
};
};
});
});
}
}
// Backbone.View
// -------------
// Backbone Views are almost more convention than they are actual code. A View
// is simply a JavaScript object that represents a logical chunk of UI in the
// DOM. This might be a single item, an entire list, a sidebar or panel, or
// even the surrounding frame which wraps your whole app. Defining a chunk of
// UI as a **View** allows you to define your DOM events declaratively, without
// having to worry about render order ... and makes it easy for the view to
// react to specific changes in the state of your models.
// Options with special meaning *(e.g. model, collection, id, className)* are
// attached directly to the view. See `viewOptions` for an exhaustive
// list.
// Cached regex to split keys for `delegate`.
var
delegateEventSplitter
=
/^
(\S
+
)\s
*
(
.*
)
$/
;
// List of view options to be merged as properties.
var
viewOptions
=
[
'
model
'
,
'
collection
'
,
'
el
'
,
'
id
'
,
'
attributes
'
,
'
className
'
,
'
tagName
'
,
'
events
'
];
// Creating a Backbone.View creates its initial element outside of the DOM,
// if an existing element is not provided...
var
View
=
Backbone
.
View
=
function
(
options
)
{
this
.
cid
=
_
.
uniqueId
(
'
view
'
);
if
(
options
)
Object
.
keys
(
options
).
forEach
(
function
(
key
)
{
if
(
viewOptions
.
indexOf
(
key
)
!==
-
1
)
this
[
key
]
=
options
[
key
];
},
this
);
this
.
_handlers
=
[];
this
.
_ensureElement
();
this
.
initialize
.
apply
(
this
,
arguments
);
this
.
delegateEvents
();
};
// Set up all inheritable **Backbone.View** properties and methods.
_
.
extend
(
View
.
prototype
,
Events
,
{
// The default `tagName` of a View's element is `"div"`.
// Backbone.View
tagName
:
'
div
'
,
// -------------
// jQuery delegate for element lookup, scoped to DOM elements within the
// current view. This should be preferred to global lookups where possible.
$
:
function
(
selector
)
{
return
Backbone
.
$
?
this
.
$el
.
find
(
selector
)
:
this
.
findAll
(
selector
);
},
// Exoskeleton-related DOM methods.
// Backbone Views are almost more convention than they are actual code. A View
find
:
function
(
selector
)
{
// is simply a JavaScript object that represents a logical chunk of UI in the
return
this
.
el
.
querySelector
(
selector
);
// DOM. This might be a single item, an entire list, a sidebar or panel, or
},
// even the surrounding frame which wraps your whole app. Defining a chunk of
// UI as a **View** allows you to define your DOM events declaratively, without
// having to worry about render order ... and makes it easy for the view to
// react to specific changes in the state of your models.
// Creating a Backbone.View creates its initial element outside of the DOM,
// if an existing element is not provided...
var
View
=
Backbone
.
View
=
function
(
options
)
{
this
.
cid
=
_
.
uniqueId
(
'
view
'
);
if
(
options
)
Object
.
keys
(
options
).
forEach
(
function
(
key
)
{
if
(
viewOptions
.
indexOf
(
key
)
!==
-
1
)
this
[
key
]
=
options
[
key
];
},
this
);
this
.
_ensureElement
();
this
.
initialize
.
apply
(
this
,
arguments
);
};
findAll
:
function
(
selector
)
{
// Cached regex to split keys for `delegate`.
return
slice
.
call
(
this
.
el
.
querySelectorAll
(
selector
));
var
delegateEventSplitter
=
/^
(\S
+
)\s
*
(
.*
)
$/
;
},
// Initialize is an empty function by default. Override it with your own
// List of view options to be merged as properties.
// initialization logic.
var
viewOptions
=
[
'
model
'
,
'
collection
'
,
'
el
'
,
'
id
'
,
'
attributes
'
,
'
className
'
,
'
tagName
'
,
'
events
'
];
initialize
:
function
(){},
// **render** is the core function that your view should override, in order
// Set up all inheritable **Backbone.View** properties and methods.
// to populate its element (`this.el`), with the appropriate HTML. The
_
.
extend
(
View
.
prototype
,
Events
,
{
// convention is for **render** to always return `this`.
render
:
function
()
{
return
this
;
},
// Remove this view by taking the element out of the DOM, and removing any
// The default `tagName` of a View's element is `"div"`.
// applicable Backbone.Events listeners.
tagName
:
'
div
'
,
remove
:
function
()
{
if
(
Backbone
.
$
)
{
this
.
$el
.
remove
()
}
else
if
(
this
.
el
.
parentNode
)
{
this
.
el
.
parentNode
.
removeChild
(
this
.
el
);
}
this
.
stopListening
();
return
this
;
},
// Change the view's element (`this.el` property), including event
// jQuery delegate for element lookup, scoped to DOM elements within the
// re-delegation.
// current view. This should be preferred to global lookups where possible.
setElement
:
function
(
element
,
delegate
)
{
$
:
function
(
selector
)
{
if
(
Backbone
.
$
)
{
return
this
.
$el
.
find
(
selector
);
if
(
this
.
$el
)
this
.
undelegateEvents
();
},
this
.
$el
=
element
instanceof
Backbone
.
$
?
element
:
Backbone
.
$
(
element
);
this
.
el
=
this
.
$el
[
0
];
}
else
{
if
(
this
.
el
)
this
.
undelegateEvents
();
var
el
=
(
typeof
element
===
'
string
'
)
?
document
.
querySelector
(
element
)
:
element
;
this
.
el
=
el
;
}
if
(
delegate
!==
false
)
this
.
delegateEvents
();
return
this
;
},
delegate
:
function
(
eventName
,
selector
,
callback
)
{
// Initialize is an empty function by default. Override it with your own
if
(
typeof
selector
===
'
function
'
)
{
// initialization logic.
callback
=
selector
;
initialize
:
function
(){},
selector
=
null
;
}
if
(
typeof
callback
!==
'
function
'
)
{
// **render** is the core function that your view should override, in order
throw
new
TypeError
(
'
View#delegate expects callback function
'
);
// to populate its element (`this.el`), with the appropriate HTML. The
}
// convention is for **render** to always return `this`.
render
:
function
()
{
return
this
;
},
var
root
=
this
.
el
;
// Remove this view by taking the element out of the DOM, and removing any
var
bound
=
callback
.
bind
(
this
);
// applicable Backbone.Events listeners.
var
handler
=
selector
?
function
(
event
)
{
remove
:
function
()
{
// if (event.target === root) {
this
.
_removeElement
();
// event.delegateTarget = el;
this
.
stopListening
();
// return bound(event);
return
this
;
// }
},
for
(
var
el
=
event
.
target
;
el
&&
el
!==
root
;
el
=
el
.
parentNode
)
{
if
(
utils
.
matchesSelector
(
el
,
selector
))
{
// Remove this view's element from the document and all event listeners
// event.currentTarget or event.target are read-only.
// attached to it. Exposed for subclasses using an alternative DOM
event
.
delegateTarget
=
el
;
// manipulation API.
return
bound
(
event
);
_removeElement
:
function
()
{
}
this
.
$el
.
remove
();
},
// Change the view's element (`this.el` property) and re-delegate the
// view's events on the new element.
setElement
:
function
(
element
)
{
this
.
undelegateEvents
();
this
.
_setElement
(
element
);
this
.
delegateEvents
();
return
this
;
},
// Creates the `this.el` and `this.$el` references for this view using the
// given `el` and a hash of `attributes`. `el` can be a CSS selector or an
// HTML string, a jQuery context or an element. Subclasses can override
// this to utilize an alternative DOM manipulation API and are only required
// to set the `this.el` property.
_setElement
:
function
(
el
)
{
if
(
!
Backbone
.
$
)
throw
new
Error
(
'
You must either include jQuery or override Backbone.View.prototype methods (Google Backbone.NativeView)
'
);
this
.
$el
=
el
instanceof
Backbone
.
$
?
el
:
Backbone
.
$
(
el
);
this
.
el
=
this
.
$el
[
0
];
},
// Set callbacks, where `this.events` is a hash of
//
// *{"event selector": "callback"}*
//
// {
// 'mousedown .title': 'edit',
// 'click .button': 'save',
// 'click .open': function(e) { ... }
// }
//
// pairs. Callbacks will be bound to the view, with `this` set properly.
// Uses event delegation for efficiency.
// Omitting the selector binds the event to `this.el`.
delegateEvents
:
function
(
events
)
{
if
(
!
(
events
||
(
events
=
_
.
result
(
this
,
'
events
'
))))
return
this
;
this
.
undelegateEvents
();
for
(
var
key
in
events
)
{
var
method
=
events
[
key
];
if
(
typeof
method
!==
'
function
'
)
method
=
this
[
events
[
key
]];
// if (!method) continue;
var
match
=
key
.
match
(
delegateEventSplitter
);
this
.
delegate
(
match
[
1
],
match
[
2
],
method
.
bind
(
this
));
}
}
}
:
bound
;
return
this
;
},
root
.
addEventListener
(
eventName
,
handler
,
false
);
this
.
_handlers
.
push
({
// Add a single event listener to the view's element (or a child element
eventName
:
eventName
,
selector
:
selector
,
// using `selector`). This only works for delegate-able events: not `focus`,
callback
:
callback
,
handler
:
handler
// `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
});
delegate
:
function
(
eventName
,
selector
,
listener
)
{
return
handler
;
this
.
$el
.
on
(
eventName
+
'
.delegateEvents
'
+
this
.
cid
,
selector
,
listener
);
},
},
undelegate
:
function
(
eventName
,
selector
,
callback
)
{
// Clears all callbacks previously bound to the view by `delegateEvents`.
if
(
typeof
selector
===
'
function
'
)
{
// You usually don't need to use this, but may wish to if you have multiple
callback
=
selector
;
// Backbone views attached to the same DOM element.
selector
=
null
;
undelegateEvents
:
function
()
{
}
if
(
this
.
$el
)
this
.
$el
.
off
(
'
.delegateEvents
'
+
this
.
cid
);
return
this
;
var
root
=
this
.
el
;
},
var
handlers
=
this
.
_handlers
;
var
removeListener
=
function
(
item
)
{
// A finer-grained `undelegateEvents` for removing a single delegated event.
root
.
removeEventListener
(
item
.
eventName
,
item
.
handler
,
false
);
// `selector` and `listener` are both optional.
};
undelegate
:
function
(
eventName
,
selector
,
listener
)
{
this
.
$el
.
off
(
eventName
+
'
.delegateEvents
'
+
this
.
cid
,
selector
,
listener
);
// Remove all handlers.
},
if
(
!
eventName
&&
!
selector
&&
!
callback
)
{
handlers
.
forEach
(
removeListener
);
// Produces a DOM element to be assigned to your view. Exposed for
this
.
_handlers
=
[];
// subclasses using an alternative DOM manipulation API.
}
else
{
_createElement
:
function
(
tagName
)
{
// Remove some handlers.
return
document
.
createElement
(
tagName
);
handlers
},
.
filter
(
function
(
item
)
{
return
item
.
eventName
===
eventName
&&
// Ensure that the View has a DOM element to render into.
(
callback
?
item
.
callback
===
callback
:
true
)
&&
// If `this.el` is a string, pass it through `$()`, take the first
(
selector
?
item
.
selector
===
selector
:
true
);
// matching element, and re-assign it to `el`. Otherwise, create
})
// an element from the `id`, `className` and `tagName` properties.
.
forEach
(
function
(
item
)
{
_ensureElement
:
function
()
{
removeListener
(
item
);
if
(
!
this
.
el
)
{
handlers
.
splice
(
handlers
.
indexOf
(
item
),
1
);
var
attrs
=
_
.
extend
({},
_
.
result
(
this
,
'
attributes
'
));
});
if
(
this
.
id
)
attrs
.
id
=
_
.
result
(
this
,
'
id
'
);
}
if
(
this
.
className
)
attrs
[
'
class
'
]
=
_
.
result
(
this
,
'
className
'
);
},
this
.
setElement
(
this
.
_createElement
(
_
.
result
(
this
,
'
tagName
'
)));
this
.
_setAttributes
(
attrs
);
// Set callbacks, where `this.events` is a hash of
//
// *{"event selector": "callback"}*
//
// {
// 'mousedown .title': 'edit',
// 'click .button': 'save',
// 'click .open': function(e) { ... }
// }
//
// pairs. Callbacks will be bound to the view, with `this` set properly.
// Uses event delegation for efficiency.
// Omitting the selector binds the event to `this.el`.
// This only works for delegate-able events: not `focus`, `blur`, and
// not `change`, `submit`, and `reset` in Internet Explorer.
delegateEvents
:
function
(
events
,
keepOld
)
{
if
(
!
(
events
||
(
events
=
_
.
result
(
this
,
'
events
'
))))
return
this
;
if
(
!
keepOld
)
this
.
undelegateEvents
();
for
(
var
key
in
events
)
{
var
method
=
events
[
key
];
if
(
typeof
method
!==
'
function
'
)
method
=
this
[
events
[
key
]];
// if (!method) continue;
var
match
=
key
.
match
(
delegateEventSplitter
);
var
eventName
=
match
[
1
],
selector
=
match
[
2
];
if
(
Backbone
.
$
)
{
eventName
+=
'
.delegateEvents
'
+
this
.
cid
;
method
=
method
.
bind
(
this
);
this
.
$el
.
on
(
eventName
,
(
selector
?
selector
:
null
),
method
);
}
else
{
}
else
{
this
.
delegate
(
eventName
,
selector
,
method
)
this
.
setElement
(
_
.
result
(
this
,
'
el
'
));
}
}
}
},
return
this
;
},
// Clears all callbacks previously bound to the view with `delegateEvents`.
// You usually don't need to use this, but may wish to if you have multiple
// Backbone views attached to the same DOM element.
undelegateEvents
:
function
()
{
if
(
Backbone
.
$
)
{
this
.
$el
.
off
(
'
.delegateEvents
'
+
this
.
cid
);
}
else
{
this
.
undelegate
();
}
return
this
;
},
// Ensure that the View has a DOM element to render into.
// Set attributes from a hash on this view's element. Exposed for
// If `this.el` is a string, pass it through `$()`, take the first
// subclasses using an alternative DOM manipulation API.
// matching element, and re-assign it to `el`. Otherwise, create
_setAttributes
:
function
(
attributes
)
{
// an element from the `id`, `className` and `tagName` properties.
this
.
$el
.
attr
(
attributes
);
_ensureElement
:
function
()
{
if
(
!
this
.
el
)
{
var
attrs
=
_
.
extend
({},
_
.
result
(
this
,
'
attributes
'
));
if
(
this
.
id
)
attrs
.
id
=
_
.
result
(
this
,
'
id
'
);
if
(
this
.
className
)
attrs
.
className
=
_
.
result
(
this
,
'
className
'
);
if
(
attrs
[
'
class
'
])
attrs
.
className
=
attrs
[
'
class
'
];
var
el
=
_
.
extend
(
document
.
createElement
(
_
.
result
(
this
,
'
tagName
'
)),
attrs
);
this
.
setElement
(
el
,
false
);
}
else
{
this
.
setElement
(
_
.
result
(
this
,
'
el
'
),
false
);
}
}
}
});
// Backbone.sync
// -------------
// Override this function to change the manner in which Backbone persists
// models to the server. You will be passed the type of request, and the
// model in question. By default, makes a RESTful Ajax request
// to the model's `url()`. Some possible customizations could be:
//
// * Use `setTimeout` to batch rapid-fire updates into a single request.
// * Send up the models as XML instead of JSON.
// * Persist models via WebSockets instead of Ajax.
//
// Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
// as `POST`, with a `_method` parameter containing the true HTTP method,
// as well as all requests with the body as `application/x-www-form-urlencoded`
// instead of `application/json` with the model in a param named `model`.
// Useful when interfacing with server-side languages like **PHP** that make
// it difficult to read the body of `PUT` requests.
Backbone
.
sync
=
function
(
method
,
model
,
options
)
{
var
type
=
methodMap
[
method
];
// Default options, unless specified.
_
.
defaults
(
options
||
(
options
=
{}),
{
emulateHTTP
:
Backbone
.
emulateHTTP
,
emulateJSON
:
Backbone
.
emulateJSON
});
});
// Backbone.sync
// -------------
// Default JSON-request options.
// Override this function to change the manner in which Backbone persists
var
params
=
{
type
:
type
,
dataType
:
'
json
'
};
// models to the server. You will be passed the type of request, and the
// model in question. By default, makes a RESTful Ajax request
// Ensure that we have a URL.
// to the model's `url()`. Some possible customizations could be:
if
(
!
options
.
url
)
{
//
params
.
url
=
_
.
result
(
model
,
'
url
'
)
||
urlError
();
// * Use `setTimeout` to batch rapid-fire updates into a single request.
}
// * Send up the models as XML instead of JSON.
// * Persist models via WebSockets instead of Ajax.
// Ensure that we have the appropriate request data.
Backbone
.
sync
=
function
(
method
,
model
,
options
)
{
if
(
options
.
data
==
null
&&
model
&&
(
method
===
'
create
'
||
method
===
'
update
'
||
method
===
'
patch
'
))
{
options
||
(
options
=
{})
params
.
contentType
=
'
application/json
'
;
params
.
data
=
JSON
.
stringify
(
options
.
attrs
||
model
.
toJSON
(
options
));
}
// For older servers, emulate JSON by encoding the request into an HTML-form.
if
(
options
.
emulateJSON
)
{
params
.
contentType
=
'
application/x-www-form-urlencoded
'
;
params
.
data
=
params
.
data
?
{
model
:
params
.
data
}
:
{};
}
// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if
(
options
.
emulateHTTP
&&
(
type
===
'
PUT
'
||
type
===
'
DELETE
'
||
type
===
'
PATCH
'
))
{
params
.
type
=
'
POST
'
;
if
(
options
.
emulateJSON
)
params
.
data
.
_method
=
type
;
var
beforeSend
=
options
.
beforeSend
;
options
.
beforeSend
=
function
(
xhr
)
{
xhr
.
setRequestHeader
(
'
X-HTTP-Method-Override
'
,
type
);
if
(
beforeSend
)
return
beforeSend
.
apply
(
this
,
arguments
);
};
}
// Don't process data on a non-GET request.
if
(
params
.
type
!==
'
GET
'
&&
!
options
.
emulateJSON
)
{
params
.
processData
=
false
;
}
// Make the request, allowing the user to override any Ajax options.
var
xhr
=
options
.
xhr
=
Backbone
.
ajax
(
_
.
extend
(
params
,
options
));
model
.
trigger
(
'
request
'
,
model
,
xhr
,
options
);
return
xhr
;
};
// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
var
methodMap
=
{
'
create
'
:
'
POST
'
,
'
update
'
:
'
PUT
'
,
'
patch
'
:
'
PATCH
'
,
'
delete
'
:
'
DELETE
'
,
'
read
'
:
'
GET
'
};
// Set the default implementation of `Backbone.ajax` to proxy through to `$`.
// Override this if you'd like to use a different library.
Backbone
.
ajax
=
function
()
{
return
Backbone
.
$
.
ajax
.
apply
(
Backbone
.
$
,
arguments
);
};
// Backbone.Router
// ---------------
// Routers map faux-URLs to actions, and fire events when routes are
// matched. Creating a new one sets its `routes` hash, if not set statically.
var
Router
=
Backbone
.
Router
=
function
(
options
)
{
options
||
(
options
=
{});
if
(
options
.
routes
)
this
.
routes
=
options
.
routes
;
this
.
_bindRoutes
();
this
.
initialize
.
apply
(
this
,
arguments
);
};
// Cached regular expressions for matching named param parts and splatted
// parts of route strings.
var
optionalParam
=
/
\((
.*
?)\)
/g
;
var
namedParam
=
/
(\(\?)?
:
\w
+/g
;
var
splatParam
=
/
\*\w
+/g
;
var
escapeRegExp
=
/
[\-
{}
\[\]
+?.,
\\\^
$|#
\s]
/g
;
var
isRegExp
=
function
(
value
)
{
return
value
?
(
typeof
value
===
'
object
'
&&
toString
.
call
(
value
)
===
'
[object RegExp]
'
)
:
false
;
};
// Set up all inheritable **Backbone.Router** properties and methods.
var
type
=
methodMap
[
method
];
_
.
extend
(
Router
.
prototype
,
Events
,
{
// Initialize is an empty function by default. Override it with your own
// Default JSON-request options.
// initialization logic.
var
params
=
{
type
:
type
,
dataType
:
'
json
'
};
initialize
:
function
(){},
// Manually bind a single named route to a callback. For example:
// Ensure that we have a URL.
//
if
(
!
options
.
url
)
{
// this.route('search/:query/p:num', 'search', function(query, num) {
params
.
url
=
_
.
result
(
model
,
'
url
'
)
||
urlError
();
// ...
// });
//
route
:
function
(
route
,
name
,
callback
)
{
if
(
!
isRegExp
(
route
))
route
=
this
.
_routeToRegExp
(
route
);
if
(
typeof
name
===
'
function
'
)
{
callback
=
name
;
name
=
''
;
}
}
if
(
!
callback
)
callback
=
this
[
name
];
var
router
=
this
;
Backbone
.
history
.
route
(
route
,
function
(
fragment
)
{
var
args
=
router
.
_extractParameters
(
route
,
fragment
);
callback
&&
callback
.
apply
(
router
,
args
);
router
.
trigger
.
apply
(
router
,
[
'
route:
'
+
name
].
concat
(
args
));
router
.
trigger
(
'
route
'
,
name
,
args
);
Backbone
.
history
.
trigger
(
'
route
'
,
router
,
name
,
args
);
});
return
this
;
},
// Simple proxy to `Backbone.history` to save a fragment into the history.
// Ensure that we have the appropriate request data.
navigate
:
function
(
fragment
,
options
)
{
if
(
options
.
data
==
null
&&
model
&&
(
method
===
'
create
'
||
method
===
'
update
'
||
method
===
'
patch
'
))
{
Backbone
.
history
.
navigate
(
fragment
,
options
);
params
.
contentType
=
'
application/json
'
;
return
this
;
params
.
data
=
JSON
.
stringify
(
options
.
attrs
||
model
.
toJSON
(
options
));
},
// Bind all defined routes to `Backbone.history`. We have to reverse the
// order of the routes here to support behavior where the most general
// routes can be defined at the bottom of the route map.
_bindRoutes
:
function
()
{
if
(
!
this
.
routes
)
return
;
this
.
routes
=
_
.
result
(
this
,
'
routes
'
);
var
route
,
routes
=
Object
.
keys
(
this
.
routes
);
while
((
route
=
routes
.
pop
())
!=
null
)
{
this
.
route
(
route
,
this
.
routes
[
route
]);
}
}
},
// Convert a route string into a regular expression, suitable for matching
// Don't process data on a non-GET request.
// against the current location hash.
if
(
params
.
type
!==
'
GET
'
)
{
_routeToRegExp
:
function
(
route
)
{
params
.
processData
=
false
;
route
=
route
.
replace
(
escapeRegExp
,
'
\\
$&
'
)
}
.
replace
(
optionalParam
,
'
(?:$1)?
'
)
.
replace
(
namedParam
,
function
(
match
,
optional
)
{
return
optional
?
match
:
'
([^
\
/]+)
'
;
})
.
replace
(
splatParam
,
'
(.*?)
'
);
return
new
RegExp
(
'
^
'
+
route
+
'
$
'
);
},
// Given a route, and a URL fragment that it matches, return the array of
// Make the request, allowing the user to override any Ajax options.
// extracted decoded parameters. Empty or unmatched parameters will be
var
xhr
=
options
.
xhr
=
Backbone
.
ajax
(
_
.
extend
(
params
,
options
));
// treated as `null` to normalize cross-browser behavior.
model
.
trigger
(
'
request
'
,
model
,
xhr
,
options
);
_extractParameters
:
function
(
route
,
fragment
)
{
return
xhr
;
var
params
=
route
.
exec
(
fragment
).
slice
(
1
);
};
return
params
.
map
(
function
(
param
)
{
return
param
?
decodeURIComponent
(
param
)
:
null
;
});
}
});
// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
// Backbone.History
var
methodMap
=
{
// ----------------
'
create
'
:
'
POST
'
,
'
update
'
:
'
PUT
'
,
// Handles cross-browser history management, based on either
'
patch
'
:
'
PATCH
'
,
// [pushState](http://diveintohtml5.info/history.html) and real URLs, or
'
delete
'
:
'
DELETE
'
,
// [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
'
read
'
:
'
GET
'
// and URL fragments.
};
var
History
=
Backbone
.
History
=
function
()
{
this
.
handlers
=
[];
this
.
checkUrl
=
this
.
checkUrl
.
bind
(
this
);
// Ensure that `History` can be used outside of the browser.
if
(
typeof
window
!==
'
undefined
'
)
{
this
.
location
=
window
.
location
;
this
.
history
=
window
.
history
;
}
};
// Cached regex for stripping a leading hash/slash and trailing space.
// Set the default implementation of `Backbone.ajax` to proxy through to `$`.
var
routeStripper
=
/^
[
#
\/]
|
\s
+$/g
;
// Override this if you'd like to use a different library.
Backbone
.
ajax
=
function
()
{
if
(
!
Backbone
.
$
)
throw
new
Error
(
'
You must either include jQuery or override Backbone.ajax (Google Backbone.NativeAjax)
'
);
return
Backbone
.
$
.
ajax
.
apply
(
Backbone
.
$
,
arguments
);
};
// Cached regex for stripping leading and trailing slashes.
// Backbone.Router
var
rootStripper
=
/^
\/
+|
\/
+$/g
;
// ---------------
// Cached regex for removing a trailing slash.
// Routers map faux-URLs to actions, and fire events when routes are
var
trailingSlash
=
/
\/
$/
;
// matched. Creating a new one sets its `routes` hash, if not set statically.
var
Router
=
Backbone
.
Router
=
function
(
options
)
{
options
||
(
options
=
{});
if
(
options
.
routes
)
this
.
routes
=
options
.
routes
;
this
.
_bindRoutes
();
this
.
initialize
.
apply
(
this
,
arguments
);
};
// Cached regex for stripping urls of hash and query.
// Cached regular expressions for matching named param parts and splatted
var
pathStripper
=
/
[
#
]
.*$/
;
// parts of route strings.
var
optionalParam
=
/
\((
.*
?)\)
/g
;
var
namedParam
=
/
(\(\?)?
:
\w
+/g
;
var
splatParam
=
/
\*\w
+/g
;
var
escapeRegExp
=
/
[\-
{}
\[\]
+?.,
\\\^
$|#
\s]
/g
;
// Has the history handling already been started?
var
isRegExp
=
function
(
value
)
{
History
.
started
=
false
;
return
value
?
(
typeof
value
===
'
object
'
&&
toString
.
call
(
value
)
===
'
[object RegExp]
'
)
:
false
;
};
// Set up all inheritable **Backbone.History** properties and methods.
// Set up all inheritable **Backbone.Router** properties and methods.
_
.
extend
(
History
.
prototype
,
Events
,
{
_
.
extend
(
Router
.
prototype
,
Events
,
{
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize
:
function
(){},
// Manually bind a single named route to a callback. For example:
//
// this.route('search/:query/p:num', 'search', function(query, num) {
// ...
// });
//
route
:
function
(
route
,
name
,
callback
)
{
if
(
!
isRegExp
(
route
))
route
=
this
.
_routeToRegExp
(
route
);
if
(
typeof
name
===
'
function
'
)
{
callback
=
name
;
name
=
''
;
}
if
(
!
callback
)
callback
=
this
[
name
];
var
router
=
this
;
Backbone
.
history
.
route
(
route
,
function
(
fragment
)
{
var
args
=
router
.
_extractParameters
(
route
,
fragment
);
router
.
execute
(
callback
,
args
);
router
.
trigger
.
apply
(
router
,
[
'
route:
'
+
name
].
concat
(
args
));
router
.
trigger
(
'
route
'
,
name
,
args
);
Backbone
.
history
.
trigger
(
'
route
'
,
router
,
name
,
args
);
});
return
this
;
},
// Gets the true hash value. Cannot use location.hash directly due to bug
// Execute a route handler with the provided parameters. This is an
// in Firefox where location.hash will always be decoded.
// excellent place to do pre-route setup or post-route cleanup.
getHash
:
function
(
window
)
{
execute
:
function
(
callback
,
args
)
{
var
match
=
(
window
||
this
).
location
.
href
.
match
(
/#
(
.*
)
$/
);
if
(
callback
)
callback
.
apply
(
this
,
args
);
return
match
?
match
[
1
]
:
''
;
},
},
// Get the cross-browser normalized URL fragment, either from the URL,
// Simple proxy to `Backbone.history` to save a fragment into the history.
// the hash, or the override.
navigate
:
function
(
fragment
,
options
)
{
getFragment
:
function
(
fragment
)
{
Backbone
.
history
.
navigate
(
fragment
,
options
);
if
(
fragment
==
null
)
{
return
this
;
if
(
this
.
_wantsPushState
||
!
this
.
_wantsHashChange
)
{
},
// CHANGED: Make fragment include query string.
fragment
=
this
.
location
.
pathname
+
this
.
location
.
search
;
// Bind all defined routes to `Backbone.history`. We have to reverse the
var
root
=
this
.
root
.
replace
(
trailingSlash
,
''
);
// order of the routes here to support behavior where the most general
if
(
!
fragment
.
indexOf
(
root
))
fragment
=
fragment
.
slice
(
root
.
length
);
// routes can be defined at the bottom of the route map.
}
else
{
_bindRoutes
:
function
()
{
fragment
=
this
.
getHash
();
if
(
!
this
.
routes
)
return
;
this
.
routes
=
_
.
result
(
this
,
'
routes
'
);
var
route
,
routes
=
Object
.
keys
(
this
.
routes
);
while
((
route
=
routes
.
pop
())
!=
null
)
{
this
.
route
(
route
,
this
.
routes
[
route
]);
}
}
}
},
return
fragment
.
replace
(
routeStripper
,
''
);
},
// Convert a route string into a regular expression, suitable for matching
// against the current location hash.
// Start the hash change handling, returning `true` if the current URL matches
_routeToRegExp
:
function
(
route
)
{
// an existing route, and `false` otherwise.
route
=
route
.
replace
(
escapeRegExp
,
'
\\
$&
'
)
start
:
function
(
options
)
{
.
replace
(
optionalParam
,
'
(?:$1)?
'
)
if
(
History
.
started
)
throw
new
Error
(
"
Backbone.history has already been started
"
);
.
replace
(
namedParam
,
function
(
match
,
optional
)
{
History
.
started
=
true
;
return
optional
?
match
:
'
([^/?]+)
'
;
})
// Figure out the initial configuration.
.
replace
(
splatParam
,
'
([^?]*?)
'
);
// Is pushState desired or should we use hashchange only?
return
new
RegExp
(
'
^
'
+
route
+
'
(?:
\\
?([
\\
s
\\
S]*))?$
'
);
this
.
options
=
_
.
extend
({
root
:
'
/
'
},
this
.
options
,
options
);
},
this
.
root
=
this
.
options
.
root
;
this
.
_wantsHashChange
=
this
.
options
.
hashChange
!==
false
;
// Given a route, and a URL fragment that it matches, return the array of
this
.
_wantsPushState
=
!!
this
.
options
.
pushState
;
// extracted decoded parameters. Empty or unmatched parameters will be
var
fragment
=
this
.
getFragment
();
// treated as `null` to normalize cross-browser behavior.
_extractParameters
:
function
(
route
,
fragment
)
{
// Normalize root to always include a leading and trailing slash.
var
params
=
route
.
exec
(
fragment
).
slice
(
1
);
this
.
root
=
(
'
/
'
+
this
.
root
+
'
/
'
).
replace
(
rootStripper
,
'
/
'
);
return
params
.
map
(
function
(
param
,
i
)
{
// Don't decode the search params.
// Depending on whether we're using pushState or hashes, determine how we
if
(
i
===
params
.
length
-
1
)
return
param
||
null
;
// check the URL state.
return
param
?
decodeURIComponent
(
param
)
:
null
;
if
(
this
.
_wantsPushState
)
{
});
window
.
addEventListener
(
'
popstate
'
,
this
.
checkUrl
,
false
);
}
else
if
(
this
.
_wantsHashChange
)
{
window
.
addEventListener
(
'
hashchange
'
,
this
.
checkUrl
,
false
);
}
}
// Determine if we need to change the base url, for a pushState link
});
// opened by a non-pushState browser.
this
.
fragment
=
fragment
;
var
loc
=
this
.
location
;
var
atRoot
=
loc
.
pathname
.
replace
(
/
[^\/]
$/
,
'
$&/
'
)
===
this
.
root
;
// Transition from hashChange to pushState or vice versa if both are
// requested.
if
(
this
.
_wantsHashChange
&&
this
.
_wantsPushState
)
{
// If we've started out with a hash-based route, but we're currently
// in a browser where it could be `pushState`-based instead...
if
(
atRoot
&&
loc
.
hash
)
{
this
.
fragment
=
this
.
getHash
().
replace
(
routeStripper
,
''
);
// CHANGED: It's no longer needed to add loc.search at the end,
// as query params have been already included into @fragment
this
.
history
.
replaceState
({},
document
.
title
,
this
.
root
+
this
.
fragment
);
}
// Backbone.History
// ----------------
// Handles cross-browser history management, based on either
// [pushState](http://diveintohtml5.info/history.html) and real URLs, or
// [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
// and URL fragments.
var
History
=
Backbone
.
History
=
function
()
{
this
.
handlers
=
[];
this
.
checkUrl
=
this
.
checkUrl
.
bind
(
this
);
// Ensure that `History` can be used outside of the browser.
if
(
typeof
window
!==
'
undefined
'
)
{
this
.
location
=
window
.
location
;
this
.
history
=
window
.
history
;
}
}
};
if
(
!
this
.
options
.
silent
)
return
this
.
loadUrl
();
// Cached regex for stripping a leading hash/slash and trailing space.
},
var
routeStripper
=
/^
[
#
\/]
|
\s
+$/g
;
// Cached regex for stripping leading and trailing slashes.
var
rootStripper
=
/^
\/
+|
\/
+$/g
;
// Cached regex for removing a trailing slash.
var
trailingSlash
=
/
\/
$/
;
// Cached regex for stripping urls of hash and query.
var
pathStripper
=
/
[
#
]
.*$/
;
// Has the history handling already been started?
History
.
started
=
false
;
// Set up all inheritable **Backbone.History** properties and methods.
_
.
extend
(
History
.
prototype
,
Events
,
{
// 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
)
{
var
match
=
(
window
||
this
).
location
.
href
.
match
(
/#
(
.*
)
$/
);
return
match
?
match
[
1
]
:
''
;
},
// Get the cross-browser normalized URL fragment, either from the URL,
// the hash, or the override.
getFragment
:
function
(
fragment
,
forcePushState
)
{
if
(
fragment
==
null
)
{
if
(
this
.
_wantsPushState
||
!
this
.
_wantsHashChange
)
{
fragment
=
decodeURI
(
this
.
location
.
pathname
+
this
.
location
.
search
);
var
root
=
this
.
root
.
replace
(
trailingSlash
,
''
);
if
(
!
fragment
.
indexOf
(
root
))
fragment
=
fragment
.
slice
(
root
.
length
);
}
else
{
fragment
=
this
.
getHash
();
}
}
return
fragment
.
replace
(
routeStripper
,
''
);
},
// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
start
:
function
(
options
)
{
if
(
History
.
started
)
throw
new
Error
(
"
Backbone.history has already been started
"
);
History
.
started
=
true
;
// Figure out the initial configuration.
// Is pushState desired or should we use hashchange only?
this
.
options
=
_
.
extend
({
root
:
'
/
'
},
this
.
options
,
options
);
this
.
root
=
this
.
options
.
root
;
this
.
_wantsHashChange
=
this
.
options
.
hashChange
!==
false
;
this
.
_wantsPushState
=
!!
this
.
options
.
pushState
;
var
fragment
=
this
.
getFragment
();
// Normalize root to always include a leading and trailing slash.
this
.
root
=
(
'
/
'
+
this
.
root
+
'
/
'
).
replace
(
rootStripper
,
'
/
'
);
// Depending on whether we're using pushState or hashes, determine how we
// check the URL state.
if
(
this
.
_wantsPushState
)
{
window
.
addEventListener
(
'
popstate
'
,
this
.
checkUrl
,
false
);
}
else
if
(
this
.
_wantsHashChange
)
{
window
.
addEventListener
(
'
hashchange
'
,
this
.
checkUrl
,
false
);
}
// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
// Determine if we need to change the base url, for a pushState link
// but possibly useful for unit testing Routers.
// opened by a non-pushState browser.
stop
:
function
()
{
this
.
fragment
=
fragment
;
window
.
removeEventListener
(
'
popstate
'
,
this
.
checkUrl
);
var
loc
=
this
.
location
;
window
.
removeEventListener
(
'
hashchange
'
,
this
.
checkUrl
);
History
.
started
=
false
;
},
// Add a route to be tested when the fragment changes. Routes added later
// Transition from hashChange to pushState or vice versa if both are
// may override previous routes.
// requested.
route
:
function
(
route
,
callback
)
{
if
(
this
.
_wantsHashChange
&&
this
.
_wantsPushState
)
{
this
.
handlers
.
unshift
({
route
:
route
,
callback
:
callback
});
},
// Checks the current URL to see if it has changed, and if it has,
// If we've started out with a hash-based route, but we're currently
// calls `loadUrl`.
// in a browser where it could be `pushState`-based instead...
checkUrl
:
function
(
e
)
{
if
(
this
.
atRoot
()
&&
loc
.
hash
)
{
var
current
=
this
.
getFragment
();
this
.
fragment
=
this
.
getHash
().
replace
(
routeStripper
,
''
);
if
(
current
===
this
.
fragment
)
return
false
;
this
.
history
.
replaceState
({},
document
.
title
,
this
.
root
+
this
.
fragment
);
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
)
{
fragment
=
this
.
fragment
=
this
.
getFragment
(
fragment
);
return
this
.
handlers
.
some
(
function
(
handler
)
{
if
(
handler
.
route
.
test
(
fragment
))
{
handler
.
callback
(
fragment
);
return
true
;
}
}
});
},
// Save a fragment into the hash history, or replace the URL state if the
if
(
!
this
.
options
.
silent
)
return
this
.
loadUrl
();
// 'replace' option is passed. You are responsible for properly URL-encoding
},
// the fragment in advance.
//
// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
// The options object can contain `trigger: true` if you wish to have the
// but possibly useful for unit testing Routers.
// route callback be fired (not usually desirable), or `replace: true`, if
stop
:
function
()
{
// you wish to modify the current URL without adding an entry to the history.
window
.
removeEventListener
(
'
popstate
'
,
this
.
checkUrl
);
navigate
:
function
(
fragment
,
options
)
{
window
.
removeEventListener
(
'
hashchange
'
,
this
.
checkUrl
);
if
(
!
History
.
started
)
return
false
;
History
.
started
=
false
;
if
(
!
options
||
options
===
true
)
options
=
{
trigger
:
!!
options
};
},
var
url
=
this
.
root
+
(
fragment
=
this
.
getFragment
(
fragment
||
''
));
// Add a route to be tested when the fragment changes. Routes added later
// may override previous routes.
// Strip the fragment of the query and hash for matching.
route
:
function
(
route
,
callback
)
{
fragment
=
fragment
.
replace
(
pathStripper
,
''
);
this
.
handlers
.
unshift
({
route
:
route
,
callback
:
callback
});
},
if
(
this
.
fragment
===
fragment
)
return
;
this
.
fragment
=
fragment
;
// Checks the current URL to see if it has changed, and if it has,
// calls `loadUrl`.
// Don't include a trailing slash on the root.
checkUrl
:
function
()
{
if
(
fragment
===
''
&&
url
!==
'
/
'
)
url
=
url
.
slice
(
0
,
-
1
);
var
current
=
this
.
getFragment
();
if
(
current
===
this
.
fragment
)
return
false
;
// If we're using pushState we use it to set the fragment as a real URL.
this
.
loadUrl
();
if
(
this
.
_wantsPushState
)
{
},
this
.
history
[
options
.
replace
?
'
replaceState
'
:
'
pushState
'
]({},
document
.
title
,
url
);
// Attempt to load the current URL fragment. If a route succeeds with a
// If hash changes haven't been explicitly disabled, update the hash
// match, returns `true`. If no defined routes matches the fragment,
// fragment to store history.
// returns `false`.
}
else
if
(
this
.
_wantsHashChange
)
{
loadUrl
:
function
(
fragment
)
{
this
.
_updateHash
(
this
.
location
,
fragment
,
options
.
replace
);
fragment
=
this
.
fragment
=
this
.
getFragment
(
fragment
);
// If you've told us that you explicitly don't want fallback hashchange-
return
this
.
handlers
.
some
(
function
(
handler
)
{
// based history, then `navigate` becomes a page refresh.
if
(
handler
.
route
.
test
(
fragment
))
{
}
else
{
handler
.
callback
(
fragment
);
return
this
.
location
.
assign
(
url
);
return
true
;
}
});
},
// Save a fragment into the hash history, or replace the URL state if the
// 'replace' option is passed. You are responsible for properly URL-encoding
// the fragment in advance.
//
// The options object can contain `trigger: true` if you wish to have the
// route callback be fired (not usually desirable), or `replace: true`, if
// 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
};
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
;
// Don't include a trailing slash on the root.
if
(
fragment
===
''
&&
url
!==
'
/
'
)
url
=
url
.
slice
(
0
,
-
1
);
// If we're using pushState we use it to set the fragment as a real URL.
if
(
this
.
_wantsPushState
)
{
this
.
history
[
options
.
replace
?
'
replaceState
'
:
'
pushState
'
]({},
document
.
title
,
url
);
// If hash changes haven't been explicitly disabled, update the hash
// fragment to store history.
}
else
if
(
this
.
_wantsHashChange
)
{
this
.
_updateHash
(
this
.
location
,
fragment
,
options
.
replace
);
// If you've told us that you explicitly don't want fallback hashchange-
// based history, then `navigate` becomes a page refresh.
}
else
{
return
this
.
location
.
assign
(
url
);
}
if
(
options
.
trigger
)
return
this
.
loadUrl
(
fragment
);
},
// Update the hash location, either replacing the current entry, or adding
// a new one to the browser history.
_updateHash
:
function
(
location
,
fragment
,
replace
)
{
if
(
replace
)
{
var
href
=
location
.
href
.
replace
(
/
(
javascript:|#
)
.*$/
,
''
);
location
.
replace
(
href
+
'
#
'
+
fragment
);
}
else
{
// Some browsers require that `hash` contains a leading #.
location
.
hash
=
'
#
'
+
fragment
;
}
}
}
if
(
options
.
trigger
)
return
this
.
loadUrl
(
fragment
);
},
// Update the hash location, either replacing the current entry, or adding
});
// a new one to the browser history.
_updateHash
:
function
(
location
,
fragment
,
replace
)
{
if
(
replace
)
{
var
href
=
location
.
href
.
replace
(
/
(
javascript:|#
)
.*$/
,
''
);
location
.
replace
(
href
+
'
#
'
+
fragment
);
}
else
{
// Some browsers require that `hash` contains a leading #.
location
.
hash
=
'
#
'
+
fragment
;
}
}
});
// !!!
// !!!
// Init.
// Init.
Model
.
extend
=
Collection
.
extend
=
Router
.
extend
=
View
.
extend
=
History
.
extend
=
Backbone
.
extend
;
[
'
Model
'
,
'
Collection
'
,
'
Router
'
,
'
View
'
,
'
History
'
].
forEach
(
function
(
name
)
{
var
item
=
Backbone
[
name
];
if
(
item
)
item
.
extend
=
Backbone
.
extend
;
});
// Allow the `Backbone` object to serve as a global event bus, for folks who
// Allow the `Backbone` object to serve as a global event bus, for folks who
// want global "pubsub" in a convenient place.
// want global "pubsub" in a convenient place.
_
.
extend
(
Backbone
,
Events
);
_
.
extend
(
Backbone
,
Events
);
// Create the default Backbone.history.
// Create the default Backbone.history
if the History module is included
.
Backbone
.
history
=
new
History
;
if
(
History
)
Backbone
.
history
=
new
History
()
;
return
Backbone
;
return
Backbone
;
});
});
examples/exoskeleton/package.json
View file @
28b669e4
{
{
"private"
:
true
,
"private"
:
true
,
"dependencies"
:
{
"dependencies"
:
{
"exoskeleton"
:
"^0.
3
.0"
,
"exoskeleton"
:
"^0.
7
.0"
,
"backbone.localstorage"
:
"^1.1.16"
,
"backbone.localstorage"
:
"^1.1.16"
,
"backbone.nativeview"
:
"^0.3.2"
,
"todomvc-app-css"
:
"^1.0.0"
,
"todomvc-app-css"
:
"^1.0.0"
,
"todomvc-common"
:
"^1.0.1"
,
"todomvc-common"
:
"^1.0.1"
,
"microtemplates"
:
"^0.1.0"
"microtemplates"
:
"^0.1.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