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
7cdcf5a8
Commit
7cdcf5a8
authored
Aug 26, 2015
by
Pascal Hartig
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[WIP] Elm 0.15 Update
parent
f183e30a
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
8810 additions
and
5637 deletions
+8810
-5637
examples/elm/Task.elm
examples/elm/Task.elm
+0
-133
examples/elm/Todo.elm
examples/elm/Todo.elm
+183
-106
examples/elm/elm-package.json
examples/elm/elm-package.json
+5
-5
examples/elm/elm.js
examples/elm/elm.js
+8551
-5289
examples/elm/index.html
examples/elm/index.html
+11
-33
examples/elm/node_modules/todomvc-app-css/index.css
examples/elm/node_modules/todomvc-app-css/index.css
+51
-67
examples/elm/node_modules/todomvc-common/base.js
examples/elm/node_modules/todomvc-common/base.js
+7
-2
examples/elm/package.json
examples/elm/package.json
+1
-1
examples/elm/readme.md
examples/elm/readme.md
+1
-1
No files found.
examples/elm/Task.elm
deleted
100644 → 0
View file @
f183e30a
module
Task
where
import
Html
(
..
)
import
Html
.
Attributes
(
..
)
import
Html
.
Events
(
..
)
import
Json
.
Decode
as
Json
import
LocalChannel
as
LC
import
Maybe
import
Signal
import
String
-- MODEL
type
alias
Model
=
{
description
:
String
,
completed
:
Bool
,
edits
:
Maybe
String
,
id
:
Int
}
init
:
String
->
Int
->
Model
init
desc
id
=
{
description
=
desc
,
completed
=
False
,
edits
=
Nothing
,
id
=
id
}
-- UPDATE
type
Action
=
Focus
|
Edit
String
|
Cancel
|
Commit
|
Completed
Bool
|
Delete
update
:
Action
->
Model
->
Maybe
Model
update
update
task
=
case
update
of
Focus
->
Just
{
task
|
edits
<-
Just
task
.
description
}
Edit
description
->
Just
{
task
|
edits
<-
Just
description
}
Cancel
->
Just
{
task
|
edits
<-
Nothing
}
Commit
->
case
task
.
edits
of
Nothing
->
Just
task
Just
rawDescription
->
let
description
=
String
.
trim
rawDescription
in
if
String
.
isEmpty
description
then
Nothing
else
Just
{
task
|
edits
<-
Nothing
,
description
<-
description
}
Completed
bool
->
Just
{
task
|
completed
<-
bool
}
Delete
->
Nothing
-- VIEW
view
:
LC
.
LocalChannel
(
Int
,
Action
)
->
Model
->
Html
view
channel
task
=
let
className
=
(
if
task
.
completed
then
"
completed "
else
"
"
)
++
case
task
.
edits
of
Just
_
->
"
editing"
Nothing
->
"
"
description
=
Maybe
.
withDefault
task
.
description
task
.
edits
in
li
[
class
className
]
[
div
[
class
"
view"
]
[
input
[
class
"
toggle"
,
type
'
"
checkbox"
,
checked
task
.
completed
,
onClick
(
LC
.
send
channel
(
task
.
id
,
Completed
(
not
task
.
completed
)))
]
[]
,
label
[
onDoubleClick
(
LC
.
send
channel
(
task
.
id
,
Focus
))
]
[
text
description
]
,
button
[
class
"
destroy"
,
onClick
(
LC
.
send
channel
(
task
.
id
,
Delete
))
]
[]
]
,
input
[
class
"
edit"
,
value
description
,
name
"
title"
,
id
(
"
todo-"
++
toString
task
.
id
)
,
on
"
input"
targetValue
(
\
desc
->
LC
.
send
channel
(
task
.
id
,
Edit
desc
))
,
onBlur
(
LC
.
send
channel
(
task
.
id
,
Commit
))
,
onFinish
(
LC
.
send
channel
(
task
.
id
,
Commit
))
(
LC
.
send
channel
(
task
.
id
,
Cancel
))
]
[]
]
onFinish
:
Signal
.
Message
->
Signal
.
Message
->
Attribute
onFinish
enterMessage
escapeMessage
=
let
select
key
=
case
key
of
13
->
Ok
enterMessage
27
->
Ok
escapeMessage
_
->
Err
"
Not a 'finish' key, such as ENTER or ESCAPE"
in
on
"
keydown"
(
Json
.
customDecoder
keyCode
select
)
identity
examples/elm/Todo.elm
View file @
7cdcf5a8
...
@@ -3,9 +3,9 @@ module Todo where
...
@@ -3,9 +3,9 @@ module Todo where
This application is broken up into four distinct parts:
This application is broken up into four distinct parts:
1. Model - a full de
scription of the application as data
1. Model - a full de
finition of the application's state
2. Update - a way to
update the model based on user actions
2. Update - a way to
step the application state forward
3. View - a way to visualize our
model
with HTML
3. View - a way to visualize our
application state
with HTML
4. Inputs - the signals necessary to manage events
4. Inputs - the signals necessary to manage events
This clean division of concerns is a core part of Elm. You can read more about
This clean division of concerns is a core part of Elm. You can read more about
...
@@ -16,30 +16,43 @@ document for notes on structuring more complex GUIs with Elm:
...
@@ -16,30 +16,43 @@ document for notes on structuring more complex GUIs with Elm:
http://elm-lang.org/learn/Architecture.elm
http://elm-lang.org/learn/Architecture.elm
-}
-}
import
Html
(
..
)
import
Html
exposing
(
..
)
import
Html
.
Attributes
(
..
)
import
Html
.
Attributes
exposing
(
..
)
import
Html
.
Events
(
..
)
import
Html
.
Events
exposing
(
..
)
import
Html
.
Lazy
(
lazy
,
lazy2
)
import
Html
.
Lazy
exposing
(
lazy
,
lazy2
,
lazy3
)
import
List
import
Json
.
Decode
as
Json
import
LocalChannel
as
LC
import
Signal
exposing
(
Signal
,
Address
)
import
Maybe
import
Signal
import
String
import
String
import
Task
import
Window
import
Window
---- MODEL ----
-- MODEL
-- The full application state of our todo app.
-- The full application state of our todo app.
type
alias
Model
=
type
alias
Model
=
{
tasks
:
List
Task
.
Model
{
tasks
:
List
Task
,
field
:
String
,
field
:
String
,
uid
:
Int
,
uid
:
Int
,
visibility
:
String
,
visibility
:
String
}
}
type
alias
Task
=
{
description
:
String
,
completed
:
Bool
,
editing
:
Bool
,
id
:
Int
}
newTask
:
String
->
Int
->
Task
newTask
desc
id
=
{
description
=
desc
,
completed
=
False
,
editing
=
False
,
id
=
id
}
emptyModel
:
Model
emptyModel
:
Model
emptyModel
=
emptyModel
=
{
tasks
=
[]
{
tasks
=
[]
...
@@ -49,7 +62,7 @@ emptyModel =
...
@@ -49,7 +62,7 @@ emptyModel =
}
}
--
UPDATE
--
-- UPDATE ----
-- A description of the kinds of actions that can be performed on the model of
-- A description of the kinds of actions that can be performed on the model of
-- our application. See the following post for more info on this pattern and
-- our application. See the following post for more info on this pattern and
...
@@ -57,84 +70,115 @@ emptyModel =
...
@@ -57,84 +70,115 @@ emptyModel =
type
Action
type
Action
=
NoOp
=
NoOp
|
UpdateField
String
|
UpdateField
String
|
EditingTask
Int
Bool
|
UpdateTask
Int
String
|
Add
|
Add
|
UpdateTask
(
Int
,
Task
.
Action
)
|
Delete
Int
|
DeleteComplete
|
DeleteComplete
|
Check
Int
Bool
|
CheckAll
Bool
|
CheckAll
Bool
|
ChangeVisibility
String
|
ChangeVisibility
String
-- How we update our Model on a
ny given Action
-- How we update our Model on a
given Action?
update
:
Action
->
Model
->
Model
update
:
Action
->
Model
->
Model
update
action
model
=
update
action
model
=
case
action
of
case
action
of
NoOp
->
model
NoOp
->
model
Add
->
{
model
|
uid
<-
model
.
uid
+
1
,
field
<-
"
"
,
tasks
<-
if
String
.
isEmpty
model
.
field
then
model
.
tasks
else
model
.
tasks
++
[
newTask
model
.
field
model
.
uid
]
}
UpdateField
str
->
UpdateField
str
->
{
model
|
field
<-
str
}
{
model
|
field
<-
str
}
Add
->
EditingTask
id
isEditing
->
let
description
=
String
.
trim
model
.
field
in
let
updateTask
t
=
if
t
.
id
==
id
then
{
t
|
editing
<-
isEditing
}
else
t
if
String
.
isEmpty
description
then
model
else
{
model
|
uid
<-
model
.
uid
+
1
,
field
<-
"
"
,
tasks
<-
model
.
tasks
++
[
Task
.
init
description
model
.
uid
]
}
UpdateTask
(
id
,
taskAction
)
->
let
updateTask
t
=
if
t
.
id
==
id
then
Task
.
update
taskAction
t
else
Just
t
in
in
{
model
|
tasks
<-
List
.
filterMap
updateTask
model
.
tasks
}
{
model
|
tasks
<-
List
.
map
updateTask
model
.
tasks
}
UpdateTask
id
task
->
let
updateTask
t
=
if
t
.
id
==
id
then
{
t
|
description
<-
task
}
else
t
in
{
model
|
tasks
<-
List
.
map
updateTask
model
.
tasks
}
Delete
id
->
{
model
|
tasks
<-
List
.
filter
(
\
t
->
t
.
id
/=
id
)
model
.
tasks
}
DeleteComplete
->
DeleteComplete
->
{
model
|
tasks
<-
List
.
filter
(
not
<<
.
completed
)
model
.
tasks
}
{
model
|
tasks
<-
List
.
filter
(
not
<<
.
completed
)
model
.
tasks
}
CheckAll
bool
->
Check
id
isCompleted
->
let
updateTask
t
=
{
t
|
completed
<-
bool
}
let
updateTask
t
=
if
t
.
id
==
id
then
{
t
|
completed
<-
isCompleted
}
else
t
in
{
model
|
tasks
<-
List
.
map
updateTask
model
.
tasks
}
in
{
model
|
tasks
<-
List
.
map
updateTask
model
.
tasks
}
CheckAll
isCompleted
->
let
updateTask
t
=
{
t
|
completed
<-
isCompleted
}
in
{
model
|
tasks
<-
List
.
map
updateTask
model
.
tasks
}
ChangeVisibility
visibility
->
ChangeVisibility
visibility
->
{
model
|
visibility
<-
visibility
}
{
model
|
visibility
<-
visibility
}
--
VIEW
--
-- VIEW ----
view
:
Model
->
Html
view
:
Address
Action
->
Model
->
Html
view
model
=
view
address
model
=
div
div
[
class
"
todomvc-wrapper"
[
class
"
todomvc-wrapper"
,
style
[
(
"
visibility"
,
"
hidden"
)
]
,
style
[
(
"
visibility"
,
"
hidden"
)
]
]
]
[
section
[
section
[
id
"
todoapp"
]
[
class
"
todoapp"
]
[
lazy
taskEntry
model
.
field
[
lazy
2
taskEntry
address
model
.
field
,
lazy
2
taskList
model
.
visibility
model
.
tasks
,
lazy
3
taskList
address
model
.
visibility
model
.
tasks
,
lazy
2
control
s
model
.
visibility
model
.
tasks
,
lazy
3
controls
addres
s
model
.
visibility
model
.
tasks
]
]
,
infoFooter
,
infoFooter
]
]
taskEntry
:
String
->
Html
taskEntry
task
=
onEnter
:
Address
a
->
a
->
Attribute
onEnter
address
value
=
on
"
keydown"
(
Json
.
customDecoder
keyCode
is13
)
(
\
_
->
Signal
.
message
address
value
)
is13
:
Int
->
Result
String
()
is13
code
=
if
code
==
13
then
Ok
()
else
Err
"
not the right key code"
taskEntry
:
Address
Action
->
String
->
Html
taskEntry
address
task
=
header
header
[
id
"
header"
]
[
class
"
header"
]
[
h1
[]
[
text
"
todos"
]
[
h1
[]
[
text
"
todos"
]
,
input
,
input
[
id
"
new-todo"
[
class
"
new-todo"
,
placeholder
"
What needs to be done?"
,
placeholder
"
What needs to be done?"
,
autofocus
True
,
autofocus
True
,
value
task
,
value
task
,
name
"
newTodo"
,
name
"
newTodo"
,
on
"
input"
targetValue
(
Signal
.
send
action
s
<<
UpdateField
)
,
on
"
input"
targetValue
(
Signal
.
message
addres
s
<<
UpdateField
)
,
Task
.
onFinish
(
Signal
.
send
actions
Add
)
(
Signal
.
send
actions
NoOp
)
,
onEnter
address
Add
]
]
[]
[]
]
]
taskList
:
String
->
List
Task
.
Model
->
Html
taskList
visibility
tasks
=
taskList
:
Address
Action
->
String
->
List
Task
->
Html
taskList
address
visibility
tasks
=
let
isVisible
todo
=
let
isVisible
todo
=
case
visibility
of
case
visibility
of
"
Completed"
->
todo
.
completed
"
Completed"
->
todo
.
completed
...
@@ -146,124 +190,157 @@ taskList visibility tasks =
...
@@ -146,124 +190,157 @@ taskList visibility tasks =
cssVisibility
=
if
List
.
isEmpty
tasks
then
"
hidden"
else
"
visible"
cssVisibility
=
if
List
.
isEmpty
tasks
then
"
hidden"
else
"
visible"
in
in
section
section
[
id
"
main"
[
class
"
main"
,
style
[
(
"
visibility"
,
cssVisibility
)
]
,
style
[
(
"
visibility"
,
cssVisibility
)
]
]
]
[
input
[
input
[
id
"
toggle-all"
[
class
"
toggle-all"
,
type
'
"
checkbox"
,
type
'
"
checkbox"
,
name
"
toggle"
,
name
"
toggle"
,
checked
allCompleted
,
checked
allCompleted
,
onClick
(
Signal
.
send
actions
(
CheckAll
(
not
allCompleted
)
))
,
onClick
address
(
CheckAll
(
not
allCompleted
))
]
]
[]
[]
,
label
,
label
[
for
"
toggle-all"
]
[
for
"
toggle-all"
]
[
text
"
Mark all as complete"
]
[
text
"
Mark all as complete"
]
,
ul
,
ul
[
id
"
todo-list"
]
[
class
"
todo-list"
]
(
List
.
map
(
Task
.
view
taskAction
s
)
(
List
.
filter
isVisible
tasks
))
(
List
.
map
(
todoItem
addres
s
)
(
List
.
filter
isVisible
tasks
))
]
]
controls
:
String
->
List
Task
.
Model
->
Html
controls
visibility
tasks
=
todoItem
:
Address
Action
->
Task
->
Html
todoItem
address
todo
=
li
[
classList
[
(
"
completed"
,
todo
.
completed
)
,
(
"
editing"
,
todo
.
editing
)
]
]
[
div
[
class
"
view"
]
[
input
[
class
"
toggle"
,
type
'
"
checkbox"
,
checked
todo
.
completed
,
onClick
address
(
Check
todo
.
id
(
not
todo
.
completed
))
]
[]
,
label
[
onDoubleClick
address
(
EditingTask
todo
.
id
True
)
]
[
text
todo
.
description
]
,
button
[
class
"
destroy"
,
onClick
address
(
Delete
todo
.
id
)
]
[]
]
,
input
[
class
"
edit"
,
value
todo
.
description
,
name
"
title"
,
class
(
"
todo-"
++
toString
todo
.
id
)
,
on
"
input"
targetValue
(
Signal
.
message
address
<<
UpdateTask
todo
.
id
)
,
onBlur
address
(
EditingTask
todo
.
id
False
)
,
onEnter
address
(
EditingTask
todo
.
id
False
)
]
[]
]
controls
:
Address
Action
->
String
->
List
Task
->
Html
controls
address
visibility
tasks
=
let
tasksCompleted
=
List
.
length
(
List
.
filter
.
completed
tasks
)
let
tasksCompleted
=
List
.
length
(
List
.
filter
.
completed
tasks
)
tasksLeft
=
List
.
length
tasks
-
tasksCompleted
tasksLeft
=
List
.
length
tasks
-
tasksCompleted
item_
=
if
tasksLeft
==
1
then
"
item"
else
"
items"
item_
=
if
tasksLeft
==
1
then
"
item"
else
"
items"
in
in
footer
footer
[
id
"
footer"
[
class
"
footer"
,
hidden
(
List
.
isEmpty
tasks
)
,
hidden
(
List
.
isEmpty
tasks
)
]
]
[
span
[
span
[
id
"
todo-count"
]
[
class
"
todo-count"
]
[
strong
[]
[
text
(
toString
tasksLeft
)
]
[
strong
[]
[
text
(
toString
tasksLeft
)
]
,
text
(
item_
++
"
left"
)
,
text
(
item_
++
"
left"
)
]
]
,
ul
,
ul
[
id
"
filters"
]
[
class
"
filters"
]
[
visibilitySwap
"
#/"
"
All"
visibility
[
visibilitySwap
address
"
#/"
"
All"
visibility
,
text
"
"
,
text
"
"
,
visibilitySwap
"
#/active"
"
Active"
visibility
,
visibilitySwap
address
"
#/active"
"
Active"
visibility
,
text
"
"
,
text
"
"
,
visibilitySwap
"
#/completed"
"
Completed"
visibility
,
visibilitySwap
address
"
#/completed"
"
Completed"
visibility
]
]
,
button
,
button
[
class
"
clear-completed"
[
class
"
clear-completed"
,
id
"
clear-completed"
,
id
"
clear-completed"
,
hidden
(
tasksCompleted
==
0
)
,
hidden
(
tasksCompleted
==
0
)
,
onClick
(
Signal
.
send
actions
DeleteComplete
)
,
onClick
address
DeleteComplete
]
]
[
text
(
"
Clear completed ("
++
toString
tasksCompleted
++
"
)"
)
]
[
text
(
"
Clear completed ("
++
toString
tasksCompleted
++
"
)"
)
]
]
]
visibilitySwap
:
String
->
String
->
String
->
Html
visibilitySwap
uri
visibility
actualVisibility
=
visibilitySwap
:
Address
Action
->
String
->
String
->
String
->
Html
let
className
=
if
visibility
==
actualVisibility
then
"
selected"
else
"
"
in
visibilitySwap
address
uri
visibility
actualVisibility
=
li
li
[
onClick
(
Signal
.
send
actions
(
ChangeVisibility
visibility
))
]
[
onClick
address
(
ChangeVisibility
visibility
)
]
[
a
[
class
className
,
href
uri
]
[
text
visibility
]
]
[
a
[
href
uri
,
classList
[(
"
selected"
,
visibility
==
actualVisibility
)]
]
[
text
visibility
]
]
infoFooter
:
Html
infoFooter
:
Html
infoFooter
=
infoFooter
=
footer
[
id
"
info"
]
footer
[
class
"
info"
]
[
p
[]
[
text
"
Double-click to edit a todo"
]
[
p
[]
[
text
"
Double-click to edit a todo"
]
,
p
[]
[
text
"
Written by "
,
p
[]
,
a
[
href
"
https://github.com/evancz"
]
[
text
"
Evan Czaplicki"
]
[
text
"
Written by "
]
,
a
[
href
"
https://github.com/evancz"
]
[
text
"
Evan Czaplicki"
]
,
p
[]
[
text
"
Part of "
]
,
a
[
href
"
http://todomvc.com"
]
[
text
"
TodoMVC"
]
,
p
[]
]
[
text
"
Part of "
,
a
[
href
"
http://todomvc.com"
]
[
text
"
TodoMVC"
]
]
]
]
--
SIGNALS
--
-- INPUTS ----
-- wire the entire application together
-- wire the entire application together
main
:
Signal
Html
main
:
Signal
Html
main
=
main
=
Signal
.
map
view
model
Signal
.
map
(
view
actions
.
address
)
model
-- manage the model of our application over time
-- manage the model of our application over time
model
:
Signal
Model
model
:
Signal
Model
model
=
model
=
Signal
.
foldp
update
initialModel
allActions
Signal
.
foldp
update
initialModel
actions
.
signal
initialModel
:
Model
initialModel
:
Model
initialModel
=
initialModel
=
Maybe
.
withDefault
emptyModel
savedModel
Maybe
.
withDefault
emptyModel
getStorage
allActions
:
Signal
Action
-- actions from user input
allActions
=
actions
:
Signal
.
Mailbox
Action
Signal
.
merge
actions
=
(
Signal
.
subscribe
actions
)
Signal
.
mailbox
NoOp
(
Signal
.
map
ChangeVisibility
route
)
-- interactions with localStorage
port
savedModel
:
Maybe
Model
port
save
:
Signal
Model
port
focus
:
Signal
String
port
save
=
model
port
focus
=
let
needsFocus
act
=
case
act
of
EditingTask
id
bool
->
bool
_
->
False
-- routing
toSelector
(
EditingTask
id
_
)
=
(
"
#todo-"
++
toString
id
)
port
route
:
Signal
String
in
actions
.
signal
|>
Signal
.
filter
needsFocus
(
EditingTask
0
True
)
|>
Signal
.
map
toSelector
-- actions from user input
actions
:
Signal
.
Channel
Action
-- interactions with localStorage to save the model
taskActions
:
LC
.
LocalChannel
(
Int
,
Task
.
A
ction
)
port
getStorage
:
Maybe
Model
port
setStorage
:
Signal
Model
port
focus
:
Signal
(
Maybe
Int
)
port
setStorage
=
model
port
focus
=
let
toSelector
action
=
case
action
of
UpdateTask
(
id
,
Task
.
Focus
)
->
Just
id
_
->
Nothing
in
examples/elm/elm-package.json
View file @
7cdcf5a8
...
@@ -8,8 +8,8 @@
...
@@ -8,8 +8,8 @@
],
],
"exposed-modules"
:
[],
"exposed-modules"
:
[],
"dependencies"
:
{
"dependencies"
:
{
"elm-lang/core"
:
"1.0.0 <= v < 2.0.0"
,
"elm-lang/core"
:
"2.0.0 <= v < 3.0.0"
,
"evancz/elm-html"
:
"1.0.0 <= v < 2.0.0"
,
"evancz/elm-html"
:
"3.0.0 <= v < 4.0.0"
"evancz/local-channel"
:
"1.0.0 <= v < 2.0.0"
},
}
"elm-version"
:
"0.15.0 <= v < 0.16.0"
}
}
\ No newline at end of file
examples/elm/elm.js
View file @
7cdcf5a8
This source diff could not be displayed because it is too large. You can
view the blob
instead.
examples/elm/index.html
View file @
7cdcf5a8
...
@@ -19,44 +19,22 @@
...
@@ -19,44 +19,22 @@
<script
src=
"elm.js"
></script>
<script
src=
"elm.js"
></script>
<script>
<script>
(
function
()
{
(
function
()
{
var
result
=
localStorage
.
getItem
(
'
elm-todo-model
'
);
var
storedState
=
localStorage
.
getItem
(
'
elm-todo-state
'
);
var
s
avedModel
=
result
?
JSON
.
parse
(
result
)
:
null
;
var
s
tartingState
=
storedState
?
JSON
.
parse
(
storedState
)
:
null
;
var
todomvc
=
Elm
.
fullscreen
(
Elm
.
Todo
,
{
var
todomvc
=
Elm
.
fullscreen
(
Elm
.
Todo
,
{
getStorage
:
startingState
});
savedModel
:
savedModel
,
todomvc
.
ports
.
focus
.
subscribe
(
function
(
selector
)
{
route
:
getRoute
()
});
todomvc
.
ports
.
save
.
subscribe
(
function
(
model
)
{
localStorage
.
setItem
(
'
elm-todo-model
'
,
JSON
.
stringify
(
model
));
});
// Routing
window
.
addEventListener
(
'
popstate
'
,
function
()
{
todomvc
.
ports
.
route
.
send
(
getRoute
());
},
false
);
function
getRoute
()
{
var
hash
=
location
.
href
.
split
(
'
#
'
)[
1
]
||
''
;
var
route
=
hash
.
replace
(
'
/
'
,
''
);
if
([
'
all
'
,
'
active
'
,
'
completed
'
].
indexOf
(
route
)
>=
0
)
{
return
route
[
0
].
toUpperCase
()
+
route
.
substr
(
1
);
}
return
'
All
'
;
}
// Setting focus manually
todomvc
.
ports
.
focus
.
subscribe
(
function
(
id
)
{
setTimeout
(
function
()
{
setTimeout
(
function
()
{
if
(
id
===
null
)
{
var
nodes
=
document
.
querySelectorAll
(
selector
);
return
;
if
(
nodes
.
length
===
1
&&
document
.
activeElement
!==
nodes
[
0
])
{
}
nodes
[
0
].
focus
()
var
node
=
document
.
getElementById
(
'
todo-
'
+
id
);
if
(
document
.
activeElement
!==
node
)
{
node
.
focus
();
}
}
},
50
);
},
50
);
});
});
todomvc
.
ports
.
setStorage
.
subscribe
(
function
(
state
)
{
localStorage
.
setItem
(
'
elm-todo-state
'
,
JSON
.
stringify
(
state
));
});
}());
}());
</script>
</script>
<script
async
src=
"node_modules/todomvc-common/base.js"
></script>
<script
async
src=
"node_modules/todomvc-common/base.js"
></script>
...
...
examples/elm/node_modules/todomvc-app-css/index.css
View file @
7cdcf5a8
...
@@ -15,11 +15,9 @@ button {
...
@@ -15,11 +15,9 @@ button {
font-weight
:
inherit
;
font-weight
:
inherit
;
color
:
inherit
;
color
:
inherit
;
-webkit-appearance
:
none
;
-webkit-appearance
:
none
;
-ms-appearance
:
none
;
appearance
:
none
;
appearance
:
none
;
-webkit-font-smoothing
:
antialiased
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
}
}
...
@@ -33,7 +31,6 @@ body {
...
@@ -33,7 +31,6 @@ body {
margin
:
0
auto
;
margin
:
0
auto
;
-webkit-font-smoothing
:
antialiased
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
font-weight
:
300
;
font-weight
:
300
;
}
}
...
@@ -47,7 +44,7 @@ input[type="checkbox"] {
...
@@ -47,7 +44,7 @@ input[type="checkbox"] {
display
:
none
;
display
:
none
;
}
}
#
todoapp
{
.
todoapp
{
background
:
#fff
;
background
:
#fff
;
margin
:
130px
0
40px
0
;
margin
:
130px
0
40px
0
;
position
:
relative
;
position
:
relative
;
...
@@ -55,25 +52,25 @@ input[type="checkbox"] {
...
@@ -55,25 +52,25 @@ input[type="checkbox"] {
0
25px
50px
0
rgba
(
0
,
0
,
0
,
0.1
);
0
25px
50px
0
rgba
(
0
,
0
,
0
,
0.1
);
}
}
#
todoapp
input
::-webkit-input-placeholder
{
.
todoapp
input
::-webkit-input-placeholder
{
font-style
:
italic
;
font-style
:
italic
;
font-weight
:
300
;
font-weight
:
300
;
color
:
#e6e6e6
;
color
:
#e6e6e6
;
}
}
#
todoapp
input
::-moz-placeholder
{
.
todoapp
input
::-moz-placeholder
{
font-style
:
italic
;
font-style
:
italic
;
font-weight
:
300
;
font-weight
:
300
;
color
:
#e6e6e6
;
color
:
#e6e6e6
;
}
}
#
todoapp
input
::input-placeholder
{
.
todoapp
input
::input-placeholder
{
font-style
:
italic
;
font-style
:
italic
;
font-weight
:
300
;
font-weight
:
300
;
color
:
#e6e6e6
;
color
:
#e6e6e6
;
}
}
#
todoapp
h1
{
.
todoapp
h1
{
position
:
absolute
;
position
:
absolute
;
top
:
-155px
;
top
:
-155px
;
width
:
100%
;
width
:
100%
;
...
@@ -83,11 +80,10 @@ input[type="checkbox"] {
...
@@ -83,11 +80,10 @@ input[type="checkbox"] {
color
:
rgba
(
175
,
47
,
47
,
0.15
);
color
:
rgba
(
175
,
47
,
47
,
0.15
);
-webkit-text-rendering
:
optimizeLegibility
;
-webkit-text-rendering
:
optimizeLegibility
;
-moz-text-rendering
:
optimizeLegibility
;
-moz-text-rendering
:
optimizeLegibility
;
-ms-text-rendering
:
optimizeLegibility
;
text-rendering
:
optimizeLegibility
;
text-rendering
:
optimizeLegibility
;
}
}
#
new-todo
,
.
new-todo
,
.edit
{
.edit
{
position
:
relative
;
position
:
relative
;
margin
:
0
;
margin
:
0
;
...
@@ -102,22 +98,20 @@ input[type="checkbox"] {
...
@@ -102,22 +98,20 @@ input[type="checkbox"] {
padding
:
6px
;
padding
:
6px
;
border
:
1px
solid
#999
;
border
:
1px
solid
#999
;
box-shadow
:
inset
0
-1px
5px
0
rgba
(
0
,
0
,
0
,
0.2
);
box-shadow
:
inset
0
-1px
5px
0
rgba
(
0
,
0
,
0
,
0.2
);
-ms-box-sizing
:
border-box
;
box-sizing
:
border-box
;
box-sizing
:
border-box
;
-webkit-font-smoothing
:
antialiased
;
-webkit-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-moz-font-smoothing
:
antialiased
;
-ms-font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
font-smoothing
:
antialiased
;
}
}
#
new-todo
{
.
new-todo
{
padding
:
16px
16px
16px
60px
;
padding
:
16px
16px
16px
60px
;
border
:
none
;
border
:
none
;
background
:
rgba
(
0
,
0
,
0
,
0.003
);
background
:
rgba
(
0
,
0
,
0
,
0.003
);
box-shadow
:
inset
0
-2px
1px
rgba
(
0
,
0
,
0
,
0.03
);
box-shadow
:
inset
0
-2px
1px
rgba
(
0
,
0
,
0
,
0.03
);
}
}
#
main
{
.
main
{
position
:
relative
;
position
:
relative
;
z-index
:
2
;
z-index
:
2
;
border-top
:
1px
solid
#e6e6e6
;
border-top
:
1px
solid
#e6e6e6
;
...
@@ -127,7 +121,7 @@ label[for='toggle-all'] {
...
@@ -127,7 +121,7 @@ label[for='toggle-all'] {
display
:
none
;
display
:
none
;
}
}
#
toggle-all
{
.
toggle-all
{
position
:
absolute
;
position
:
absolute
;
top
:
-55px
;
top
:
-55px
;
left
:
-12px
;
left
:
-12px
;
...
@@ -137,50 +131,50 @@ label[for='toggle-all'] {
...
@@ -137,50 +131,50 @@ label[for='toggle-all'] {
border
:
none
;
/* Mobile Safari */
border
:
none
;
/* Mobile Safari */
}
}
#
toggle-all
:before
{
.
toggle-all
:before
{
content
:
'❯'
;
content
:
'❯'
;
font-size
:
22px
;
font-size
:
22px
;
color
:
#e6e6e6
;
color
:
#e6e6e6
;
padding
:
10px
27px
10px
27px
;
padding
:
10px
27px
10px
27px
;
}
}
#
toggle-all
:checked:before
{
.
toggle-all
:checked:before
{
color
:
#737373
;
color
:
#737373
;
}
}
#
todo-list
{
.
todo-list
{
margin
:
0
;
margin
:
0
;
padding
:
0
;
padding
:
0
;
list-style
:
none
;
list-style
:
none
;
}
}
#
todo-list
li
{
.
todo-list
li
{
position
:
relative
;
position
:
relative
;
font-size
:
24px
;
font-size
:
24px
;
border-bottom
:
1px
solid
#ededed
;
border-bottom
:
1px
solid
#ededed
;
}
}
#
todo-list
li
:last-child
{
.
todo-list
li
:last-child
{
border-bottom
:
none
;
border-bottom
:
none
;
}
}
#
todo-list
li
.editing
{
.
todo-list
li
.editing
{
border-bottom
:
none
;
border-bottom
:
none
;
padding
:
0
;
padding
:
0
;
}
}
#
todo-list
li
.editing
.edit
{
.
todo-list
li
.editing
.edit
{
display
:
block
;
display
:
block
;
width
:
506px
;
width
:
506px
;
padding
:
13px
17px
12px
17px
;
padding
:
13px
17px
12px
17px
;
margin
:
0
0
0
43px
;
margin
:
0
0
0
43px
;
}
}
#
todo-list
li
.editing
.view
{
.
todo-list
li
.editing
.view
{
display
:
none
;
display
:
none
;
}
}
#
todo-list
li
.toggle
{
.
todo-list
li
.toggle
{
text-align
:
center
;
text-align
:
center
;
width
:
40px
;
width
:
40px
;
/* auto, since non-WebKit browsers doesn't support input styling */
/* auto, since non-WebKit browsers doesn't support input styling */
...
@@ -191,19 +185,18 @@ label[for='toggle-all'] {
...
@@ -191,19 +185,18 @@ label[for='toggle-all'] {
margin
:
auto
0
;
margin
:
auto
0
;
border
:
none
;
/* Mobile Safari */
border
:
none
;
/* Mobile Safari */
-webkit-appearance
:
none
;
-webkit-appearance
:
none
;
-ms-appearance
:
none
;
appearance
:
none
;
appearance
:
none
;
}
}
#
todo-list
li
.toggle
:after
{
.
todo-list
li
.toggle
:after
{
content
:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>')
;
content
:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>')
;
}
}
#
todo-list
li
.toggle
:checked:after
{
.
todo-list
li
.toggle
:checked:after
{
content
:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>')
;
content
:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>')
;
}
}
#
todo-list
li
label
{
.
todo-list
li
label
{
white-space
:
pre
;
white-space
:
pre
;
word-break
:
break-word
;
word-break
:
break-word
;
padding
:
15px
60px
15px
15px
;
padding
:
15px
60px
15px
15px
;
...
@@ -213,12 +206,12 @@ label[for='toggle-all'] {
...
@@ -213,12 +206,12 @@ label[for='toggle-all'] {
transition
:
color
0.4s
;
transition
:
color
0.4s
;
}
}
#
todo-list
li
.completed
label
{
.
todo-list
li
.completed
label
{
color
:
#d9d9d9
;
color
:
#d9d9d9
;
text-decoration
:
line-through
;
text-decoration
:
line-through
;
}
}
#
todo-list
li
.destroy
{
.
todo-list
li
.destroy
{
display
:
none
;
display
:
none
;
position
:
absolute
;
position
:
absolute
;
top
:
0
;
top
:
0
;
...
@@ -233,27 +226,27 @@ label[for='toggle-all'] {
...
@@ -233,27 +226,27 @@ label[for='toggle-all'] {
transition
:
color
0.2s
ease-out
;
transition
:
color
0.2s
ease-out
;
}
}
#
todo-list
li
.destroy
:hover
{
.
todo-list
li
.destroy
:hover
{
color
:
#af5b5e
;
color
:
#af5b5e
;
}
}
#
todo-list
li
.destroy
:after
{
.
todo-list
li
.destroy
:after
{
content
:
'×'
;
content
:
'×'
;
}
}
#
todo-list
li
:hover
.destroy
{
.
todo-list
li
:hover
.destroy
{
display
:
block
;
display
:
block
;
}
}
#
todo-list
li
.edit
{
.
todo-list
li
.edit
{
display
:
none
;
display
:
none
;
}
}
#
todo-list
li
.editing
:last-child
{
.
todo-list
li
.editing
:last-child
{
margin-bottom
:
-1px
;
margin-bottom
:
-1px
;
}
}
#
footer
{
.
footer
{
color
:
#777
;
color
:
#777
;
padding
:
10px
15px
;
padding
:
10px
15px
;
height
:
20px
;
height
:
20px
;
...
@@ -261,7 +254,7 @@ label[for='toggle-all'] {
...
@@ -261,7 +254,7 @@ label[for='toggle-all'] {
border-top
:
1px
solid
#e6e6e6
;
border-top
:
1px
solid
#e6e6e6
;
}
}
#
footer
:before
{
.
footer
:before
{
content
:
''
;
content
:
''
;
position
:
absolute
;
position
:
absolute
;
right
:
0
;
right
:
0
;
...
@@ -276,16 +269,16 @@ label[for='toggle-all'] {
...
@@ -276,16 +269,16 @@ label[for='toggle-all'] {
0
17px
2px
-6px
rgba
(
0
,
0
,
0
,
0.2
);
0
17px
2px
-6px
rgba
(
0
,
0
,
0
,
0.2
);
}
}
#
todo-count
{
.
todo-count
{
float
:
left
;
float
:
left
;
text-align
:
left
;
text-align
:
left
;
}
}
#
todo-count
strong
{
.
todo-count
strong
{
font-weight
:
300
;
font-weight
:
300
;
}
}
#
filters
{
.
filters
{
margin
:
0
;
margin
:
0
;
padding
:
0
;
padding
:
0
;
list-style
:
none
;
list-style
:
none
;
...
@@ -294,11 +287,11 @@ label[for='toggle-all'] {
...
@@ -294,11 +287,11 @@ label[for='toggle-all'] {
left
:
0
;
left
:
0
;
}
}
#
filters
li
{
.
filters
li
{
display
:
inline
;
display
:
inline
;
}
}
#
filters
li
a
{
.
filters
li
a
{
color
:
inherit
;
color
:
inherit
;
margin
:
3px
;
margin
:
3px
;
padding
:
3px
7px
;
padding
:
3px
7px
;
...
@@ -307,39 +300,30 @@ label[for='toggle-all'] {
...
@@ -307,39 +300,30 @@ label[for='toggle-all'] {
border-radius
:
3px
;
border-radius
:
3px
;
}
}
#
filters
li
a
.selected
,
.
filters
li
a
.selected
,
#
filters
li
a
:hover
{
.
filters
li
a
:hover
{
border-color
:
rgba
(
175
,
47
,
47
,
0.1
);
border-color
:
rgba
(
175
,
47
,
47
,
0.1
);
}
}
#
filters
li
a
.selected
{
.
filters
li
a
.selected
{
border-color
:
rgba
(
175
,
47
,
47
,
0.2
);
border-color
:
rgba
(
175
,
47
,
47
,
0.2
);
}
}
#
clear-completed
,
.
clear-completed
,
html
#
clear-completed
:active
{
html
.
clear-completed
:active
{
float
:
right
;
float
:
right
;
position
:
relative
;
position
:
relative
;
line-height
:
20px
;
line-height
:
20px
;
text-decoration
:
none
;
text-decoration
:
none
;
cursor
:
pointer
;
cursor
:
pointer
;
visibility
:
hidden
;
position
:
relative
;
position
:
relative
;
}
}
#clear-completed
::after
{
.clear-completed
:hover
{
visibility
:
visible
;
content
:
'Clear completed'
;
position
:
absolute
;
right
:
0
;
white-space
:
nowrap
;
}
#clear-completed
:hover::after
{
text-decoration
:
underline
;
text-decoration
:
underline
;
}
}
#
info
{
.
info
{
margin
:
65px
auto
0
;
margin
:
65px
auto
0
;
color
:
#bfbfbf
;
color
:
#bfbfbf
;
font-size
:
10px
;
font-size
:
10px
;
...
@@ -347,17 +331,17 @@ html #clear-completed:active {
...
@@ -347,17 +331,17 @@ html #clear-completed:active {
text-align
:
center
;
text-align
:
center
;
}
}
#
info
p
{
.
info
p
{
line-height
:
1
;
line-height
:
1
;
}
}
#
info
a
{
.
info
a
{
color
:
inherit
;
color
:
inherit
;
text-decoration
:
none
;
text-decoration
:
none
;
font-weight
:
400
;
font-weight
:
400
;
}
}
#
info
a
:hover
{
.
info
a
:hover
{
text-decoration
:
underline
;
text-decoration
:
underline
;
}
}
...
@@ -366,16 +350,16 @@ html #clear-completed:active {
...
@@ -366,16 +350,16 @@ html #clear-completed:active {
Can't use it globally since it destroys checkboxes in Firefox
Can't use it globally since it destroys checkboxes in Firefox
*/
*/
@media
screen
and
(
-webkit-min-device-pixel-ratio
:
0
)
{
@media
screen
and
(
-webkit-min-device-pixel-ratio
:
0
)
{
#
toggle-all
,
.
toggle-all
,
#
todo-list
li
.toggle
{
.
todo-list
li
.toggle
{
background
:
none
;
background
:
none
;
}
}
#
todo-list
li
.toggle
{
.
todo-list
li
.toggle
{
height
:
40px
;
height
:
40px
;
}
}
#
toggle-all
{
.
toggle-all
{
-webkit-transform
:
rotate
(
90deg
);
-webkit-transform
:
rotate
(
90deg
);
transform
:
rotate
(
90deg
);
transform
:
rotate
(
90deg
);
-webkit-appearance
:
none
;
-webkit-appearance
:
none
;
...
@@ -384,11 +368,11 @@ html #clear-completed:active {
...
@@ -384,11 +368,11 @@ html #clear-completed:active {
}
}
@media
(
max-width
:
430px
)
{
@media
(
max-width
:
430px
)
{
#
footer
{
.
footer
{
height
:
50px
;
height
:
50px
;
}
}
#
filters
{
.
filters
{
bottom
:
10px
;
bottom
:
10px
;
}
}
}
}
examples/elm/node_modules/todomvc-common/base.js
View file @
7cdcf5a8
...
@@ -114,7 +114,12 @@
...
@@ -114,7 +114,12 @@
})({});
})({});
if
(
location
.
hostname
===
'
todomvc.com
'
)
{
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
(
i
,
s
,
o
,
g
,
r
,
a
,
m
){
i
[
'
GoogleAnalyticsObject
'
]
=
r
;
i
[
r
]
=
i
[
r
]
||
function
(){
(
i
[
r
].
q
=
i
[
r
].
q
||
[]).
push
(
arguments
)},
i
[
r
].
l
=
1
*
new
Date
();
a
=
s
.
createElement
(
o
),
m
=
s
.
getElementsByTagName
(
o
)[
0
];
a
.
async
=
1
;
a
.
src
=
g
;
m
.
parentNode
.
insertBefore
(
a
,
m
)
})(
window
,
document
,
'
script
'
,
'
https://www.google-analytics.com/analytics.js
'
,
'
ga
'
);
ga
(
'
create
'
,
'
UA-31081062-1
'
,
'
auto
'
);
ga
(
'
send
'
,
'
pageview
'
);
}
}
/* jshint ignore:end */
/* jshint ignore:end */
...
@@ -228,7 +233,7 @@
...
@@ -228,7 +233,7 @@
xhr
.
onload
=
function
(
e
)
{
xhr
.
onload
=
function
(
e
)
{
var
parsedResponse
=
JSON
.
parse
(
e
.
target
.
responseText
);
var
parsedResponse
=
JSON
.
parse
(
e
.
target
.
responseText
);
if
(
parsedResponse
instanceof
Array
)
{
if
(
parsedResponse
instanceof
Array
)
{
var
count
=
parsedResponse
.
length
var
count
=
parsedResponse
.
length
;
if
(
count
!==
0
)
{
if
(
count
!==
0
)
{
issueLink
.
innerHTML
=
'
This app has
'
+
count
+
'
open issues
'
;
issueLink
.
innerHTML
=
'
This app has
'
+
count
+
'
open issues
'
;
document
.
getElementById
(
'
issue-count
'
).
style
.
display
=
'
inline
'
;
document
.
getElementById
(
'
issue-count
'
).
style
.
display
=
'
inline
'
;
...
...
examples/elm/package.json
View file @
7cdcf5a8
{
{
"private"
:
true
,
"private"
:
true
,
"dependencies"
:
{
"dependencies"
:
{
"todomvc-app-css"
:
"^
1.0.0
"
,
"todomvc-app-css"
:
"^
2.0.1
"
,
"todomvc-common"
:
"^1.0.1"
"todomvc-common"
:
"^1.0.1"
}
}
}
}
examples/elm/readme.md
View file @
7cdcf5a8
...
@@ -46,7 +46,7 @@ Run the following commands from the root of this project:
...
@@ -46,7 +46,7 @@ Run the following commands from the root of this project:
```
bash
```
bash
elm-package
install
elm-package
install
elm-make Todo.elm
--output
build/Todo.js
elm-make Todo.elm
```
```
Then open
`index.html`
in your browser!
Then open
`index.html`
in your browser!
...
...
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