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
Show 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/*
node_modules/backbone.localstorage/*
!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/index.css
...
...
examples/exoskeleton/index.html
View file @
28b669e4
...
...
@@ -53,6 +53,8 @@
<script
src=
"node_modules/exoskeleton/exoskeleton.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.nativeview/backbone.nativeview.js"
></script>
<script>
Backbone
.
View
=
Backbone
.
NativeView
;
</script>
<script
src=
"js/models/todo.js"
></script>
<script
src=
"js/collections/todos.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 || {};
el
.
style
.
display
=
toggle
?
''
:
'
none
'
;
};
var
matchesSelector
=
function
(
node
,
selector
)
{
return
[].
some
.
call
(
document
.
querySelectorAll
(
selector
),
function
(
el
)
{
return
el
===
node
;
});
};
// The Application
// ---------------
...
...
@@ -32,10 +38,10 @@ var app = app || {};
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
initialize
:
function
()
{
this
.
allCheckbox
=
this
.
find
(
'
#toggle-all
'
);
this
.
input
=
this
.
find
(
'
#new-todo
'
);
this
.
footer
=
this
.
find
(
'
#footer
'
);
this
.
main
=
this
.
find
(
'
#main
'
);
this
.
allCheckbox
=
this
.
$
(
'
#toggle-all
'
).
item
(
0
);
this
.
input
=
this
.
$
(
'
#new-todo
'
).
item
(
0
);
this
.
footer
=
this
.
$
(
'
#footer
'
).
item
(
0
);
this
.
main
=
this
.
$
(
'
#main
'
).
item
(
0
);
this
.
listenTo
(
app
.
todos
,
'
add
'
,
this
.
addOne
);
this
.
listenTo
(
app
.
todos
,
'
reset
'
,
this
.
addAll
);
...
...
@@ -66,9 +72,9 @@ var app = app || {};
remaining
:
remaining
});
this
.
findAll
(
'
#filters li a
'
).
forEach
(
function
(
el
)
{
[].
forEach
.
call
(
this
.
$
(
'
#filters li a
'
),
function
(
el
)
{
el
.
classList
.
remove
(
'
selected
'
);
if
(
Backbone
.
utils
.
matchesSelector
(
el
,
selector
))
{
if
(
matchesSelector
(
el
,
selector
))
{
el
.
classList
.
add
(
'
selected
'
);
}
});
...
...
@@ -90,7 +96,7 @@ var app = app || {};
// Add all items in the **Todos** collection at once.
addAll
:
function
()
{
this
.
find
(
'
#todo-list
'
).
innerHTML
=
''
;
this
.
$
(
'
#todo-list
'
).
item
(
0
).
innerHTML
=
''
;
app
.
todos
.
forEach
(
this
.
addOne
,
this
);
},
...
...
examples/exoskeleton/js/views/todo-view.js
View file @
28b669e4
...
...
@@ -41,7 +41,7 @@ var app = app || {};
var
method
=
this
.
model
.
get
(
'
completed
'
)
?
'
add
'
:
'
remove
'
;
this
.
el
.
classList
[
method
](
'
completed
'
);
this
.
toggleVisible
();
this
.
input
=
this
.
find
(
'
.edit
'
);
this
.
input
=
this
.
$
(
'
.edit
'
).
item
(
0
);
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>
* Based on Backbone.js
* (c) 2010-2013 Jeremy Ashkenas, DocumentCloud
* 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
)
{
define
([
'
underscore
'
,
'
jquery
'
],
factory
);
}
else
if
(
typeof
exports
===
'
object
'
)
{
factory
(
require
(
'
underscore
'
),
require
(
'
jquery
'
));
define
([
'
underscore
'
,
'
jquery
'
,
'
exports
'
],
function
(
_
,
$
,
exports
)
{
root
.
Backbone
=
root
.
Exoskeleton
=
factory
(
root
,
exports
,
_
,
$
);
});
}
else
if
(
typeof
exports
!==
'
undefined
'
)
{
var
_
,
$
;
try
{
_
=
require
(
'
underscore
'
);
}
catch
(
e
)
{
}
try
{
$
=
require
(
'
jquery
'
);
}
catch
(
e
)
{
}
factory
(
root
,
exports
,
_
,
$
);
}
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
'
;
// 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
// restored later on, if `noConflict` is used.
var
previousBackbone
=
root
.
Backbone
;
// 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
=
{};
}
var
previousExoskeleton
=
root
.
Exoskeleton
;
// Underscore replacement.
var
utils
=
_
=
Backbone
.
utils
=
_
||
{}
;
var
utils
=
Backbone
.
utils
=
_
=
(
_
||
{})
;
// Hold onto a local reference to `$`. Can be changed at any point.
Backbone
.
$
=
$
;
...
...
@@ -48,7 +43,7 @@
var
array
=
[];
var
push
=
array
.
push
;
var
slice
=
array
.
slice
;
var
splice
=
array
.
splice
;
var
toString
=
({}).
toString
;
// Current version of the library. Keep in sync with `package.json`.
// Backbone.VERSION = '1.0.0';
...
...
@@ -57,20 +52,10 @@
// to its previous owner. Returns a reference to this Backbone object.
Backbone
.
noConflict
=
function
()
{
root
.
Backbone
=
previousBackbone
;
root
.
Exoskeleton
=
previousExoskeleton
;
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
// -------
...
...
@@ -84,7 +69,7 @@
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent's constructor.
if
(
protoProps
&&
hasOwnProperty
.
call
(
protoProps
,
'
constructor
'
))
{
if
(
protoProps
&&
_
.
has
(
protoProps
,
'
constructor
'
))
{
child
=
protoProps
.
constructor
;
}
else
{
child
=
function
(){
return
parent
.
apply
(
this
,
arguments
);
};
...
...
@@ -123,53 +108,47 @@
model
.
trigger
(
'
error
'
,
model
,
resp
,
options
);
};
};
utils
.
result
=
function
result
(
object
,
property
)
{
// Checker for utility methods. Useful for custom builds.
var
utilExists
=
function
(
method
)
{
return
typeof
_
[
method
]
===
'
function
'
;
};
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
)
{
[].
slice
.
call
(
arguments
,
1
).
forEach
(
function
(
item
)
{
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
.
extend
=
function
extend
(
obj
)
{
[].
slice
.
call
(
arguments
,
1
).
forEach
(
function
(
item
)
{
utils
.
extend
=
function
extend
(
obj
)
{
slice
.
call
(
arguments
,
1
).
forEach
(
function
(
item
)
{
for
(
var
key
in
item
)
obj
[
key
]
=
item
[
key
];
});
return
obj
;
};
};
var
htmlEscapes
=
{
var
htmlEscapes
=
{
'
&
'
:
'
&
'
,
'
<
'
:
'
<
'
,
'
>
'
:
'
>
'
,
'
"
'
:
'
"
'
,
"
'
"
:
'
'
'
};
};
utils
.
escape
=
function
escape
(
string
)
{
utils
.
escape
=
function
escape
(
string
)
{
return
string
==
null
?
''
:
String
(
string
).
replace
(
/
[
&<>"'
]
/g
,
function
(
match
)
{
return
htmlEscapes
[
match
];
});
};
utils
.
sortedIndex
=
function
sortedIndex
(
array
,
obj
,
iterator
,
context
)
{
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
.
sortBy
=
function
(
obj
,
value
,
context
)
{
var
iterator
=
typeof
value
===
'
function
'
?
value
:
function
(
obj
){
return
obj
[
value
];
};
return
obj
.
map
(
function
(
value
,
index
,
list
)
{
...
...
@@ -191,17 +170,21 @@ utils.sortBy = function(obj, value, context) {
.
map
(
function
(
item
)
{
return
item
.
value
;
});
};
};
/** Used to generate unique IDs */
var
idCounter
=
0
;
/** Used to generate unique IDs */
var
idCounter
=
0
;
utils
.
uniqueId
=
function
uniqueId
(
prefix
)
{
utils
.
uniqueId
=
function
uniqueId
(
prefix
)
{
var
id
=
++
idCounter
+
''
;
return
prefix
?
prefix
+
id
:
id
;
};
};
utils
.
has
=
function
(
obj
,
key
)
{
return
Object
.
hasOwnProperty
.
call
(
obj
,
key
);
};
var
eq
=
function
(
a
,
b
,
aStack
,
bStack
)
{
var
eq
=
function
(
a
,
b
,
aStack
,
bStack
)
{
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if
(
a
===
b
)
return
a
!==
0
||
1
/
a
==
1
/
b
;
...
...
@@ -257,10 +240,10 @@ var eq = function(a, b, aStack, bStack) {
bStack
.
push
(
b
);
var
size
=
0
,
result
=
true
;
// 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
--
)
{
...
...
@@ -270,17 +253,17 @@ var eq = function(a, b, aStack, bStack) {
}
else
{
// Deep compare objects.
for
(
var
key
in
a
)
{
if
(
hasOwnProperty
.
call
(
a
,
key
))
{
if
(
_
.
has
(
a
,
key
))
{
// Count the expected number of properties.
size
++
;
// Deep compare each member.
if
(
!
(
result
=
hasOwnProperty
.
call
(
b
,
key
)
&&
eq
(
a
[
key
],
b
[
key
],
aStack
,
bStack
)))
break
;
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
(
hasOwnProperty
.
call
(
b
,
key
)
&&
!
(
size
--
))
break
;
if
(
_
.
has
(
b
,
key
)
&&
!
(
size
--
))
break
;
}
result
=
!
size
;
}
...
...
@@ -289,29 +272,13 @@ var eq = function(a, b, aStack, bStack) {
aStack
.
pop
();
bStack
.
pop
();
return
result
;
};
};
// Perform a deep comparison to check if two objects are equal.
utils
.
isEqual
=
function
(
a
,
b
)
{
// Perform a deep comparison to check if two objects are equal.
utils
.
isEqual
=
function
(
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
// ---------------
...
...
@@ -343,7 +310,6 @@ var Events = Backbone.Events = {
if
(
!
eventsApi
(
this
,
'
once
'
,
name
,
[
callback
,
context
])
||
!
callback
)
return
this
;
var
self
=
this
;
var
ran
;
var
once
=
function
()
{
if
(
ran
)
return
;
ran
=
true
;
...
...
@@ -362,10 +328,9 @@ var Events = Backbone.Events = {
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
]
:
Object
.
keys
(
this
.
_events
);
for
(
i
=
0
,
l
=
names
.
length
;
i
<
l
;
i
++
)
{
name
=
names
[
i
];
...
...
@@ -459,7 +424,7 @@ var triggerEvents = function(events, args) {
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
;
}
};
...
...
@@ -483,6 +448,11 @@ Object.keys(listenMethods).forEach(function(method) {
// Aliases for backwards compatibility.
Events
.
bind
=
Events
.
on
;
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
// --------------
...
...
@@ -525,7 +495,7 @@ _.extend(Model.prototype, Events, {
// Return a copy of the model's `attributes` object.
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
...
...
@@ -600,7 +570,7 @@ _.extend(Model.prototype, Events, {
// 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
);
}
...
...
@@ -611,6 +581,7 @@ _.extend(Model.prototype, Events, {
if
(
changing
)
return
this
;
if
(
!
silent
)
{
while
(
this
.
_pending
)
{
options
=
this
.
_pending
;
this
.
_pending
=
false
;
this
.
trigger
(
'
change
'
,
this
,
options
);
}
...
...
@@ -637,7 +608,7 @@ _.extend(Model.prototype, Events, {
// If you specify an attribute name, determine if that attribute has changed.
hasChanged
:
function
(
attr
)
{
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
...
...
@@ -778,9 +749,12 @@ _.extend(Model.prototype, Events, {
// 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
...
...
@@ -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.
isNew
:
function
()
{
return
this
.
id
==
null
;
return
!
this
.
has
(
this
.
idAttribute
)
;
},
// Check if the model is currently in a valid state.
...
...
@@ -822,7 +796,7 @@ if (_.keys) {
var
modelMethods
=
[
'
keys
'
,
'
values
'
,
'
pairs
'
,
'
invert
'
,
'
pick
'
,
'
omit
'
];
// 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
()
{
var
args
=
slice
.
call
(
arguments
);
args
.
unshift
(
this
.
attributes
);
...
...
@@ -830,6 +804,7 @@ if (_.keys) {
};
});
}
// Backbone.Collection
// -------------------
...
...
@@ -901,7 +876,7 @@ _.extend(Collection.prototype, Events, {
options
.
index
=
index
;
model
.
trigger
(
'
remove
'
,
model
,
this
,
options
);
}
this
.
_removeReference
(
model
);
this
.
_removeReference
(
model
,
options
);
}
return
singular
?
models
[
0
]
:
models
;
},
...
...
@@ -927,11 +902,11 @@ _.extend(Collection.prototype, Events, {
// Turn bare objects into model references, and prevent invalid models
// from being added.
for
(
i
=
0
,
l
=
models
.
length
;
i
<
l
;
i
++
)
{
attrs
=
models
[
i
];
attrs
=
models
[
i
]
||
{}
;
if
(
attrs
instanceof
Model
)
{
id
=
model
=
attrs
;
}
else
{
id
=
attrs
[
targetModel
.
prototype
.
idAttribute
];
id
=
attrs
[
targetModel
.
prototype
.
idAttribute
||
'
id
'
];
}
// If a duplicate is found, prevent it from being added and
...
...
@@ -951,14 +926,13 @@ _.extend(Collection.prototype, Events, {
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
);
}
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.
...
...
@@ -1008,7 +982,7 @@ _.extend(Collection.prototype, Events, {
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
();
...
...
@@ -1049,7 +1023,7 @@ _.extend(Collection.prototype, Events, {
// Get a model from the set by id.
get
:
function
(
obj
)
{
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.
...
...
@@ -1127,7 +1101,7 @@ _.extend(Collection.prototype, Events, {
if
(
!
options
.
wait
)
this
.
add
(
model
,
options
);
var
collection
=
this
;
var
success
=
options
.
success
;
options
.
success
=
function
(
model
,
resp
,
options
)
{
options
.
success
=
function
(
model
,
resp
)
{
if
(
options
.
wait
)
collection
.
add
(
model
,
options
);
if
(
success
)
success
(
model
,
resp
,
options
);
};
...
...
@@ -1151,17 +1125,14 @@ _.extend(Collection.prototype, Events, {
_reset
:
function
()
{
this
.
length
=
0
;
this
.
models
=
[];
this
.
_byId
=
{}
;
this
.
_byId
=
Object
.
create
(
null
)
;
},
// Prepare a hash of attributes (or other model) to be added to this
// collection.
_prepareModel
:
function
(
attrs
,
options
)
{
if
(
attrs
instanceof
Collection
.
prototype
.
model
)
{
if
(
!
attrs
.
collection
)
attrs
.
collection
=
this
;
return
attrs
;
}
options
=
options
?
_
.
extend
({},
options
)
:
{};
if
(
attrs
instanceof
Model
)
return
attrs
;
options
=
_
.
extend
({},
options
);
options
.
collection
=
this
;
var
model
=
new
this
.
model
(
attrs
,
options
);
if
(
!
model
.
validationError
)
return
model
;
...
...
@@ -1169,8 +1140,16 @@ _.extend(Collection.prototype, Events, {
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.
_removeReference
:
function
(
model
)
{
_removeReference
:
function
(
model
,
options
)
{
if
(
this
===
model
.
collection
)
delete
model
.
collection
;
model
.
off
(
'
all
'
,
this
.
_onModelEvent
,
this
);
},
...
...
@@ -1191,7 +1170,7 @@ _.extend(Collection.prototype, Events, {
});
if
(
_
.
each
)
{
if
(
utilExists
(
'
each
'
)
)
{
// Underscore methods that we want to implement on the Collection.
// 90% of the core usefulness of Backbone Collections is actually implemented
// right here:
...
...
@@ -1203,7 +1182,7 @@ if (_.each) {
'
lastIndexOf
'
,
'
isEmpty
'
,
'
chain
'
];
// 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
()
{
var
args
=
slice
.
call
(
arguments
);
args
.
unshift
(
this
.
models
);
...
...
@@ -1215,7 +1194,7 @@ if (_.each) {
var
attributeMethods
=
[
'
groupBy
'
,
'
countBy
'
,
'
sortBy
'
];
// Use attributes instead of properties.
attributeMethods
.
forEach
(
function
(
method
)
{
attributeMethods
.
f
ilter
(
utilExists
).
f
orEach
(
function
(
method
)
{
Collection
.
prototype
[
method
]
=
function
(
value
,
context
)
{
var
iterator
=
typeof
value
===
'
function
'
?
value
:
function
(
model
)
{
return
model
.
get
(
value
);
...
...
@@ -1226,12 +1205,23 @@ if (_.each) {
}
else
{
[
'
forEach
'
,
'
map
'
,
'
filter
'
,
'
some
'
,
'
every
'
,
'
reduce
'
,
'
reduceRight
'
,
'
indexOf
'
,
'
lastIndexOf
'
].
forEach
(
function
(
method
)
{
var
fn
=
Array
.
prototype
[
method
];
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.
[
'
sortBy
'
].
forEach
(
function
(
method
)
{
Collection
.
prototype
[
method
]
=
function
(
value
,
context
)
{
...
...
@@ -1242,42 +1232,39 @@ 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
)
{
// 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.
// 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
();
};
};
// Cached regex to split keys for `delegate`.
var
delegateEventSplitter
=
/^
(\S
+
)\s
*
(
.*
)
$/
;
// Set up all inheritable **Backbone.View** properties and methods.
_
.
extend
(
View
.
prototype
,
Events
,
{
// List of view options to be merged as properties.
var
viewOptions
=
[
'
model
'
,
'
collection
'
,
'
el
'
,
'
id
'
,
'
attributes
'
,
'
className
'
,
'
tagName
'
,
'
events
'
];
// Set up all inheritable **Backbone.View** properties and methods.
_
.
extend
(
View
.
prototype
,
Events
,
{
// The default `tagName` of a View's element is `"div"`.
tagName
:
'
div
'
,
...
...
@@ -1285,16 +1272,7 @@ _.extend(View.prototype, Events, {
// 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.
find
:
function
(
selector
)
{
return
this
.
el
.
querySelector
(
selector
);
},
findAll
:
function
(
selector
)
{
return
slice
.
call
(
this
.
el
.
querySelectorAll
(
selector
));
return
this
.
$el
.
find
(
selector
);
},
// Initialize is an empty function by default. Override it with your own
...
...
@@ -1311,95 +1289,36 @@ _.extend(View.prototype, Events, {
// Remove this view by taking the element out of the DOM, and removing any
// applicable Backbone.Events listeners.
remove
:
function
()
{
if
(
Backbone
.
$
)
{
this
.
$el
.
remove
()
}
else
if
(
this
.
el
.
parentNode
)
{
this
.
el
.
parentNode
.
removeChild
(
this
.
el
);
}
this
.
_removeElement
();
this
.
stopListening
();
return
this
;
},
// Change the view's element (`this.el` property), including event
// re-delegation.
setElement
:
function
(
element
,
delegate
)
{
if
(
Backbone
.
$
)
{
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
;
// Remove this view's element from the document and all event listeners
// attached to it. Exposed for subclasses using an alternative DOM
// manipulation API.
_removeElement
:
function
()
{
this
.
$el
.
remove
();
},
delegate
:
function
(
eventName
,
selector
,
callback
)
{
if
(
typeof
selector
===
'
function
'
)
{
callback
=
selector
;
selector
=
null
;
}
if
(
typeof
callback
!==
'
function
'
)
{
throw
new
TypeError
(
'
View#delegate expects callback function
'
);
}
var
root
=
this
.
el
;
var
bound
=
callback
.
bind
(
this
);
var
handler
=
selector
?
function
(
event
)
{
// if (event.target === root) {
// event.delegateTarget = el;
// return bound(event);
// }
for
(
var
el
=
event
.
target
;
el
&&
el
!==
root
;
el
=
el
.
parentNode
)
{
if
(
utils
.
matchesSelector
(
el
,
selector
))
{
// event.currentTarget or event.target are read-only.
event
.
delegateTarget
=
el
;
return
bound
(
event
);
}
}
}
:
bound
;
root
.
addEventListener
(
eventName
,
handler
,
false
);
this
.
_handlers
.
push
({
eventName
:
eventName
,
selector
:
selector
,
callback
:
callback
,
handler
:
handler
});
return
handler
;
// 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
;
},
undelegate
:
function
(
eventName
,
selector
,
callback
)
{
if
(
typeof
selector
===
'
function
'
)
{
callback
=
selector
;
selector
=
null
;
}
var
root
=
this
.
el
;
var
handlers
=
this
.
_handlers
;
var
removeListener
=
function
(
item
)
{
root
.
removeEventListener
(
item
.
eventName
,
item
.
handler
,
false
);
};
// Remove all handlers.
if
(
!
eventName
&&
!
selector
&&
!
callback
)
{
handlers
.
forEach
(
removeListener
);
this
.
_handlers
=
[];
}
else
{
// Remove some handlers.
handlers
.
filter
(
function
(
item
)
{
return
item
.
eventName
===
eventName
&&
(
callback
?
item
.
callback
===
callback
:
true
)
&&
(
selector
?
item
.
selector
===
selector
:
true
);
})
.
forEach
(
function
(
item
)
{
removeListener
(
item
);
handlers
.
splice
(
handlers
.
indexOf
(
item
),
1
);
});
}
// 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
...
...
@@ -1415,42 +1334,46 @@ _.extend(View.prototype, Events, {
// 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
)
{
delegateEvents
:
function
(
events
)
{
if
(
!
(
events
||
(
events
=
_
.
result
(
this
,
'
events
'
))))
return
this
;
if
(
!
keepOld
)
this
.
undelegateEvents
();
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
{
this
.
delegate
(
eventName
,
selector
,
method
)
}
this
.
delegate
(
match
[
1
],
match
[
2
],
method
.
bind
(
this
));
}
return
this
;
},
// Clears all callbacks previously bound to the view with `delegateEvents`.
// Add a single event listener to the view's element (or a child element
// using `selector`). This only works for delegate-able events: not `focus`,
// `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
delegate
:
function
(
eventName
,
selector
,
listener
)
{
this
.
$el
.
on
(
eventName
+
'
.delegateEvents
'
+
this
.
cid
,
selector
,
listener
);
},
// Clears all callbacks previously bound to the view by `delegateEvents`.
// You usually don't need to use this, but may wish to if you have multiple
// Backbone views attached to the same DOM element.
undelegateEvents
:
function
()
{
if
(
Backbone
.
$
)
{
this
.
$el
.
off
(
'
.delegateEvents
'
+
this
.
cid
);
}
else
{
this
.
undelegate
();
}
if
(
this
.
$el
)
this
.
$el
.
off
(
'
.delegateEvents
'
+
this
.
cid
);
return
this
;
},
// A finer-grained `undelegateEvents` for removing a single delegated event.
// `selector` and `listener` are both optional.
undelegate
:
function
(
eventName
,
selector
,
listener
)
{
this
.
$el
.
off
(
eventName
+
'
.delegateEvents
'
+
this
.
cid
,
selector
,
listener
);
},
// Produces a DOM element to be assigned to your view. Exposed for
// subclasses using an alternative DOM manipulation API.
_createElement
:
function
(
tagName
)
{
return
document
.
createElement
(
tagName
);
},
// Ensure that the View has a DOM element to render into.
// If `this.el` is a string, pass it through `$()`, take the first
// matching element, and re-assign it to `el`. Otherwise, create
...
...
@@ -1459,43 +1382,36 @@ _.extend(View.prototype, Events, {
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
);
if
(
this
.
className
)
attrs
[
'
class
'
]
=
_
.
result
(
this
,
'
className
'
);
this
.
setElement
(
this
.
_createElement
(
_
.
result
(
this
,
'
tagName
'
)));
this
.
_setAttributes
(
attrs
);
}
else
{
this
.
setElement
(
_
.
result
(
this
,
'
el
'
),
false
);
this
.
setElement
(
_
.
result
(
this
,
'
el
'
)
);
}
},
// Set attributes from a hash on this view's element. Exposed for
// subclasses using an alternative DOM manipulation API.
_setAttributes
:
function
(
attributes
)
{
this
.
$el
.
attr
(
attributes
);
}
});
// Backbone.sync
// -------------
});
// 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.
Backbone
.
sync
=
function
(
method
,
model
,
options
)
{
options
||
(
options
=
{})
// 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
});
// Default JSON-request options.
var
params
=
{
type
:
type
,
dataType
:
'
json
'
};
...
...
@@ -1510,26 +1426,8 @@ Backbone.sync = function(method, model, options) {
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
)
{
if
(
params
.
type
!==
'
GET
'
)
{
params
.
processData
=
false
;
}
...
...
@@ -1537,47 +1435,49 @@ Backbone.sync = function(method, model, 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
=
{
// 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
()
{
// 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
()
{
if
(
!
Backbone
.
$
)
throw
new
Error
(
'
You must either include jQuery or override Backbone.ajax (Google Backbone.NativeAjax)
'
);
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
)
{
// 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
;
// 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
)
{
var
isRegExp
=
function
(
value
)
{
return
value
?
(
typeof
value
===
'
object
'
&&
toString
.
call
(
value
)
===
'
[object RegExp]
'
)
:
false
;
};
};
// Set up all inheritable **Backbone.Router** properties and methods.
_
.
extend
(
Router
.
prototype
,
Events
,
{
// Set up all inheritable **Backbone.Router** properties and methods.
_
.
extend
(
Router
.
prototype
,
Events
,
{
// Initialize is an empty function by default. Override it with your own
// initialization logic.
...
...
@@ -1599,7 +1499,7 @@ _.extend(Router.prototype, Events, {
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
);
...
...
@@ -1607,6 +1507,12 @@ _.extend(Router.prototype, Events, {
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
);
...
...
@@ -1631,10 +1537,10 @@ _.extend(Router.prototype, Events, {
route
=
route
.
replace
(
escapeRegExp
,
'
\\
$&
'
)
.
replace
(
optionalParam
,
'
(?:$1)?
'
)
.
replace
(
namedParam
,
function
(
match
,
optional
)
{
return
optional
?
match
:
'
([^
\
/
]+)
'
;
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
...
...
@@ -1642,20 +1548,23 @@ _.extend(Router.prototype, Events, {
// treated as `null` to normalize cross-browser behavior.
_extractParameters
:
function
(
route
,
fragment
)
{
var
params
=
route
.
exec
(
fragment
).
slice
(
1
);
return
params
.
map
(
function
(
param
)
{
return
params
.
map
(
function
(
param
,
i
)
{
// Don't decode the search params.
if
(
i
===
params
.
length
-
1
)
return
param
||
null
;
return
param
?
decodeURIComponent
(
param
)
:
null
;
});
}
});
// 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
()
{
});
// 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
);
...
...
@@ -1664,25 +1573,30 @@ var History = Backbone.History = function() {
this
.
location
=
window
.
location
;
this
.
history
=
window
.
history
;
}
};
};
// Cached regex for stripping a leading hash/slash and trailing space.
var
routeStripper
=
/^
[
#
\/]
|
\s
+$/g
;
// 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 stripping leading and trailing slashes.
var
rootStripper
=
/^
\/
+|
\/
+$/g
;
// Cached regex for removing a trailing slash.
var
trailingSlash
=
/
\/
$/
;
// Cached regex for removing a trailing slash.
var
trailingSlash
=
/
\/
$/
;
// Cached regex for stripping urls of hash and query.
var
pathStripper
=
/
[
#
]
.*$/
;
// Cached regex for stripping urls of hash and query.
var
pathStripper
=
/
[
#
]
.*$/
;
// Has the history handling already been started?
History
.
started
=
false
;
// Has the history handling already been started?
History
.
started
=
false
;
// Set up all inheritable **Backbone.History** properties and methods.
_
.
extend
(
History
.
prototype
,
Events
,
{
// 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.
...
...
@@ -1693,11 +1607,10 @@ _.extend(History.prototype, Events, {
// Get the cross-browser normalized URL fragment, either from the URL,
// the hash, or the override.
getFragment
:
function
(
fragment
)
{
getFragment
:
function
(
fragment
,
forcePushState
)
{
if
(
fragment
==
null
)
{
if
(
this
.
_wantsPushState
||
!
this
.
_wantsHashChange
)
{
// CHANGED: Make fragment include query string.
fragment
=
this
.
location
.
pathname
+
this
.
location
.
search
;
fragment
=
decodeURI
(
this
.
location
.
pathname
+
this
.
location
.
search
);
var
root
=
this
.
root
.
replace
(
trailingSlash
,
''
);
if
(
!
fragment
.
indexOf
(
root
))
fragment
=
fragment
.
slice
(
root
.
length
);
}
else
{
...
...
@@ -1736,17 +1649,15 @@ _.extend(History.prototype, Events, {
// 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
)
{
if
(
this
.
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
);
}
...
...
@@ -1771,7 +1682,7 @@ _.extend(History.prototype, Events, {
// Checks the current URL to see if it has changed, and if it has,
// calls `loadUrl`.
checkUrl
:
function
(
e
)
{
checkUrl
:
function
(
)
{
var
current
=
this
.
getFragment
();
if
(
current
===
this
.
fragment
)
return
false
;
this
.
loadUrl
();
...
...
@@ -1803,7 +1714,7 @@ _.extend(History.prototype, Events, {
var
url
=
this
.
root
+
(
fragment
=
this
.
getFragment
(
fragment
||
''
));
// Strip the fragment of the query and
hash for matching.
// Strip the
hash for matching.
fragment
=
fragment
.
replace
(
pathStripper
,
''
);
if
(
this
.
fragment
===
fragment
)
return
;
...
...
@@ -1840,16 +1751,20 @@ _.extend(History.prototype, Events, {
}
}
});
});
// !!!
// 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
// want global "pubsub" in a convenient place.
_
.
extend
(
Backbone
,
Events
);
// Create the default Backbone.history.
Backbone
.
history
=
new
History
;
// Create the default Backbone.history
if the History module is included
.
if
(
History
)
Backbone
.
history
=
new
History
()
;
return
Backbone
;
});
examples/exoskeleton/package.json
View file @
28b669e4
{
"private"
:
true
,
"dependencies"
:
{
"exoskeleton"
:
"^0.
3
.0"
,
"exoskeleton"
:
"^0.
7
.0"
,
"backbone.localstorage"
:
"^1.1.16"
,
"backbone.nativeview"
:
"^0.3.2"
,
"todomvc-app-css"
:
"^1.0.0"
,
"todomvc-common"
:
"^1.0.1"
,
"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