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
e6f14976
Commit
e6f14976
authored
Nov 24, 2012
by
Oscar Godson
Committed by
Stephen Sawchuk
May 01, 2013
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TodoMVC app in vanilla JS. No, that's not a framework.
parent
8ea9aef8
Changes
12
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1971 additions
and
301 deletions
+1971
-301
vanilla-examples/vanillajs/component.json
vanilla-examples/vanillajs/component.json
+8
-0
vanilla-examples/vanillajs/components/director/build/director.js
...-examples/vanillajs/components/director/build/director.js
+712
-0
vanilla-examples/vanillajs/components/todomvc-common/base.css
...lla-examples/vanillajs/components/todomvc-common/base.css
+414
-0
vanilla-examples/vanillajs/components/todomvc-common/base.js
vanilla-examples/vanillajs/components/todomvc-common/base.js
+38
-0
vanilla-examples/vanillajs/components/todomvc-common/bg.png
vanilla-examples/vanillajs/components/todomvc-common/bg.png
+0
-0
vanilla-examples/vanillajs/index.html
vanilla-examples/vanillajs/index.html
+20
-8
vanilla-examples/vanillajs/js/app.js
vanilla-examples/vanillajs/js/app.js
+53
-293
vanilla-examples/vanillajs/js/controller.js
vanilla-examples/vanillajs/js/controller.js
+344
-0
vanilla-examples/vanillajs/js/helpers.js
vanilla-examples/vanillajs/js/helpers.js
+19
-0
vanilla-examples/vanillajs/js/model.js
vanilla-examples/vanillajs/js/model.js
+119
-0
vanilla-examples/vanillajs/js/store.js
vanilla-examples/vanillajs/js/store.js
+153
-0
vanilla-examples/vanillajs/js/view.js
vanilla-examples/vanillajs/js/view.js
+91
-0
No files found.
vanilla-examples/vanillajs/component.json
0 → 100644
View file @
e6f14976
{
"name"
:
"todomvc-vanillajs"
,
"version"
:
"0.0.0"
,
"dependencies"
:
{
"todomvc-common"
:
"~0.1.4"
,
"director"
:
"~1.2.0"
}
}
vanilla-examples/vanillajs/components/director/build/director.js
0 → 100644
View file @
e6f14976
This diff is collapsed.
Click to expand it.
vanilla-examples/vanillajs/components/todomvc-common/base.css
0 → 100644
View file @
e6f14976
html
,
body
{
margin
:
0
;
padding
:
0
;
}
button
{
margin
:
0
;
padding
:
0
;
border
:
0
;
background
:
none
;
font-size
:
100%
;
vertical-align
:
baseline
;
font-family
:
inherit
;
color
:
inherit
;
-webkit-appearance
:
none
;
/*-moz-appearance: none;*/
-ms-appearance
:
none
;
-o-appearance
:
none
;
appearance
:
none
;
}
body
{
font
:
14px
'Helvetica Neue'
,
Helvetica
,
Arial
,
sans-serif
;
line-height
:
1.4em
;
background
:
#eaeaea
url('bg.png')
;
color
:
#4d4d4d
;
width
:
550px
;
margin
:
0
auto
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
-o-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
}
#todoapp
{
background
:
#fff
;
background
:
rgba
(
255
,
255
,
255
,
0.9
);
margin
:
130px
0
40px
0
;
border
:
1px
solid
#ccc
;
position
:
relative
;
border-top-left-radius
:
2px
;
border-top-right-radius
:
2px
;
box-shadow
:
0
2px
6px
0
rgba
(
0
,
0
,
0
,
0.2
),
0
25px
50px
0
rgba
(
0
,
0
,
0
,
0.15
);
}
#todoapp
:before
{
content
:
''
;
border-left
:
1px
solid
#f5d6d6
;
border-right
:
1px
solid
#f5d6d6
;
width
:
2px
;
position
:
absolute
;
top
:
0
;
left
:
40px
;
height
:
100%
;
}
#todoapp
input
::-webkit-input-placeholder
{
font-style
:
italic
;
}
#todoapp
input
:-moz-placeholder
{
font-style
:
italic
;
color
:
#a9a9a9
;
}
#todoapp
h1
{
position
:
absolute
;
top
:
-120px
;
width
:
100%
;
font-size
:
70px
;
font-weight
:
bold
;
text-align
:
center
;
color
:
#b3b3b3
;
color
:
rgba
(
255
,
255
,
255
,
0.3
);
text-shadow
:
-1px
-1px
rgba
(
0
,
0
,
0
,
0.2
);
-webkit-text-rendering
:
optimizeLegibility
;
-moz-text-rendering
:
optimizeLegibility
;
-ms-text-rendering
:
optimizeLegibility
;
-o-text-rendering
:
optimizeLegibility
;
text-rendering
:
optimizeLegibility
;
}
#header
{
padding-top
:
15px
;
border-radius
:
inherit
;
}
#header
:before
{
content
:
''
;
position
:
absolute
;
top
:
0
;
right
:
0
;
left
:
0
;
height
:
15px
;
z-index
:
2
;
border-bottom
:
1px
solid
#6c615c
;
background
:
#8d7d77
;
background
:
-webkit-gradient
(
linear
,
left
top
,
left
bottom
,
from
(
rgba
(
132
,
110
,
100
,
0.8
)),
to
(
rgba
(
101
,
84
,
76
,
0.8
)));
background
:
-webkit-linear-gradient
(
top
,
rgba
(
132
,
110
,
100
,
0.8
),
rgba
(
101
,
84
,
76
,
0.8
));
background
:
-moz-linear-gradient
(
top
,
rgba
(
132
,
110
,
100
,
0.8
),
rgba
(
101
,
84
,
76
,
0.8
));
background
:
-o-linear-gradient
(
top
,
rgba
(
132
,
110
,
100
,
0.8
),
rgba
(
101
,
84
,
76
,
0.8
));
background
:
-ms-linear-gradient
(
top
,
rgba
(
132
,
110
,
100
,
0.8
),
rgba
(
101
,
84
,
76
,
0.8
));
background
:
linear-gradient
(
top
,
rgba
(
132
,
110
,
100
,
0.8
),
rgba
(
101
,
84
,
76
,
0.8
));
filter
:
progid
:
DXImageTransform
.
Microsoft
.
gradient
(
GradientType
=
0
,
StartColorStr
=
'#9d8b83'
,
EndColorStr
=
'#847670'
);
border-top-left-radius
:
1px
;
border-top-right-radius
:
1px
;
}
#new-todo
,
.edit
{
position
:
relative
;
margin
:
0
;
width
:
100%
;
font-size
:
24px
;
font-family
:
inherit
;
line-height
:
1.4em
;
border
:
0
;
outline
:
none
;
color
:
inherit
;
padding
:
6px
;
border
:
1px
solid
#999
;
box-shadow
:
inset
0
-1px
5px
0
rgba
(
0
,
0
,
0
,
0.2
);
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
-ms-box-sizing
:
border-box
;
-o-box-sizing
:
border-box
;
box-sizing
:
border-box
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
-o-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
}
#new-todo
{
padding
:
16px
16px
16px
60px
;
border
:
none
;
background
:
rgba
(
0
,
0
,
0
,
0.02
);
z-index
:
2
;
box-shadow
:
none
;
}
#main
{
position
:
relative
;
z-index
:
2
;
border-top
:
1px
dotted
#adadad
;
}
label
[
for
=
'toggle-all'
]
{
display
:
none
;
}
#toggle-all
{
position
:
absolute
;
top
:
-42px
;
left
:
-4px
;
width
:
40px
;
text-align
:
center
;
border
:
none
;
/* Mobile Safari */
}
#toggle-all
:before
{
content
:
'»'
;
font-size
:
28px
;
color
:
#d9d9d9
;
padding
:
0
25px
7px
;
}
#toggle-all
:checked:before
{
color
:
#737373
;
}
#todo-list
{
margin
:
0
;
padding
:
0
;
list-style
:
none
;
}
#todo-list
li
{
position
:
relative
;
font-size
:
24px
;
border-bottom
:
1px
dotted
#ccc
;
}
#todo-list
li
:last-child
{
border-bottom
:
none
;
}
#todo-list
li
.editing
{
border-bottom
:
none
;
padding
:
0
;
}
#todo-list
li
.editing
.edit
{
display
:
block
;
width
:
506px
;
padding
:
13px
17px
12px
17px
;
margin
:
0
0
0
43px
;
}
#todo-list
li
.editing
.view
{
display
:
none
;
}
#todo-list
li
.toggle
{
text-align
:
center
;
width
:
40px
;
/* auto, since non-WebKit browsers doesn't support input styling */
height
:
auto
;
position
:
absolute
;
top
:
0
;
bottom
:
0
;
margin
:
auto
0
;
border
:
none
;
/* Mobile Safari */
-webkit-appearance
:
none
;
/*-moz-appearance: none;*/
-ms-appearance
:
none
;
-o-appearance
:
none
;
appearance
:
none
;
}
#todo-list
li
.toggle
:after
{
content
:
'✔'
;
line-height
:
43px
;
/* 40 + a couple of pixels visual adjustment */
font-size
:
20px
;
color
:
#d9d9d9
;
text-shadow
:
0
-1px
0
#bfbfbf
;
}
#todo-list
li
.toggle
:checked:after
{
color
:
#85ada7
;
text-shadow
:
0
1px
0
#669991
;
bottom
:
1px
;
position
:
relative
;
}
#todo-list
li
label
{
word-break
:
break-word
;
padding
:
15px
;
margin-left
:
45px
;
display
:
block
;
line-height
:
1.2
;
-webkit-transition
:
color
0.4s
;
-moz-transition
:
color
0.4s
;
-ms-transition
:
color
0.4s
;
-o-transition
:
color
0.4s
;
transition
:
color
0.4s
;
}
#todo-list
li
.completed
label
{
color
:
#a9a9a9
;
text-decoration
:
line-through
;
}
#todo-list
li
.destroy
{
display
:
none
;
position
:
absolute
;
top
:
0
;
right
:
10px
;
bottom
:
0
;
width
:
40px
;
height
:
40px
;
margin
:
auto
0
;
font-size
:
22px
;
color
:
#a88a8a
;
-webkit-transition
:
all
0.2s
;
-moz-transition
:
all
0.2s
;
-ms-transition
:
all
0.2s
;
-o-transition
:
all
0.2s
;
transition
:
all
0.2s
;
}
#todo-list
li
.destroy
:hover
{
text-shadow
:
0
0
1px
#000
,
0
0
10px
rgba
(
199
,
107
,
107
,
0.8
);
-webkit-transform
:
scale
(
1.3
);
-moz-transform
:
scale
(
1.3
);
-ms-transform
:
scale
(
1.3
);
-o-transform
:
scale
(
1.3
);
transform
:
scale
(
1.3
);
}
#todo-list
li
.destroy
:after
{
content
:
'✖'
;
}
#todo-list
li
:hover
.destroy
{
display
:
block
;
}
#todo-list
li
.edit
{
display
:
none
;
}
#todo-list
li
.editing
:last-child
{
margin-bottom
:
-1px
;
}
#footer
{
color
:
#777
;
padding
:
0
15px
;
position
:
absolute
;
right
:
0
;
bottom
:
-31px
;
left
:
0
;
height
:
20px
;
z-index
:
1
;
text-align
:
center
;
}
#footer
:before
{
content
:
''
;
position
:
absolute
;
right
:
0
;
bottom
:
31px
;
left
:
0
;
height
:
50px
;
z-index
:
-1
;
box-shadow
:
0
1px
1px
rgba
(
0
,
0
,
0
,
0.3
),
0
6px
0
-3px
rgba
(
255
,
255
,
255
,
0.8
),
0
7px
1px
-3px
rgba
(
0
,
0
,
0
,
0.3
),
0
43px
0
-6px
rgba
(
255
,
255
,
255
,
0.8
),
0
44px
2px
-6px
rgba
(
0
,
0
,
0
,
0.2
);
}
#todo-count
{
float
:
left
;
text-align
:
left
;
}
#filters
{
margin
:
0
;
padding
:
0
;
list-style
:
none
;
position
:
absolute
;
right
:
0
;
left
:
0
;
}
#filters
li
{
display
:
inline
;
}
#filters
li
a
{
color
:
#83756f
;
margin
:
2px
;
text-decoration
:
none
;
}
#filters
li
a
.selected
{
font-weight
:
bold
;
}
#clear-completed
{
float
:
right
;
position
:
relative
;
line-height
:
20px
;
text-decoration
:
none
;
background
:
rgba
(
0
,
0
,
0
,
0.1
);
font-size
:
11px
;
padding
:
0
10px
;
border-radius
:
3px
;
box-shadow
:
0
-1px
0
0
rgba
(
0
,
0
,
0
,
0.2
);
}
#clear-completed
:hover
{
background
:
rgba
(
0
,
0
,
0
,
0.15
);
box-shadow
:
0
-1px
0
0
rgba
(
0
,
0
,
0
,
0.3
);
}
#info
{
margin
:
65px
auto
0
;
color
:
#a6a6a6
;
font-size
:
12px
;
text-shadow
:
0
1px
0
rgba
(
255
,
255
,
255
,
0.7
);
text-align
:
center
;
}
#info
a
{
color
:
inherit
;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media
screen
and
(
-webkit-min-device-pixel-ratio
:
0
)
{
#toggle-all
,
#todo-list
li
.toggle
{
background
:
none
;
}
#todo-list
li
.toggle
{
height
:
40px
;
}
#toggle-all
{
top
:
-56px
;
left
:
-15px
;
width
:
65px
;
height
:
41px
;
-webkit-transform
:
rotate
(
90deg
);
transform
:
rotate
(
90deg
);
-webkit-appearance
:
none
;
appearance
:
none
;
}
}
.hidden
{
display
:
none
;
}
vanilla-examples/vanillajs/components/todomvc-common/base.js
0 → 100644
View file @
e6f14976
(
function
()
{
'
use strict
'
;
if
(
location
.
hostname
===
'
todomvc.com
'
)
{
window
.
_gaq
=
[[
'
_setAccount
'
,
'
UA-31081062-1
'
],[
'
_trackPageview
'
]];(
function
(
d
,
t
){
var
g
=
d
.
createElement
(
t
),
s
=
d
.
getElementsByTagName
(
t
)[
0
];
g
.
src
=
'
//www.google-analytics.com/ga.js
'
;
s
.
parentNode
.
insertBefore
(
g
,
s
)}(
document
,
'
script
'
));
}
function
getSourcePath
()
{
// If accessed via addyosmani.github.io/todomvc/, strip the project path.
if
(
location
.
hostname
.
indexOf
(
'
github.io
'
)
>
0
)
{
return
location
.
pathname
.
replace
(
/todomvc
\/
/
,
''
);
}
return
location
.
pathname
;
}
function
appendSourceLink
()
{
var
sourceLink
=
document
.
createElement
(
'
a
'
);
var
paragraph
=
document
.
createElement
(
'
p
'
);
var
footer
=
document
.
getElementById
(
'
info
'
);
var
urlBase
=
'
https://github.com/addyosmani/todomvc/tree/gh-pages
'
;
if
(
footer
)
{
sourceLink
.
href
=
urlBase
+
getSourcePath
();
sourceLink
.
appendChild
(
document
.
createTextNode
(
'
Check out the source
'
));
paragraph
.
appendChild
(
sourceLink
);
footer
.
appendChild
(
paragraph
);
}
}
function
redirect
()
{
if
(
location
.
hostname
===
'
addyosmani.github.io
'
)
{
location
.
href
=
location
.
href
.
replace
(
'
addyosmani.github.io/todomvc
'
,
'
todomvc.com
'
);
}
}
appendSourceLink
();
redirect
();
})();
vanilla-examples/vanillajs/components/todomvc-common/bg.png
0 → 100644
View file @
e6f14976
2.08 KB
vanilla-examples/vanillajs/index.html
View file @
e6f14976
...
...
@@ -4,10 +4,7 @@
<meta
charset=
"utf-8"
>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge,chrome=1"
>
<title>
VanillaJS • TodoMVC
</title>
<link
rel=
"stylesheet"
href=
"../../assets/base.css"
>
<!--[if IE]>
<script src="../../assets/ie.js"></script>
<![endif]-->
<link
rel=
"stylesheet"
href=
"components/todomvc-common/base.css"
>
</head>
<body>
<section
id=
"todoapp"
>
...
...
@@ -22,16 +19,31 @@
</section>
<footer
id=
"footer"
>
<span
id=
"todo-count"
></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"
>
Clear completed
</button>
</footer>
</section>
<footer
id=
"info"
>
<p>
Double-click to edit a todo
</p>
<p>
Created by
<a
href=
"http://twitter.com/ffesseler"
>
Florian Fesseler
</a></p>
<p>
Cleanup, edits by
<a
href=
"http://github.com/boushley"
>
Aaron Boushley
</a></p>
<p>
Part of
<a
href=
"http://todomvc.com"
>
TodoMVC
</a></p>
<p>
Created by
<a
href=
"http://twitter.com/oscargodson"
>
Oscar Godson
</a></p>
</footer>
<script
src=
"../../assets/base.js"
></script>
<script
src=
"components/todomvc-common/base.js"
></script>
<script
src=
"components/director/build/director.js"
></script>
<script
src=
"js/helpers.js"
></script>
<script
src=
"js/store.js"
></script>
<script
src=
"js/model.js"
></script>
<script
src=
"js/view.js"
></script>
<script
src=
"js/controller.js"
></script>
<script
src=
"js/app.js"
></script>
</body>
</html>
vanilla-examples/vanillajs/js/app.js
View file @
e6f14976
/*global Store, Model, View, Controller, $$ */
(
function
()
{
'
use strict
'
;
var
todos
=
[],
stat
=
{},
ENTER_KEY
=
13
;
window
.
addEventListener
(
'
load
'
,
windowLoadHandler
,
false
);
function
Todo
(
title
,
completed
)
{
this
.
id
=
getUuid
();
this
.
title
=
title
;
this
.
completed
=
completed
;
}
function
Stat
()
{
this
.
todoLeft
=
0
;
this
.
todoCompleted
=
0
;
this
.
totalTodo
=
0
;
}
function
windowLoadHandler
()
{
loadTodos
();
refreshData
();
addEventListeners
();
}
function
addEventListeners
()
{
document
.
getElementById
(
'
new-todo
'
).
addEventListener
(
'
keypress
'
,
newTodoKeyPressHandler
,
false
);
document
.
getElementById
(
'
toggle-all
'
).
addEventListener
(
'
change
'
,
toggleAllChangeHandler
,
false
);
}
function
inputEditTodoKeyPressHandler
(
event
)
{
var
inputEditTodo
=
event
.
target
,
trimmedText
=
inputEditTodo
.
value
.
trim
(),
todoId
=
event
.
target
.
id
.
slice
(
6
);
if
(
trimmedText
)
{
if
(
event
.
keyCode
===
ENTER_KEY
)
{
editTodo
(
todoId
,
trimmedText
);
}
}
else
{
removeTodoById
(
todoId
);
refreshData
();
}
}
function
inputEditTodoBlurHandler
(
event
)
{
var
inputEditTodo
=
event
.
target
,
todoId
=
event
.
target
.
id
.
slice
(
6
);
editTodo
(
todoId
,
inputEditTodo
.
value
);
}
function
newTodoKeyPressHandler
(
event
)
{
if
(
event
.
keyCode
===
ENTER_KEY
)
{
addTodo
(
document
.
getElementById
(
'
new-todo
'
).
value
);
}
}
function
toggleAllChangeHandler
(
event
)
{
for
(
var
i
in
todos
)
{
todos
[
i
].
completed
=
event
.
target
.
checked
;
}
refreshData
();
}
function
spanDeleteClickHandler
(
event
)
{
removeTodoById
(
event
.
target
.
getAttribute
(
'
data-todo-id
'
));
refreshData
();
}
function
hrefClearClickHandler
()
{
removeTodosCompleted
();
refreshData
();
}
function
todoContentHandler
(
event
)
{
var
todoId
=
event
.
target
.
getAttribute
(
'
data-todo-id
'
),
div
=
document
.
getElementById
(
'
li_
'
+
todoId
),
inputEditTodo
=
document
.
getElementById
(
'
input_
'
+
todoId
);
div
.
className
=
'
editing
'
;
inputEditTodo
.
focus
();
}
function
checkboxChangeHandler
(
event
)
{
var
checkbox
=
event
.
target
,
todo
=
getTodoById
(
checkbox
.
getAttribute
(
'
data-todo-id
'
));
todo
.
completed
=
checkbox
.
checked
;
refreshData
();
}
function
loadTodos
()
{
if
(
!
localStorage
.
getItem
(
'
todos-vanillajs
'
))
{
localStorage
.
setItem
(
'
todos-vanillajs
'
,
JSON
.
stringify
([]));
}
todos
=
JSON
.
parse
(
localStorage
.
getItem
(
'
todos-vanillajs
'
));
}
function
addTodo
(
text
)
{
var
trimmedText
=
text
.
trim
();
if
(
trimmedText
)
{
var
todo
=
new
Todo
(
trimmedText
,
false
);
todos
.
push
(
todo
);
refreshData
();
}
}
function
editTodo
(
todoId
,
text
)
{
var
i
,
l
;
for
(
i
=
0
,
l
=
todos
.
length
;
i
<
l
;
i
++
)
{
if
(
todos
[
i
].
id
===
todoId
)
{
todos
[
i
].
title
=
text
;
}
}
refreshData
();
}
function
removeTodoById
(
id
)
{
var
i
=
todos
.
length
;
while
(
i
--
)
{
if
(
todos
[
i
].
id
===
id
)
{
todos
.
splice
(
i
,
1
);
/**
* Sets up a brand new Todo list.
*
* @param {string} name The name of your new to do list.
*/
function
Todo
(
name
)
{
this
.
storage
=
new
Store
(
name
);
this
.
model
=
new
Model
(
this
.
storage
);
this
.
view
=
new
View
();
this
.
controller
=
new
Controller
(
this
.
model
,
this
.
view
);
}
}
}
function
removeTodosCompleted
()
{
var
i
=
todos
.
length
;
while
(
i
--
)
{
console
.
log
(
i
);
if
(
todos
[
i
].
completed
)
{
todos
.
splice
(
i
,
1
);
}
}
}
function
getTodoById
(
id
)
{
var
i
,
l
;
for
(
i
=
0
,
l
=
todos
.
length
;
i
<
l
;
i
++
)
{
if
(
todos
[
i
].
id
===
id
)
{
return
todos
[
i
];
}
}
}
function
refreshData
()
{
saveTodos
();
computeStats
();
redrawTodosUI
();
redrawStatsUI
();
changeToggleAllCheckboxState
();
}
function
saveTodos
()
{
localStorage
.
setItem
(
'
todos-vanillajs
'
,
JSON
.
stringify
(
todos
));
}
function
computeStats
()
{
var
i
,
l
;
stat
=
new
Stat
();
stat
.
totalTodo
=
todos
.
length
;
for
(
i
=
0
,
l
=
todos
.
length
;
i
<
l
;
i
++
)
{
if
(
todos
[
i
].
completed
)
{
stat
.
todoCompleted
++
;
}
}
stat
.
todoLeft
=
stat
.
totalTodo
-
stat
.
todoCompleted
;
}
function
redrawTodosUI
()
{
var
todo
,
checkbox
,
label
,
deleteLink
,
divDisplay
,
inputEditTodo
,
li
,
i
,
l
,
ul
=
document
.
getElementById
(
'
todo-list
'
);
var
todo
=
new
Todo
(
'
todos-vanillajs
'
);
document
.
getElementById
(
'
main
'
).
style
.
display
=
todos
.
length
?
'
block
'
:
'
none
'
;
/**
* Finds the model ID of the clicked DOM element
*
* @param {object} target The starting point in the DOM for it to try to find
* the ID of the model.
*/
function
lookupId
(
target
)
{
var
lookup
=
target
;
ul
.
innerHTML
=
''
;
document
.
getElementById
(
'
new-todo
'
).
value
=
''
;
for
(
i
=
0
,
l
=
todos
.
length
;
i
<
l
;
i
++
)
{
todo
=
todos
[
i
];
// create checkbox
checkbox
=
document
.
createElement
(
'
input
'
);
checkbox
.
className
=
'
toggle
'
;
checkbox
.
setAttribute
(
'
data-todo-id
'
,
todo
.
id
);
checkbox
.
type
=
'
checkbox
'
;
checkbox
.
addEventListener
(
'
change
'
,
checkboxChangeHandler
);
// create div text
label
=
document
.
createElement
(
'
label
'
);
label
.
setAttribute
(
'
data-todo-id
'
,
todo
.
id
);
label
.
appendChild
(
document
.
createTextNode
(
todo
.
title
));
label
.
addEventListener
(
'
dblclick
'
,
todoContentHandler
);
// create delete button
deleteLink
=
document
.
createElement
(
'
button
'
);
deleteLink
.
className
=
'
destroy
'
;
deleteLink
.
setAttribute
(
'
data-todo-id
'
,
todo
.
id
);
deleteLink
.
addEventListener
(
'
click
'
,
spanDeleteClickHandler
);
// create divDisplay
divDisplay
=
document
.
createElement
(
'
div
'
);
divDisplay
.
className
=
'
view
'
;
divDisplay
.
setAttribute
(
'
data-todo-id
'
,
todo
.
id
);
divDisplay
.
appendChild
(
checkbox
);
divDisplay
.
appendChild
(
label
);
divDisplay
.
appendChild
(
deleteLink
);
// create todo input
inputEditTodo
=
document
.
createElement
(
'
input
'
);
inputEditTodo
.
id
=
'
input_
'
+
todo
.
id
;
inputEditTodo
.
className
=
'
edit
'
;
inputEditTodo
.
value
=
todo
.
title
;
inputEditTodo
.
addEventListener
(
'
keypress
'
,
inputEditTodoKeyPressHandler
);
inputEditTodo
.
addEventListener
(
'
blur
'
,
inputEditTodoBlurHandler
);
// create li
li
=
document
.
createElement
(
'
li
'
);
li
.
id
=
'
li_
'
+
todo
.
id
;
li
.
appendChild
(
divDisplay
);
li
.
appendChild
(
inputEditTodo
);
if
(
todo
.
completed
)
{
li
.
className
+=
'
completed
'
;
checkbox
.
checked
=
true
;
while
(
lookup
.
nodeName
!==
'
LI
'
)
{
lookup
=
lookup
.
parentNode
;
}
ul
.
appendChild
(
li
);
}
return
lookup
.
dataset
.
id
;
}
function
changeToggleAllCheckboxState
()
{
var
toggleAll
=
document
.
getElementById
(
'
toggle-all
'
);
// When the enter key is pressed fire the addItem method.
$$
(
'
#new-todo
'
).
addEventListener
(
'
keypress
'
,
function
(
e
)
{
todo
.
controller
.
addItem
(
e
);
});
toggleAll
.
checked
=
stat
.
todoCompleted
===
todos
.
length
;
}
// A delegation event. Will check what item was clicked whenever you click on any
// part of a list item.
$$
(
'
#todo-list
'
).
addEventListener
(
'
click
'
,
function
(
e
)
{
var
target
=
e
.
target
;
function
redrawStatsUI
()
{
removeChildren
(
document
.
getElementsByTagName
(
'
footer
'
)[
0
]);
document
.
getElementById
(
'
footer
'
).
style
.
display
=
todos
.
length
?
'
block
'
:
'
none
'
;
if
(
stat
.
todoCompleted
)
{
drawTodoClear
();
}
if
(
stat
.
totalTodo
)
{
drawTodoCount
();
// If you click a destroy button
if
(
target
.
className
.
indexOf
(
'
destroy
'
)
>
-
1
)
{
todo
.
controller
.
removeItem
(
lookupId
(
target
));
}
}
function
drawTodoCount
()
{
var
number
=
document
.
createElement
(
'
strong
'
),
remaining
=
document
.
createElement
(
'
span
'
),
text
=
'
'
+
(
stat
.
todoLeft
===
1
?
'
item
'
:
'
items
'
)
+
'
left
'
;
// create remaining count
number
.
innerHTML
=
stat
.
todoLeft
;
remaining
.
id
=
'
todo-count
'
;
remaining
.
appendChild
(
number
);
remaining
.
appendChild
(
document
.
createTextNode
(
text
));
document
.
getElementsByTagName
(
'
footer
'
)[
0
].
appendChild
(
remaining
);
// If you click the checkmark
if
(
target
.
className
.
indexOf
(
'
toggle
'
)
>
-
1
)
{
todo
.
controller
.
toggleComplete
(
lookupId
(
target
),
target
);
}
function
drawTodoClear
()
{
var
buttonClear
=
document
.
createElement
(
'
button
'
);
buttonClear
.
id
=
'
clear-completed
'
;
buttonClear
.
addEventListener
(
'
click
'
,
hrefClearClickHandler
);
buttonClear
.
innerHTML
=
'
Clear completed (
'
+
stat
.
todoCompleted
+
'
)
'
;
});
document
.
getElementsByTagName
(
'
footer
'
)[
0
].
appendChild
(
buttonClear
);
}
$$
(
'
#todo-list
'
).
addEventListener
(
'
dblclick
'
,
function
(
e
)
{
var
target
=
e
.
target
;
function
removeChildren
(
node
)
{
node
.
innerHTML
=
''
;
if
(
target
.
nodeName
===
'
LABEL
'
)
{
todo
.
controller
.
editItem
(
lookupId
(
target
),
target
)
;
}
});
function
getUuid
(
)
{
var
i
,
random
,
uuid
=
''
;
$$
(
'
#toggle-all
'
).
addEventListener
(
'
click
'
,
function
(
e
)
{
todo
.
controller
.
toggleAll
(
e
);
})
;
for
(
i
=
0
;
i
<
32
;
i
++
)
{
random
=
Math
.
random
()
*
16
|
0
;
if
(
i
===
8
||
i
===
12
||
i
===
16
||
i
===
20
)
{
uuid
+=
'
-
'
;
}
uuid
+=
(
i
===
12
?
4
:
(
i
===
16
?
(
random
&
3
|
8
)
:
random
)).
toString
(
16
);
}
return
uuid
;
}
$$
(
'
#clear-completed
'
).
addEventListener
(
'
click
'
,
function
()
{
todo
.
controller
.
removeCompletedItems
();
});
})();
vanilla-examples/vanillajs/js/controller.js
0 → 100644
View file @
e6f14976
/*global Router, $$, $ */
(
function
(
window
)
{
'
use strict
'
;
/**
* Takes a model and view and acts as the controller between them
*
* @constructor
* @param {object} model The model constructor
* @param {object} view The view constructor
*/
function
Controller
(
model
,
view
)
{
this
.
model
=
model
;
this
.
view
=
view
;
this
.
ENTER_KEY
=
13
;
this
.
ESCAPE_KEY
=
27
;
this
.
$main
=
$$
(
'
#main
'
);
this
.
$toggleAll
=
$$
(
'
#toggle-all
'
);
this
.
$todoList
=
$$
(
'
#todo-list
'
);
this
.
$todoItemCounter
=
$$
(
'
#todo-count
'
);
this
.
$clearCompleted
=
$$
(
'
#clear-completed
'
);
this
.
$footer
=
$$
(
'
#footer
'
);
this
.
router
=
new
Router
();
this
.
router
.
init
();
window
.
addEventListener
(
'
load
'
,
function
()
{
this
.
_updateFilterState
();
}.
bind
(
this
));
// Couldn't figure out how to get flatiron to run some code on all pages. I
// tried '*', but then it overwrites ALL handlers for all the other pages
// and only runs this.
window
.
addEventListener
(
'
hashchange
'
,
function
()
{
this
.
_updateFilterState
();
}.
bind
(
this
));
// Make sure on page load we start with a hash to trigger the flatiron and
// onhashchange routes
if
(
window
.
location
.
href
.
indexOf
(
'
#
'
)
===
-
1
)
{
window
.
location
.
hash
=
'
#/
'
;
}
}
/**
* An event to fire on load. Will get all items and display them in the
* todo-list
*/
Controller
.
prototype
.
showAll
=
function
()
{
this
.
model
.
read
(
function
(
data
)
{
this
.
$todoList
.
innerHTML
=
this
.
view
.
show
(
data
);
}.
bind
(
this
));
};
/**
* Renders all active tasks
*/
Controller
.
prototype
.
showActive
=
function
()
{
this
.
model
.
read
({
completed
:
0
},
function
(
data
)
{
this
.
$todoList
.
innerHTML
=
this
.
view
.
show
(
data
);
}.
bind
(
this
));
};
/**
* Renders all completed tasks
*/
Controller
.
prototype
.
showCompleted
=
function
()
{
this
.
model
.
read
({
completed
:
1
},
function
(
data
)
{
this
.
$todoList
.
innerHTML
=
this
.
view
.
show
(
data
);
}.
bind
(
this
));
};
/**
* An event to fire whenever you want to add an item. Simply pass in the event
* object and it'll handle the DOM insertion and saving of the new item.
*
* @param {object} e The event object
*/
Controller
.
prototype
.
addItem
=
function
(
e
)
{
var
input
=
$$
(
'
#new-todo
'
);
var
title
=
title
||
''
;
if
(
e
.
keyCode
===
this
.
ENTER_KEY
)
{
this
.
model
.
create
(
e
.
target
.
value
,
function
(
data
)
{
// We want to make sure we don't add incomplete
// items to the completed tab when you go to
// add an item and you're viewing the completed
// items
if
(
this
.
_getCurrentPage
()
!==
'
completed
'
)
{
this
.
$todoList
.
innerHTML
=
this
.
$todoList
.
innerHTML
+
this
.
view
.
show
(
data
);
}
input
.
value
=
''
;
}.
bind
(
this
));
}
this
.
_filter
();
};
/**
* Hides the label text and creates an input to edit the title of the item.
* When you hit enter or blur out of the input it saves it and updates the UI
* with the new name.
*
* @param {number} id The id of the item to edit
* @param {object} label The label you want to edit the text of
*/
Controller
.
prototype
.
editItem
=
function
(
id
,
label
)
{
var
li
=
label
;
// This finds the <label>'s parent <li>
while
(
li
.
nodeName
!==
'
LI
'
)
{
li
=
li
.
parentNode
;
}
var
onSaveHandler
=
function
()
{
var
value
=
input
.
value
.
trim
();
var
discarding
=
input
.
dataset
.
discard
;
if
(
value
.
length
&&
!
discarding
)
{
this
.
model
.
update
(
id
,
{
title
:
input
.
value
});
// Instead of re-rendering the whole view just update
// this piece of it
label
.
innerHTML
=
value
;
}
else
if
(
value
.
length
===
0
)
{
// No value was entered in the input. We'll remove the todo item.
this
.
removeItem
(
id
);
}
// Remove the input since we no longer need it
// Less DOM means faster rendering
li
.
removeChild
(
input
);
// Remove the editing class
li
.
className
=
li
.
className
.
replace
(
'
editing
'
,
''
);
}.
bind
(
this
);
// Append the editing class
li
.
className
=
li
.
className
+
'
editing
'
;
var
input
=
document
.
createElement
(
'
input
'
);
input
.
className
=
'
edit
'
;
// Get the innerHTML of the label instead of requesting the data from the
// ORM. If this were a real DB this would save a lot of time and would avoid
// a spinner gif.
input
.
value
=
label
.
innerHTML
;
li
.
appendChild
(
input
);
input
.
addEventListener
(
'
blur
'
,
onSaveHandler
);
input
.
addEventListener
(
'
keypress
'
,
function
(
e
)
{
if
(
e
.
keyCode
===
this
.
ENTER_KEY
)
{
// Remove the cursor from the input when you hit enter just like if it
// were a real form
input
.
blur
();
}
if
(
e
.
keyCode
===
this
.
ESCAPE_KEY
)
{
// Discard the changes
input
.
dataset
.
discard
=
true
;
input
.
blur
();
}
}.
bind
(
this
));
input
.
focus
();
};
/**
* By giving it an ID it'll find the DOM element matching that ID,
* remove it from the DOM and also remove it from storage.
*
* @param {number} id The ID of the item to remove from the DOM and
* storage
*/
Controller
.
prototype
.
removeItem
=
function
(
id
)
{
this
.
model
.
remove
(
id
,
function
()
{
this
.
$todoList
.
removeChild
(
$$
(
'
[data-id="
'
+
id
+
'
"]
'
));
}.
bind
(
this
));
this
.
_filter
();
};
/**
* Will remove all completed items from the DOM and storage.
*/
Controller
.
prototype
.
removeCompletedItems
=
function
()
{
this
.
model
.
read
({
completed
:
1
},
function
(
data
)
{
data
.
forEach
(
function
(
item
)
{
this
.
removeItem
(
item
.
id
);
}.
bind
(
this
));
}.
bind
(
this
));
this
.
_filter
();
};
/**
* Give it an ID of a model and a checkbox and it will update the item
* in storage based on the checkbox's state.
*
* @param {number} id The ID of the element to complete or uncomplete
* @param {object} checkbox The checkbox to check the state of complete
* or not
* @param {boolean|undefined} silent Prevent re-filtering the todo items
*/
Controller
.
prototype
.
toggleComplete
=
function
(
id
,
checkbox
,
silent
)
{
var
completed
=
checkbox
.
checked
?
1
:
0
;
this
.
model
.
update
(
id
,
{
completed
:
completed
},
function
()
{
var
listItem
=
$$
(
'
[data-id="
'
+
id
+
'
"]
'
);
if
(
!
listItem
)
{
return
;
}
listItem
.
className
=
completed
?
'
completed
'
:
''
;
// In case it was toggled from an event and not by clicking the checkbox
listItem
.
querySelector
(
'
input
'
).
checked
=
completed
;
});
if
(
!
silent
)
{
this
.
_filter
();
}
};
/**
* Will toggle ALL checkboxe's on/off state and completeness of models.
* Just pass in the event object.
*
* @param {object} e The event object
*/
Controller
.
prototype
.
toggleAll
=
function
(
e
)
{
var
completed
=
e
.
target
.
checked
?
1
:
0
;
var
query
=
0
;
if
(
completed
===
0
)
{
query
=
1
;
}
this
.
model
.
read
({
completed
:
query
},
function
(
data
)
{
data
.
forEach
(
function
(
item
)
{
this
.
toggleComplete
(
item
.
id
,
e
.
target
,
true
);
}.
bind
(
this
));
}.
bind
(
this
));
this
.
_filter
();
};
/**
* Updates the pieces of the page which change depending on the remaining
* number of todos.
*/
Controller
.
prototype
.
_updateCount
=
function
()
{
var
todos
=
this
.
model
.
getCount
();
this
.
$todoItemCounter
.
innerHTML
=
this
.
view
.
itemCounter
(
todos
.
active
);
this
.
$clearCompleted
.
innerHTML
=
this
.
view
.
clearCompletedButton
(
todos
.
completed
);
this
.
$clearCompleted
.
style
.
display
=
todos
.
completed
>
0
?
'
block
'
:
'
none
'
;
this
.
$toggleAll
.
checked
=
todos
.
completed
===
todos
.
total
;
this
.
_toggleFrame
(
todos
);
};
/**
* The main body and footer elements should not be visible when there are no
* todos left.
*
* @param {object} todos Contains a count of all todos, and their statuses.
*/
Controller
.
prototype
.
_toggleFrame
=
function
(
todos
)
{
var
frameDisplay
=
this
.
$main
.
style
.
display
;
var
frameVisible
=
frameDisplay
===
'
block
'
||
frameDisplay
===
''
;
if
(
todos
.
total
===
0
&&
frameVisible
)
{
this
.
$main
.
style
.
display
=
'
none
'
;
this
.
$footer
.
style
.
display
=
'
none
'
;
}
if
(
todos
.
total
>
0
&&
!
frameVisible
)
{
this
.
$main
.
style
.
display
=
'
block
'
;
this
.
$footer
.
style
.
display
=
'
block
'
;
}
};
/**
* Re-filters the todo items, based on the active route.
*/
Controller
.
prototype
.
_filter
=
function
()
{
var
activeRoute
=
this
.
_activeRoute
.
charAt
(
0
).
toUpperCase
()
+
this
.
_activeRoute
.
substr
(
1
);
// Update the elements on the page, which change with each completed todo
this
.
_updateCount
();
// If the last active route isn't "All", or we're switching routes, we
// re-create the todo item elements, calling:
// this.show[All|Active|Completed]();
if
(
this
.
_lastActiveRoute
!==
'
All
'
||
this
.
_lastActiveRoute
!==
activeRoute
)
{
this
[
'
show
'
+
activeRoute
]();
}
this
.
_lastActiveRoute
=
activeRoute
;
};
/**
* Simply updates the filter nav's selected states
*/
Controller
.
prototype
.
_updateFilterState
=
function
()
{
var
currentPage
=
this
.
_getCurrentPage
()
||
''
;
// Store a reference to the active route, allowing us to re-filter todo
// items as they are marked complete or incomplete.
this
.
_activeRoute
=
currentPage
;
if
(
currentPage
===
''
)
{
this
.
_activeRoute
=
'
All
'
;
}
this
.
_filter
();
// Remove all other selected states. We loop through all of them in case the
// UI gets in a funky state with two selected.
$
(
'
#filters .selected
'
).
each
(
function
(
item
)
{
item
.
className
=
''
;
});
$$
(
'
#filters [href="#/
'
+
currentPage
+
'
"]
'
).
className
=
'
selected
'
;
};
/**
* A getter for getting the current page
*/
Controller
.
prototype
.
_getCurrentPage
=
function
()
{
return
document
.
location
.
hash
.
split
(
'
/
'
)[
1
];
};
// Export to window
window
.
Controller
=
Controller
;
})(
window
);
vanilla-examples/vanillajs/js/helpers.js
0 → 100644
View file @
e6f14976
(
function
(
window
)
{
'
use strict
'
;
// Cache the querySelector/All for easier and faster reuse
window
.
$
=
document
.
querySelectorAll
.
bind
(
document
);
window
.
$$
=
document
.
querySelector
.
bind
(
document
);
// Allow for looping on Objects by chaining:
// $('.foo').each(function () {})
Object
.
prototype
.
each
=
function
(
callback
)
{
for
(
var
x
in
this
)
{
if
(
this
.
hasOwnProperty
(
x
))
{
callback
.
call
(
this
,
this
[
x
]);
}
}
};
})(
window
);
vanilla-examples/vanillajs/js/model.js
0 → 100644
View file @
e6f14976
(
function
(
window
)
{
'
use strict
'
;
/**
* Creates a new Model instance and hooks up the storage.
*
* @constructor
* @param {object} storage A reference to the client side storage class
*/
function
Model
(
storage
)
{
this
.
storage
=
storage
;
}
/**
* Creates a new todo model
*
* @param {string} [title] The title of the task
* @param {function} [callback] The callback to fire after the model is created
*/
Model
.
prototype
.
create
=
function
(
title
,
callback
)
{
title
=
title
||
''
;
callback
=
callback
||
function
()
{};
var
newItem
=
{
title
:
title
.
trim
(),
completed
:
0
};
this
.
storage
.
save
(
newItem
,
callback
);
};
/**
* Finds and returns a model in storage. If no query is given it'll simply
* return everything. If you pass in a string or number it'll look that up as
* the ID of the model to find. Lastly, you can pass it an object to match
* against.
*
* @param {string|number|object} [query] A query to match models against
* @param {function} [callback] The callback to fire after the model is found
*
* @example
* model.read(1, func); // Will find the model with an ID of 1
* model.read('1'); // Same as above
* //Below will find a model with foo equalling bar and hello equalling world.
* model.read({ foo: 'bar', hello: 'world' });
*/
Model
.
prototype
.
read
=
function
(
query
,
callback
)
{
var
queryType
=
typeof
query
;
callback
=
callback
||
function
()
{};
if
(
queryType
===
'
function
'
)
{
callback
=
query
;
return
this
.
storage
.
findAll
(
callback
);
}
else
if
(
queryType
===
'
string
'
||
queryType
===
'
number
'
)
{
this
.
storage
.
find
({
id
:
query
},
callback
);
}
else
{
this
.
storage
.
find
(
query
,
callback
);
}
};
/**
* Updates a model by giving it an ID, data to update, and a callback to fire when
* the update is complete.
*
* @param {number} id The id of the model to update
* @param {object} data The properties to update and their new value
* @param {function} callback The callback to fire when the update is complete.
*/
Model
.
prototype
.
update
=
function
(
id
,
data
,
callback
)
{
this
.
storage
.
save
(
id
,
data
,
callback
);
};
/**
* Removes a model from storage
*
* @param {number} id The ID of the model to remove
* @param {function} callback The callback to fire when the removal is complete.
*/
Model
.
prototype
.
remove
=
function
(
id
,
callback
)
{
this
.
storage
.
remove
(
id
,
callback
);
};
/**
* WARNING: Will remove ALL data from storage.
*
* @param {function} callback The callback to fire when the storage is wiped.
*/
Model
.
prototype
.
removeAll
=
function
(
callback
)
{
this
.
storage
.
drop
(
callback
);
};
/**
* Returns a count of all todos
*/
Model
.
prototype
.
getCount
=
function
()
{
var
todos
=
{
active
:
0
,
completed
:
0
,
total
:
0
};
this
.
storage
.
findAll
(
function
(
data
)
{
data
.
each
(
function
(
todo
)
{
if
(
todo
.
completed
===
1
)
{
todos
.
completed
++
;
}
else
{
todos
.
active
++
;
}
todos
.
total
++
;
});
});
return
todos
;
};
// Export to window
window
.
Model
=
Model
;
})(
window
);
vanilla-examples/vanillajs/js/store.js
0 → 100644
View file @
e6f14976
/*jshint eqeqeq:false */
(
function
(
window
)
{
'
use strict
'
;
/**
* Creates a new client side storage object and will create an empty
* collection if no collection already exists.
*
* @param {string} name The name of our DB we want to use
* @param {function} callback Our fake DB uses callbacks because in
* real life you probably would be making AJAX calls
*/
function
Store
(
name
,
callback
)
{
var
data
;
var
dbName
;
callback
=
callback
||
function
()
{};
dbName
=
this
.
_dbName
=
name
;
if
(
!
localStorage
[
dbName
])
{
data
=
{
todos
:
[]
};
localStorage
[
dbName
]
=
JSON
.
stringify
(
data
);
}
callback
.
call
(
this
,
JSON
.
parse
(
localStorage
[
dbName
]));
}
/**
* Finds items based on a query given as a JS object
*
* @param {object} query The query to match against (i.e. {foo: 'bar'})
* @param {function} callback The callback to fire when the query has
* completed running
*
* @example
* db.find({foo: 'bar', hello: 'world'}, function (data) {
* // data will return any items that have foo: bar and
* // hello: world in their properties
* });
*/
Store
.
prototype
.
find
=
function
(
query
,
callback
)
{
var
data
=
JSON
.
parse
(
localStorage
[
this
.
_dbName
]).
todos
;
var
items
=
[];
var
found
;
callback
=
callback
||
function
()
{};
for
(
var
i
=
0
;
i
<
data
.
length
;
i
++
)
{
for
(
var
q
in
query
)
{
if
(
query
[
q
]
!==
data
[
i
][
q
])
{
found
=
false
;
break
;
}
else
{
found
=
true
;
}
}
if
(
found
)
{
items
.
push
(
data
[
i
]);
}
}
callback
.
call
(
this
,
items
);
};
/**
* Will retrieve all data from the collection
*
* @param {function} callback The callback to fire upon retrieving data
*/
Store
.
prototype
.
findAll
=
function
(
callback
)
{
callback
=
callback
||
function
()
{};
callback
.
call
(
this
,
JSON
.
parse
(
localStorage
[
this
.
_dbName
]).
todos
);
};
/**
* Will save the given data to the DB. If no item exists it will create a new
* item, otherwise it'll simply update an existing item's properties
*
* @param {number} id An optional param to enter an ID of an item to update
* @param {object} data The data to save back into the DB
* @param {function} callback The callback to fire after saving
*/
Store
.
prototype
.
save
=
function
(
id
,
updateData
,
callback
)
{
var
data
=
JSON
.
parse
(
localStorage
[
this
.
_dbName
]);
var
todos
=
data
.
todos
;
callback
=
callback
||
function
()
{};
// If an ID was actually given, find the item and update each property
if
(
typeof
id
!==
'
object
'
)
{
for
(
var
i
=
0
;
i
<
todos
.
length
;
i
++
)
{
if
(
todos
[
i
].
id
==
id
)
{
for
(
var
x
in
updateData
)
{
todos
[
i
][
x
]
=
updateData
[
x
];
}
}
}
localStorage
[
this
.
_dbName
]
=
JSON
.
stringify
(
data
);
callback
.
call
(
this
,
JSON
.
parse
(
localStorage
[
this
.
_dbName
]).
todos
);
}
else
{
callback
=
updateData
;
updateData
=
id
;
// Generate an ID
updateData
.
id
=
new
Date
().
getTime
();
todos
.
push
(
updateData
);
localStorage
[
this
.
_dbName
]
=
JSON
.
stringify
(
data
);
callback
.
call
(
this
,
[
updateData
]);
}
};
/**
* Will remove an item from the Store based on its ID
*
* @param {number} id The ID of the item you want to remove
* @param {function} callback The callback to fire after saving
*/
Store
.
prototype
.
remove
=
function
(
id
,
callback
)
{
var
data
=
JSON
.
parse
(
localStorage
[
this
.
_dbName
]);
var
todos
=
data
.
todos
;
for
(
var
i
=
0
;
i
<
todos
.
length
;
i
++
)
{
if
(
todos
[
i
].
id
==
id
)
{
todos
.
splice
(
i
,
1
);
break
;
}
}
localStorage
[
this
.
_dbName
]
=
JSON
.
stringify
(
data
);
callback
.
call
(
this
,
JSON
.
parse
(
localStorage
[
this
.
_dbName
]).
todos
);
};
/**
* Will drop all storage and start fresh
*
* @param {function} callback The callback to fire after dropping the data
*/
Store
.
prototype
.
drop
=
function
(
callback
)
{
localStorage
[
this
.
_dbName
]
=
JSON
.
stringify
({
todos
:
[]});
callback
.
call
(
this
,
JSON
.
parse
(
localStorage
[
this
.
_dbName
]).
todos
);
};
// Export to window
window
.
Store
=
Store
;
})(
window
);
vanilla-examples/vanillajs/js/view.js
0 → 100644
View file @
e6f14976
/*jshint laxbreak:true */
(
function
(
window
)
{
'
use strict
'
;
/**
* Sets up defaults for all the View methods such as a default template
*
* @constructor
*/
function
View
()
{
this
.
defaultTemplate
=
'
<li data-id="{{id}}" class="{{completed}}">
'
+
'
<div class="view">
'
+
'
<input class="toggle" type="checkbox" {{checked}}>
'
+
'
<label>{{title}}</label>
'
+
'
<button class="destroy"></button>
'
+
'
</div>
'
+
'
</li>
'
;
}
/**
* Creates an <li> HTML string and returns it for placement in your app.
*
* NOTE: In real life you should be using a templating engine such as Mustache
* or Handlebars, however, this is a vanilla JS example.
*
* @param {object} data The object containing keys you want to find in the
* template to replace.
* @returns {string} HTML String of an <li> element
*
* @example
* view.show({
* id: 1,
* title: "Hello World",
* completed: 0,
* });
*/
View
.
prototype
.
show
=
function
(
data
)
{
var
i
,
l
;
var
view
=
''
;
for
(
i
=
0
,
l
=
data
.
length
;
i
<
l
;
i
++
)
{
var
template
=
this
.
defaultTemplate
;
var
completed
=
''
;
var
checked
=
''
;
if
(
data
[
i
].
completed
===
1
)
{
completed
=
'
completed
'
;
checked
=
'
checked
'
;
}
template
=
template
.
replace
(
'
{{id}}
'
,
data
[
i
].
id
);
template
=
template
.
replace
(
'
{{title}}
'
,
data
[
i
].
title
);
template
=
template
.
replace
(
'
{{completed}}
'
,
completed
);
template
=
template
.
replace
(
'
{{checked}}
'
,
checked
);
view
=
view
+
template
;
}
return
view
;
};
/**
* Displays a counter of how many to dos are left to complete
*
* @param {number} activeTodos The number of active todos.
* @returns {string} String containing the count
*/
View
.
prototype
.
itemCounter
=
function
(
activeTodos
)
{
var
plural
=
activeTodos
===
1
?
''
:
'
s
'
;
return
'
<strong>
'
+
activeTodos
+
'
</strong> item
'
+
plural
+
'
left
'
;
};
/**
* Updates the text within the "Clear completed" button
*
* @param {[type]} completedTodos The number of completed todos.
* @returns {string} String containing the count
*/
View
.
prototype
.
clearCompletedButton
=
function
(
completedTodos
)
{
if
(
completedTodos
>
0
)
{
return
'
Clear completed (
'
+
completedTodos
+
'
)
'
;
}
else
{
return
''
;
}
};
// Export to window
window
.
View
=
View
;
})(
window
);
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