Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
todomvc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sven Franck
todomvc
Commits
5690847f
Commit
5690847f
authored
Apr 29, 2012
by
weepy
Committed by
Sindre Sorhus
Apr 29, 2012
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add new app o_O
parent
894a653d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1189 additions
and
0 deletions
+1189
-0
labs/architecture-examples/o_O/index.html
labs/architecture-examples/o_O/index.html
+56
-0
labs/architecture-examples/o_O/js/app.js
labs/architecture-examples/o_O/js/app.js
+160
-0
labs/architecture-examples/o_O/js/lib/o_O.js
labs/architecture-examples/o_O/js/lib/o_O.js
+879
-0
labs/architecture-examples/o_O/js/lib/o_O.router.js
labs/architecture-examples/o_O/js/lib/o_O.router.js
+94
-0
No files found.
labs/architecture-examples/o_O/index.html
0 → 100644
View file @
5690847f
<!doctype html>
<html
lang=
"en"
>
<head>
<meta
charset=
"utf-8"
>
<title>
o_O • TodoMVC
</title>
<link
rel=
"stylesheet"
href=
"../../../assets/base.css"
>
</head>
<body>
<section
id=
"todoapp"
>
<header
id=
"header"
>
<h1>
Todos
</h1>
<input
id=
"new-todo"
data-bind=
"value: current; enterKey: add"
placeholder=
"What needs to be done?"
>
</header>
<section
id=
"main"
data-bind=
"visible: todos.count"
>
<div>
<input
id=
"toggle-all"
type=
"checkbox"
data-bind=
"value: allCompleted;"
>
<label
for=
"toggle-all"
>
Mark all as complete
</label>
</div>
<ul
id=
"todo-list"
data-bind=
"foreach: todos"
>
<li
data-bind=
"class: klass; visible; dblclick: startEditing"
>
<div
class=
"view"
>
<input
class=
"toggle"
type=
"checkbox"
data-bind=
"value: completed"
>
<label
data-bind=
"text: title"
></label>
<button
class=
"destroy"
data-bind=
"click: remove"
></button>
</div>
<input
class=
"edit"
data-bind=
"value: title; enterKey: stopEditing; blur: stopEditing"
/>
</li>
</ul>
</section>
<footer
id=
"footer"
data-bind=
"visible: todos.count"
>
<span
id=
"todo-count"
>
<strong
data-bind=
"text: remainingCount"
></strong>
<span
class=
"word"
data-bind=
"text: pluralize('item', remainingCount())"
></span>
left
</span>
<ul
id=
"filters"
>
<li><a
href=
"#/"
>
All
</a></li>
<li><a
href=
"#/active"
>
Active
</a></li>
<li><a
href=
"#/completed"
>
Completed
</a></li>
</ul>
<button
id=
"clear-completed"
data-bind=
"click: removeCompleted; visible: completedCount"
>
Clear completed
(
<span
data-bind=
'text: completedCount'
></span>
)
</button>
</footer>
</section>
<footer
id=
"info"
>
<p>
Double-click to edit a todo
</p>
<p>
Created by
<a
href=
"http://weepy.github.com"
>
weepy (Jonah Fox)
</a></p>
</footer>
<script
src=
"../../../assets/jquery.min.js"
></script>
<script
src=
"js/lib/o_O.js"
type=
"text/javascript"
></script>
<script
src=
"js/lib/o_O.router.js"
type=
"text/javascript"
></script>
<script
src=
"js/app.js"
></script>
</body>
</html>
labs/architecture-examples/o_O/js/app.js
0 → 100644
View file @
5690847f
//a custom binding to handle the enter key
o_O
.
bindings
.
enterKey
=
function
(
func
,
$el
)
{
var
ENTER_KEY
=
13
;
var
context
=
this
$el
.
keyup
(
function
(
e
)
{
if
(
e
.
keyCode
===
ENTER_KEY
)
func
.
call
(
context
);
})
}
o_O
.
bindingTypes
.
enterKey
=
'
outbound
'
// represents a single todo item
var
Todo
=
o_O
.
model
.
extend
({
title
:
''
,
completed
:
false
},
{
initialize
:
function
()
{
this
.
editing
=
o_O
(
false
)
},
startEditing
:
function
()
{
this
.
editing
(
true
)
var
self
=
this
setTimeout
(
function
()
{
$
(
self
.
el
).
parent
().
find
(
'
input.edit
'
).
focus
().
select
()
},
0
)
},
stopEditing
:
function
()
{
var
text
=
$
.
trim
(
this
.
title
()
)
text
?
this
.
title
(
text
)
:
this
.
remove
()
this
.
editing
(
false
)
},
remove
:
function
()
{
todoapp
.
todos
.
remove
(
this
)
},
visible
:
function
()
{
var
filter
=
todoapp
.
filter
(),
completed
=
this
.
completed
()
return
filter
==
''
||
(
filter
==
'
completed
'
&&
completed
)
||
(
filter
==
'
active
'
&&
!
completed
)
},
klass
:
function
()
{
if
(
this
.
editing
())
return
'
editing
'
if
(
this
.
completed
())
return
'
completed
'
else
return
''
}
}
);
// main application
var
TodoApp
=
o_O
.
model
.
extend
({
current
:
""
,
completedCount
:
0
,
filter
:
''
},
{
initialize
:
function
()
{
var
self
=
this
self
.
todos
=
o_O
.
array
(
this
.
todos
()
)
this
.
todos
.
on
(
'
set:completed set:title add remove
'
,
function
()
{
var
completed
=
self
.
todos
.
filter
(
function
(
todo
)
{
return
todo
.
completed
()
})
self
.
completedCount
(
completed
.
length
)
self
.
persist
()
})
this
.
remainingCount
=
o_O
(
function
()
{
return
self
.
todos
.
count
()
-
self
.
completedCount
()
})
// writeable computed observable
// handles marking all complete/incomplete
// or retrieving if this is true
this
.
allCompleted
=
o_O
(
function
(
v
)
{
if
(
arguments
.
length
==
0
)
{
return
self
.
remainingCount
()
==
0
}
self
.
todos
.
each
(
function
(
todo
)
{
todo
.
completed
(
v
)
})
return
v
})
},
add
:
function
()
{
var
text
=
$
.
trim
(
this
.
current
()
);
if
(
text
)
{
this
.
todos
.
unshift
(
Todo
({
title
:
text
})
);
this
.
current
(
""
)
}
},
removeCompleted
:
function
()
{
this
.
todos
.
remove
(
function
(
todo
)
{
return
todo
.
completed
()
})
return
false
},
persist
:
function
()
{
localStorage
[
'
todos-o_O
'
]
=
JSON
.
stringify
(
this
.
todos
.
toJSON
()
)
},
// adds an `s` where necessary
pluralize
:
function
(
word
,
count
)
{
return
word
+
(
count
===
1
?
""
:
"
s
"
);
}
}
);
function
main
()
{
// load todos
var
todos
=
[]
try
{
todos
=
JSON
.
parse
(
localStorage
[
'
todos-o_O
'
]
);
}
catch
(
e
)
{
}
// create models
for
(
var
i
=
0
;
i
<
todos
.
length
;
i
++
)
todos
[
i
]
=
Todo
.
create
(
todos
[
i
]
)
// create app
window
.
todoapp
=
TodoApp
(
{
todos
:
todos
}
)
// bind to DOM element
todoapp
.
bind
(
'
#todoapp
'
)
// setup Routing
o_O
.
router
()
.
add
(
'
*filter
'
,
function
(
filt
)
{
todoapp
.
filter
(
filt
)
$
(
'
#filters a
'
)
.
removeClass
(
'
selected
'
)
.
filter
(
"
[href='#/
"
+
filt
+
"
']
"
)
.
addClass
(
'
selected
'
)
})
.
start
()
}
// kick it off
main
();
labs/architecture-examples/o_O/js/lib/o_O.js
0 → 100644
View file @
5690847f
!
function
()
{
/* HTML binding for Lulz
,ad8888ba,
d8"' `"8b Power of KnockoutJS, with the agility of Backbone
d8' `8b
,adPPYba, 88 88 Elegantly binds objects to HTML
a8" "8a 88 88
8b d8 Y8, ,8P Proxies through jQuery (or whatever $ is)
"8a, ,a8" Y8a. .a8P
`"YbbdP"' `"Y8888Y"' Automatic dependency resolution
888888888888 Plays well with others
(c) 2012 by Jonah Fox (weepy), MIT Licensed */
var
VERSION
=
"
0.2.6
"
,
slice
=
Array
.
prototype
.
slice
,
Events
=
{
/*
* Create an immutable callback list, allowing traversal during modification. The tail is an empty object that will always be used as the next node.
* */
on
:
function
(
events
,
callback
,
context
)
{
var
ev
;
events
=
events
.
split
(
/
\s
+/
);
var
calls
=
this
.
_callbacks
||
(
this
.
_callbacks
=
{});
while
(
ev
=
events
.
shift
())
{
var
list
=
calls
[
ev
]
||
(
calls
[
ev
]
=
{});
var
tail
=
list
.
tail
||
(
list
.
tail
=
list
.
next
=
{});
tail
.
callback
=
callback
;
tail
.
context
=
context
;
list
.
tail
=
tail
.
next
=
{};
}
return
this
;
},
/*
* Remove one or many callbacks. If context is null, removes all callbacks with that function.
* If callback is null, removes all callbacks for the event.
* If ev is null, removes all bound callbacks for all events.
* */
off
:
function
(
events
,
callback
,
context
)
{
var
ev
,
calls
,
node
;
if
(
!
events
)
{
delete
this
.
_callbacks
;
}
else
if
(
calls
=
this
.
_callbacks
)
{
events
=
events
.
split
(
/
\s
+/
);
while
(
ev
=
events
.
shift
())
{
node
=
calls
[
ev
];
delete
calls
[
ev
];
if
(
!
callback
||
!
node
)
continue
;
// Create a new list, omitting the indicated event/context pairs.
while
((
node
=
node
.
next
)
&&
node
.
next
)
{
if
(
node
.
callback
===
callback
&&
(
!
context
||
node
.
context
===
context
))
continue
;
this
.
on
(
ev
,
node
.
callback
,
node
.
context
);
}
}
}
return
this
;
},
/*
* Trigger an event, firing all bound callbacks. Callbacks are passed the same arguments as emit is, apart from the event name.
* Listening for "*" passes the true event name as the first argument.
* */
emit
:
function
(
events
)
{
var
event
,
node
,
calls
,
tail
,
args
,
all
,
rest
;
if
(
!
(
calls
=
this
.
_callbacks
))
return
this
;
all
=
calls
[
'
all
'
];
(
events
=
events
.
split
(
/
\s
+/
)).
push
(
null
);
// Save references to the current heads & tails.
while
(
event
=
events
.
shift
())
{
if
(
all
)
events
.
push
({
next
:
all
.
next
,
tail
:
all
.
tail
,
event
:
event
});
if
(
!
(
node
=
calls
[
event
]))
continue
;
events
.
push
({
next
:
node
.
next
,
tail
:
node
.
tail
});
}
//Traverse each list, stopping when the saved tail is reached.
rest
=
slice
.
call
(
arguments
,
1
);
while
(
node
=
events
.
pop
())
{
tail
=
node
.
tail
;
args
=
node
.
event
?
[
node
.
event
].
concat
(
rest
)
:
rest
;
while
((
node
=
node
.
next
)
!==
tail
)
{
node
.
callback
.
apply
(
node
.
context
||
this
,
args
);
}
}
return
this
;
}
}
/*
* Public function to return an observable property
* sync: whether to emit changes immediately, or in the next event loop
*/
var
propertyMethods
=
{
incr
:
function
(
val
)
{
return
this
(
this
.
value
+
(
val
||
1
))
},
scale
:
function
(
val
)
{
return
this
(
this
.
value
*
(
val
||
1
))
},
toggle
:
function
(
val
)
{
return
this
(
!
this
.
value
)
},
change
:
function
(
fn
)
{
fn
?
this
.
on
(
'
set
'
,
fn
)
// setup observer
:
this
.
emit
(
'
set
'
,
this
(),
this
.
old_val
)
return
this
},
mirror
:
function
(
other
,
both
)
{
other
.
change
(
function
(
val
)
{
if
(
val
!=
this
.
value
)
this
(
val
)
})
other
.
change
()
both
&&
other
.
mirror
(
this
)
return
this
},
toString
:
function
()
{
return
'
<
'
+
(
this
.
type
?
this
.
type
+
'
:
'
:
''
)
+
this
.
value
+
'
>
'
},
bind
:
function
(
el
)
{
o_O
.
bind
(
this
,
el
)
return
this
},
emitset
:
function
()
{
if
(
this
.
_emitting
)
return
// property is already emitting to avoid circular problems
this
.
_emitting
=
true
this
.
emit
(
'
set
'
,
this
(),
this
.
old_value
)
// force a read
delete
this
.
_emitting
},
// timeout: 0,
constructor
:
o_O
// fake this - useful for checking
}
function
o_O
(
arg
,
type
)
{
var
simple
=
typeof
arg
!=
'
function
'
function
prop
(
v
)
{
if
(
arguments
.
length
)
{
prop
.
old_value
=
prop
.
value
prop
.
value
=
simple
?
v
:
arg
(
v
)
prop
.
emitset
()
}
else
{
if
(
dependencies
.
checking
)
dependencies
.
emit
(
'
get
'
,
prop
)
// emit to dependency checker
if
(
!
simple
)
prop
.
value
=
arg
()
}
return
prop
.
value
}
if
(
simple
)
prop
.
value
=
arg
else
{
prop
.
dependencies
=
[]
each
(
dependencies
(
prop
),
function
(
dep
)
{
prop
.
dependencies
.
push
(
dep
)
dep
.
change
(
function
()
{
prop
.
emitset
()
})
})
}
extend
(
prop
,
Events
,
propertyMethods
)
if
(
type
)
prop
.
type
=
type
return
prop
}
/*
* Calculate dependencies
*/
function
dependencies
(
func
)
{
var
deps
=
[]
function
add
(
dep
)
{
if
(
indexOf
(
deps
,
dep
)
<
0
&&
dep
!=
func
)
deps
.
push
(
dep
)
}
dependencies
.
checking
=
true
// we're checking dependencies
dependencies
.
on
(
'
get
'
,
add
)
// setup listener
dependencies
.
lastResult
=
func
()
// run the function
dependencies
.
off
(
'
get
'
,
add
)
// remove listener
dependencies
.
checking
=
false
// no longer checking dependencies
return
deps
}
extend
(
dependencies
,
Events
)
o_O
.
dependencies
=
dependencies
// returns a function from some text
o_O
.
expression
=
function
(
text
)
{
o_O
.
expression
.
last
=
text
// remember the last case useful for debugging syntax errors
return
new
Function
(
'
o_O
'
,
'
with(this) { return (
'
+
text
+
'
); }
'
)
}
o_O
.
nextFrame
=
(
function
()
{
var
fns
=
[]
,
timeout
;
function
run
()
{
while
(
fns
.
length
)
fns
.
shift
()()
timeout
=
null
}
var
self
=
this
// shim layer with setTimeout fallback
var
onNextFrame
=
self
.
requestAnimationFrame
||
self
.
webkitRequestAnimationFrame
||
self
.
mozRequestAnimationFrame
||
self
.
oRequestAnimationFrame
||
self
.
msRequestAnimationFrame
||
function
(
callback
)
{
self
.
setTimeout
(
callback
,
1000
/
60
);
};
return
function
(
fn
)
{
fns
.
push
(
fn
)
timeout
=
timeout
||
onNextFrame
(
run
)
}
})();
/*
* el: dom element
* binding: name of the binding rule
* expr: text containing binding specification
* context: the object that we're binding
*/
o_O
.
bindRuleToElement
=
function
(
method
,
expressionString
,
context
,
el
)
{
var
expression
=
o_O
.
expression
(
expressionString
)
,
binding
=
o_O
.
createBinding
(
method
)
,
$el
=
$
(
el
)
,
value
// contains the current value of the attribute that emit will use
// if it's an outbound event - just emit immediately and we're done
if
(
binding
.
type
==
'
outbound
'
)
{
evaluateExpression
()
emit
()
return
}
// otherwise we need to calculate our dependencies
var
deps
=
dependencies
(
evaluateExpression
)
// if we're not two way and it's a function - then really it's a short hand for missing brackets - recalculate
if
(
typeof
value
==
'
function
'
&&
binding
.
type
!=
'
twoway
'
)
{
expression
=
o_O
.
expression
(
'
(
'
+
expressionString
+
'
)()
'
)
deps
=
dependencies
(
evaluateExpression
)
}
// we should emit immediately
emit
()
// and also everytime a dependency changes - but only once per binding per frame - even if > 1 dependency changes
var
emitting
each
(
deps
,
function
(
dep
)
{
dep
.
change
(
function
()
{
evaluateExpression
()
if
(
emitting
)
return
// don't queue another
emitting
=
true
o_O
.
nextFrame
(
function
()
{
emit
()
emitting
=
false
})
})
})
// evaluates the current expressionString
function
evaluateExpression
()
{
value
=
expression
.
call
(
context
,
o_O
.
helpers
);
if
(
value
instanceof
String
)
value
=
value
.
toString
();
// strange problem
}
// emit is the function that actually performs the work
function
emit
()
{
return
binding
.
call
(
context
,
value
,
$el
)
}
}
/*
* Helper function to extract rules from a css like string
*/
function
parseBindingAttribute
(
str
)
{
if
(
!
str
)
return
[]
var
rules
=
trim
(
str
).
split
(
"
;
"
),
ret
=
[],
i
,
bits
,
binding
,
param
,
rule
for
(
var
i
=
0
;
i
<
rules
.
length
;
i
++
)
{
rule
=
trim
(
rules
[
i
])
if
(
!
rule
)
continue
// for trailing ;
bits
=
map
(
trim
(
rule
).
split
(
"
:
"
),
trim
)
binding
=
trim
(
bits
.
shift
())
param
=
trim
(
bits
.
join
(
"
:
"
))
||
binding
ret
.
push
([
binding
,
param
])
}
return
ret
}
/*
* Public function to bind an object to an element or selector
* context: the object to bind
* dom: the element or selector
* recursing: internal flag to indicate wether it is an internal call
*/
o_O
.
bind
=
function
(
context
,
dom
,
recursing
)
{
var
$el
=
$
(
dom
)
if
(
!
recursing
)
context
.
el
=
$el
[
0
]
var
recurse
=
true
,
pairs
=
parseBindingAttribute
(
$el
.
attr
(
o_O
.
bindingAttribute
))
for
(
var
i
=
0
;
i
<
pairs
.
length
;
i
++
)
{
var
method
=
pairs
[
i
][
0
]
,
expression
=
pairs
[
i
][
1
]
if
(
method
==
'
with
'
||
method
==
'
foreach
'
)
recurse
=
false
if
(
method
==
'
"class"
'
)
method
=
"
class
"
o_O
.
bindRuleToElement
(
method
,
expression
,
context
,
$el
)
}
if
(
o_O
.
removeBindingAttribute
)
$el
.
attr
(
o_O
.
bindingAttribute
,
null
)
if
(
recurse
)
{
$el
.
children
().
each
(
function
(
i
,
el
)
{
o_O
.
bind
(
context
,
el
,
true
)
})
}
}
/*
* Retrieves HTML string from a dom node (that may have been changed due to binding)
*/
function
getTemplate
(
$el
)
{
var
template
=
$el
.
data
(
'
o_O:template
'
)
if
(
template
==
null
)
{
template
=
$el
.
html
()
$el
.
html
(
''
)
// $el.attr(o_O.bindingAttribute, null)
$el
.
data
(
'
o_O:template
'
,
template
)
}
return
template
}
o_O
.
helpers
=
{
// converts a DOM event from an element with a value into its value
// useful for setting properties based on form events
value
:
function
(
fn
)
{
return
function
(
e
)
{
return
fn
.
call
(
this
,
$
(
e
.
currentTarget
).
val
(),
e
)
}
},
// converts a mouse event callback to a callback with the mouse position relative to the target
position
:
function
(
fn
)
{
return
function
(
e
)
{
var
el
=
e
.
currentTarget
var
o
=
$
(
el
).
offset
()
fn
.
call
(
this
,
e
.
pageX
-
o
.
left
,
e
.
pageY
-
o
.
top
,
e
)
}
}
}
/*
|_ o._ _|o._ _ _
|_)|| |(_||| |(_|_>
_| */
o_O
.
bindings
=
{
/*
* set visibility depenent on val
*/
visible
:
function
(
val
,
$el
)
{
val
?
$el
.
show
()
:
$el
.
hide
()
},
'
if
'
:
function
(
context
,
$el
)
{
var
template
=
getTemplate
(
$el
)
$el
.
html
(
context
?
template
:
''
)
},
unless
:
function
(
val
,
$el
)
{
return
o_O
.
bindings
[
'
if
'
](
!
val
,
$el
)
},
'
with
'
:
function
(
context
,
$el
)
{
var
template
=
getTemplate
(
$el
)
$el
.
html
(
context
?
template
:
''
)
if
(
context
)
o_O
.
bind
(
context
,
$el
)
},
options
:
function
(
options
,
$el
)
{
var
isArray
=
options
instanceof
Array
$
.
each
(
options
,
function
(
key
,
value
)
{
var
text
=
isArray
?
value
:
key
$el
.
append
(
$
(
"
<option>
"
).
attr
(
"
value
"
,
value
).
html
(
text
))
})
},
/*
* Allows binding of a list of items
* list is expected to respond to forEach
*/
foreach
:
function
(
list
,
$el
)
{
var
template
=
getTemplate
(
$el
)
// default renderer if list doesn't specify one
function
defaultRenderer
(
item
)
{
$
(
template
).
each
(
function
(
i
,
elem
)
{
var
$$
=
$
(
elem
)
$$
.
appendTo
(
$el
)
o_O
.
bind
(
item
,
$$
)
})
}
var
renderItem
=
list
.
renderItem
||
defaultRenderer
$el
.
html
(
''
)
list
.
forEach
(
function
(
item
,
index
)
{
renderItem
.
call
(
list
,
item
,
$el
,
index
)
})
list
.
onbind
&&
list
.
onbind
(
$el
)
},
log
:
function
(
context
,
$el
)
{
console
.
log
(
'
o_O
'
,
context
,
$el
,
this
)
}
}
/* general purpose
* `call: fn` will run once - useful for intialization
*/
o_O
.
bindings
.
call
=
function
(
func
,
$el
)
{
func
.
call
(
this
,
$el
)
}
o_O
.
bindings
.
call
.
type
=
'
outbound
'
/* Two-way binding to a form element to a property
* usage: bind='value: myProperty'
* special cases for checkbox
*/
o_O
.
bindings
.
value
=
function
(
property
,
$el
)
{
$el
.
change
(
function
(
e
)
{
var
checkbox
=
$
(
this
).
attr
(
'
type
'
)
==
'
checkbox
'
,
val
=
checkbox
?
!!
$
(
this
).
attr
(
'
checked
'
)
:
$
(
this
).
val
()
property
(
val
,
e
)
})
if
(
property
.
constructor
==
o_O
)
{
property
.
on
(
'
set
'
,
function
(
val
)
{
$el
.
attr
(
'
type
'
)
==
'
checkbox
'
?
$el
.
attr
(
'
checked
'
,
val
?
'
checked
'
:
null
)
:
$el
.
val
(
val
)
})
property
.
change
()
// force a change
}
}
o_O
.
bindings
.
value
.
type
=
'
twoway
'
/*
Outbound bindings - i.e. user events
*/
o_O
.
bindingTypes
=
{
focus
:
'
outbound
'
,
blur
:
'
outbound
'
,
change
:
'
outbound
'
,
submit
:
'
outbound
'
,
keypress
:
'
outbound
'
,
keydown
:
'
outbound
'
,
keyup
:
'
outbound
'
,
click
:
'
outbound
'
,
mouseover
:
'
outbound
'
,
mouseout
:
'
outbound
'
,
mousedown
:
'
outbound
'
,
mousemove
:
'
outbound
'
,
mouseup
:
'
outbound
'
,
dblclick
:
'
outbound
'
,
load
:
'
outbound
'
}
function
__bind
(
func
,
context
)
{
return
function
()
{
return
func
.
apply
(
context
,
arguments
)
}
}
o_O
.
createBinding
=
function
(
method
)
{
var
binding
if
(
o_O
.
bindings
[
method
]
)
{
binding
=
o_O
.
bindings
[
method
]
}
else
if
(
method
in
$
.
prototype
)
{
binding
=
function
(
value
,
$el
)
{
typeof
value
==
'
function
'
&&
(
value
=
__bind
(
value
,
this
))
return
$el
[
method
](
value
)
}
}
else
{
binding
=
function
(
value
,
$el
)
{
return
$el
.
attr
(
method
,
value
);
// set DOM attribute
}
}
binding
.
type
=
binding
.
type
||
o_O
.
bindingTypes
[
method
]
||
'
inbound
'
return
binding
}
/* ___ __ __ _ _
___ / _ \ | \/ | ___ __| | ___| |
/ _ \ | | | || |\/| |/ _ \ / _` |/ _ \ |
| (_) | | |_| || | | | (_) | (_| | __/ |
\___/___\___(_)_| |_|\___/ \__,_|\___|_|
|_____|
Model with observable properties, subclasses, evented
*/
function
model
(
o
,
proto
)
{
if
(
!
(
this
instanceof
model
))
return
new
model
(
o
,
proto
)
o
=
o
||
{}
this
.
properties
=
[]
for
(
var
name
in
o
)
{
model
.
addProperty
(
this
,
name
,
o
[
name
])
model
.
observeProperty
(
this
,
name
)
}
var
defaults
=
this
.
constructor
.
defaults
for
(
var
name
in
defaults
)
{
if
(
name
in
o
)
continue
var
val
=
defaults
[
name
]
model
.
addProperty
(
this
,
name
,
val
)
model
.
observeProperty
(
this
,
name
)
}
proto
&&
extend
(
this
,
proto
)
this
.
initialize
.
apply
(
this
,
arguments
)
}
extend
(
model
,
Events
,
{
observeProperty
:
function
(
model
,
name
)
{
model
[
name
].
on
(
'
set
'
,
function
(
val
,
old
)
{
model
.
emit
(
'
set:
'
+
name
,
model
,
val
,
old
)
if
(
val
===
old
)
return
var
x
=
{}
,
y
=
{}
x
[
name
]
=
val
y
[
name
]
=
old
model
.
emit
(
'
update
'
,
model
,
x
,
y
)
})
},
addProperty
:
function
(
model
,
name
,
val
)
{
model
[
name
]
=
o_O
(
val
)
model
.
properties
.
push
(
name
)
},
defaults
:
{},
types
:
{},
extend
:
function
(
defaults
,
protoProps
,
classProps
)
{
defaults
=
defaults
||
{}
var
child
=
inherits
(
this
,
protoProps
,
classProps
);
child
.
defaults
=
defaults
child
.
extend
=
this
.
extend
;
if
(
defaults
.
type
)
model
.
types
[
defaults
.
type
]
=
child
return
child
;
},
create
:
function
(
o
)
{
o
=
o
||
{}
var
type
=
this
==
model
?
model
.
types
[
o
.
type
]
:
this
if
(
!
type
)
throw
new
Error
(
'
no such Model with type:
'
+
o
.
type
)
return
new
type
(
o
)
}
})
extend
(
model
.
prototype
,
Events
,
{
toString
:
function
()
{
return
'
#<
'
+
(
this
.
type
?
this
.
type
()
:
'
model
'
)
+
'
>
'
},
bind
:
function
(
el
)
{
o_O
.
bind
(
this
,
el
);
return
this
;
},
initialize
:
function
(
o
)
{},
valid
:
function
()
{
return
true
},
// update a json model of named values
// if resultant model is invalid - it is set back to previous values
// THIS SHOULD BE SIMPLIFIED
update
:
function
(
o
)
{
var
old
=
{}
,
props
=
this
.
constructor
.
defaults
for
(
var
key
in
o
)
{
if
(
key
in
props
)
{
old
[
key
]
=
this
[
key
].
value
this
[
key
].
value
=
o
[
key
]
}
}
if
(
this
.
valid
())
{
for
(
var
key
in
old
)
{
this
[
key
].
value
=
old
[
key
]
this
[
key
](
o
[
key
])
}
this
.
emit
(
'
update
'
,
this
,
o
,
old
)
return
old
}
else
{
for
(
var
key
in
old
)
this
[
key
](
old
[
key
])
return
false
}
},
destroy
:
function
()
{
this
.
emit
(
'
destroy
'
,
this
)
},
toJSON
:
function
()
{
var
json
=
{}
for
(
var
i
=
0
;
i
<
this
.
properties
.
length
;
i
++
)
{
var
prop
=
this
.
properties
[
i
]
json
[
prop
]
=
this
[
prop
]()
}
return
json
},
clone
:
function
()
{
return
model
.
create
(
this
.
toJSON
())
}
})
o_O
.
model
=
model
/* ___
___ / _ \ __ _ _ __ _ __ __ _ _ _
/ _ \ | | | |/ _` | '__| '__/ _` | | | |
| (_) | | |_| | (_| | | | | | (_| | |_| |
\___/___\___(_)__,_|_| |_| \__,_|\__, |
|_____| |___/ */
function
array
(
items
)
{
if
(
!
(
this
instanceof
array
))
return
new
array
(
items
)
var
self
=
this
this
.
items
=
[]
this
.
count
=
o_O
(
0
)
this
.
length
=
0
this
.
count
.
change
(
function
(
count
)
{
self
.
length
=
count
})
if
(
items
)
{
for
(
var
i
=
0
;
i
<
items
.
length
;
i
++
)
this
.
push
(
items
[
i
])
}
this
.
initialize
()
}
extend
(
array
,
{
add
:
function
(
arr
,
o
,
index
)
{
arr
.
count
.
incr
()
if
(
o
.
on
&&
o
.
emit
)
{
o
.
on
(
'
all
'
,
arr
.
_onevent
,
arr
)
o
.
emit
(
'
add
'
,
o
,
arr
,
index
)
}
else
{
arr
.
emit
(
'
add
'
,
o
,
arr
,
index
)
}
return
arr
.
items
.
length
},
remove
:
function
(
arr
,
o
,
index
)
{
arr
.
count
.
incr
(
-
1
)
//force re-binding
if
(
o
.
off
&&
o
.
emit
)
{
o
.
emit
(
'
remove
'
,
o
,
arr
,
index
)
o
.
off
(
'
all
'
,
arr
.
_onevent
,
arr
)
}
else
{
arr
.
emit
(
'
remove
'
,
o
,
index
)
}
return
o
},
extend
:
function
(
protoProps
,
classProps
)
{
var
child
=
inherits
(
this
,
protoProps
,
classProps
);
child
.
extend
=
this
.
extend
;
return
child
;
}
})
extend
(
array
.
prototype
,
Events
,
{
type
:
'
o_O.array
'
,
initialize
:
function
()
{},
_onevent
:
function
(
ev
,
o
,
array
)
{
if
((
ev
==
'
add
'
||
ev
==
'
remove
'
)
&&
array
!=
this
)
return
if
(
ev
==
'
destroy
'
)
{
this
.
remove
(
o
)
}
this
.
emit
.
apply
(
this
,
arguments
)
},
bind
:
function
(
el
)
{
o_O
.
bind
(
this
,
el
)
return
this
},
indexOf
:
function
(
o
){
return
this
.
items
.
indexOf
(
o
)
},
filter
:
function
(
fn
){
return
this
.
items
.
filter
(
fn
)
},
find
:
function
(
fn
){
for
(
var
i
=
0
;
i
<
this
.
items
.
length
;
i
++
)
{
var
it
=
this
.
items
[
i
]
if
(
fn
(
it
,
i
))
return
it
}
},
map
:
function
(
fn
)
{
this
.
count
();
// force the dependency
var
ret
=
[]
for
(
var
i
=
0
;
i
<
this
.
length
;
i
++
)
{
var
result
=
fn
.
call
(
this
,
this
.
items
[
i
],
i
)
ret
.
push
(
result
)
}
return
ret
},
push
:
function
(
o
)
{
return
this
.
insert
(
o
,
this
.
length
)
},
unshift
:
function
(
o
)
{
return
this
.
insert
(
o
,
0
)
},
pop
:
function
(){
return
this
.
removeAt
(
this
.
length
-
1
)
//remove(this, this.items.pop())
},
shift
:
function
(){
return
this
.
removeAt
(
0
)
//remove(this, this.items.shift())
},
at
:
function
(
index
)
{
return
this
.
items
[
index
]
},
insert
:
function
(
o
,
index
)
{
if
(
index
<
0
||
index
>
this
.
count
())
return
false
this
.
items
.
splice
(
index
,
0
,
o
)
array
.
add
(
this
,
o
,
index
)
return
o
},
removeAt
:
function
(
index
)
{
if
(
index
<
0
||
index
>
this
.
count
())
return
false
var
o
=
this
.
items
[
index
]
this
.
items
.
splice
(
index
,
1
)
array
.
remove
(
this
,
o
,
index
)
return
o
},
remove
:
function
(
o
)
{
var
func
=
'
function
'
===
typeof
o
,
// what about if o is a function itself? - perhaps this should be another method ?
items
=
func
?
this
.
items
.
filter
(
o
)
:
[
o
],
index
,
len
=
items
.
length
for
(
var
i
=
0
;
i
<
len
;
i
++
){
index
=
this
.
indexOf
(
items
[
i
])
if
(
index
!==
-
1
)
this
.
removeAt
(
index
)
}
return
func
?
items
:
items
[
0
]
},
renderItem
:
function
(
item
,
$el
,
index
)
{
var
$$
=
$
(
getTemplate
(
$el
))
var
nextElem
=
this
.
at
(
index
).
el
||
$el
.
children
()[
index
]
nextElem
?
$$
.
insertBefore
(
nextElem
)
:
$el
.
append
(
$$
)
o_O
.
bind
(
item
,
$$
)
},
onbind
:
function
(
$el
)
{
var
self
=
this
this
.
on
(
'
add
'
,
function
(
item
,
arr
,
index
)
{
self
.
renderItem
(
item
,
$el
,
index
)
})
this
.
on
(
'
remove
'
,
this
.
removeElement
,
this
)
this
.
el
=
$el
[
0
]
},
removeElement
:
function
(
item
,
index
)
{
$
(
item
.
el
||
$
(
this
.
el
).
children
()[
index
]).
remove
()
},
toString
:
function
()
{
return
'
#<
'
+
this
.
type
+
'
:
'
+
this
.
length
+
'
>
'
},
toJSON
:
function
()
{
return
this
.
map
(
function
(
o
)
{
return
o
.
toJSON
?
o
.
toJSON
()
:
o
})
}
})
array
.
prototype
.
each
=
array
.
prototype
.
forEach
=
array
.
prototype
.
map
o_O
.
array
=
array
/* * * * * * * * * *
* HELPER FUNCTIONS
*/
function
map
(
array
,
fn
)
{
var
ret
=
[]
for
(
var
i
=
0
;
i
<
array
.
length
;
i
++
)
ret
[
i
]
=
fn
(
array
[
i
],
i
)
return
ret
}
function
extend
(
obj
)
{
var
args
=
slice
.
call
(
arguments
,
1
)
for
(
var
i
=
0
;
i
<
args
.
length
;
i
++
)
{
var
source
=
args
[
i
]
for
(
var
prop
in
source
)
obj
[
prop
]
=
source
[
prop
]
}
return
obj
}
function
each
(
array
,
action
)
{
if
(
array
.
forEach
)
return
array
.
forEach
(
action
)
for
(
var
i
=
0
,
n
=
array
.
length
;
i
<
n
;
i
++
)
if
(
i
in
array
)
action
.
call
(
null
,
array
[
i
],
i
,
array
);
}
function
trim
(
s
)
{
return
s
.
replace
(
/^
\s
+|
\s
+$/g
,
''
)
}
function
indexOf
(
array
,
obj
,
start
)
{
if
(
array
.
indexOf
)
return
array
.
indexOf
(
obj
,
start
)
for
(
var
i
=
(
start
||
0
),
j
=
array
.
length
;
i
<
j
;
i
++
)
{
if
(
array
[
i
]
===
obj
)
{
return
i
;
}
}
return
-
1
;
}
function
ctor
(){};
function
inherits
(
parent
,
protoProps
,
staticProps
)
{
var
child
=
function
(
a
,
b
)
{
if
(
this
instanceof
child
)
parent
.
apply
(
this
,
arguments
)
else
return
new
child
(
a
,
b
)
};
if
(
protoProps
&&
protoProps
.
hasOwnProperty
(
'
constructor
'
))
child
=
protoProps
.
constructor
extend
(
child
,
parent
)
ctor
.
prototype
=
parent
.
prototype
child
.
prototype
=
new
ctor
()
if
(
protoProps
)
extend
(
child
.
prototype
,
protoProps
);
if
(
staticProps
)
extend
(
child
,
staticProps
);
child
.
prototype
.
constructor
=
child
;
child
.
__super__
=
parent
.
prototype
;
return
child
;
};
o_O
.
uuid
=
function
(
len
)
{
return
Math
.
random
().
toString
(
36
).
slice
(
2
)
};
// export and options
extend
(
o_O
,
{
bindingAttribute
:
'
data-bind
'
,
removeBindingAttribute
:
true
,
inherits
:
inherits
,
extend
:
extend
,
Events
:
Events
,
VERSION
:
VERSION
})
if
(
typeof
module
==
'
undefined
'
)
{
var
scripts
=
document
.
getElementsByTagName
(
'
script
'
)
var
namespace
=
scripts
[
scripts
.
length
-
1
].
src
.
split
(
'
?
'
)[
1
]
window
[
namespace
||
'
o_O
'
]
=
o_O
}
else
{
module
.
exports
=
o_O
}
}();
labs/architecture-examples/o_O/js/lib/o_O.router.js
0 → 100644
View file @
5690847f
/*
* Router
* ======
*
* Very simple router using #hashes
* designed to work with o_O
*/
;(
function
()
{
var
namedParam
=
/:
\w
+/g
,
splatParam
=
/
\*\w
+/g
,
escapeRegExp
=
/
[
-[
\]
{}()+?.,
\\
^$|#
\s]
/g
,
routeStripper
=
/^
[
#
\/]
/
;
function
regexRoute
(
str
)
{
str
=
str
.
replace
(
escapeRegExp
,
"
\\
$&
"
)
.
replace
(
namedParam
,
"
([^
\
/]+)
"
)
.
replace
(
splatParam
,
"
(.*?)
"
)
return
new
RegExp
(
'
^
'
+
str
+
'
$
'
)
}
o_O
.
router
=
o_O
.
model
.
extend
({},
{
initialize
:
function
()
{
this
.
routes
=
[]
},
add
:
function
(
param
,
handler
)
{
if
(
param
===
404
)
{
this
.
routing_404
=
{
handler
:
handler
}
}
else
{
this
.
routes
.
push
({
regex
:
regexRoute
(
param
),
handler
:
handler
})
}
return
this
},
runRouting
:
function
(
hash
)
{
var
routes
=
[]
for
(
var
i
=
0
;
i
<
this
.
routes
.
length
;
i
++
)
{
var
route
=
this
.
routes
[
i
];
if
(
route
.
regex
.
test
(
hash
))
routes
.
push
(
route
)
}
for
(
var
i
=
0
;
i
<
routes
.
length
;
i
++
)
{
var
params
=
route
.
regex
.
exec
(
hash
).
slice
(
1
)
this
.
runRoute
(
routes
[
i
],
params
)
}
if
(
routes
.
length
==
0
&&
this
.
routing_404
)
this
.
runRoute
(
this
.
routing_404
,
[])
},
runRoute
:
function
(
route
,
params
)
{
if
(
typeof
route
.
handler
==
'
string
'
)
{
params
.
unshift
(
route
.
handler
)
this
.
emit
.
apply
(
this
,
params
)
}
else
route
.
handler
.
apply
(
null
,
params
)
},
getHash
:
function
()
{
var
match
=
window
.
location
.
href
.
match
(
/#
(
.*
)
$/
);
return
match
?
match
[
1
]
:
''
;
},
go
:
function
()
{
this
.
location
=
this
.
getHash
().
replace
(
routeStripper
,
''
);
this
.
runRouting
(
this
.
location
)
},
start
:
function
()
{
var
self
=
this
;
this
.
go
()
$
(
window
).
bind
(
'
hashchange
'
,
function
()
{
self
.
go
()
})
},
redirect
:
function
(
url
,
changeUrlHash
)
{
if
(
changeUrlHash
===
false
)
this
.
runRouting
(
url
)
else
{
var
hash
=
"
#!
"
+
url
document
.
location
.
hash
==
hash
?
this
.
go
()
:
document
.
location
.
hash
=
hash
}
}
})
o_O
.
extend
(
o_O
.
router
.
prototype
,
o_O
.
Events
)
}).
call
(
this
)
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