Commit 87d177de authored by Sam Saccone's avatar Sam Saccone

Merge pull request #1427 from mckamey/master

DUEL: App Update (#1110), cancel editing on escape keypress (#789), and routing support (#812)
parents c70a02b6 18bca51b
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
"excludeFiles": [ "excludeFiles": [
"**/node_modules/**", "**/node_modules/**",
"**/bower_components/**", "**/bower_components/**",
"examples/vanilladart/**/*.js" "examples/vanilladart/**/*.js",
"examples/duel/www/**",
"examples/duel/src/main/webapp/js/lib/**"
], ],
"requireSpaceBeforeBlockStatements": true, "requireSpaceBeforeBlockStatements": true,
"requireParenthesesAroundIIFE": true, "requireParenthesesAroundIIFE": true,
......
target/ target/
node_modules/
www/cdn/debug/
{
"name": "todomvc-duel",
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.3.0"
}
}
{
"private": true,
"scripts": {
"postinstall": "cp node_modules/todomvc-app-css/*.css src/main/webapp/css/ && cp node_modules/todomvc-common/*.css src/main/webapp/css/ && cp node_modules/todomvc-common/base.js src/main/webapp/js/lib/todos.js"
},
"dependencies": {
"todomvc-app-css": "^2.0.0",
"todomvc-common": "^1.0.1"
}
}
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
xmlns="http://maven.apache.org/POM/4.0.0" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.example.todos</groupId> <groupId>com.todomvc.duel</groupId>
<artifactId>todomvc</artifactId> <artifactId>todomvc</artifactId>
<version>0.1.0</version> <version>0.2.0</version>
<packaging>war</packaging> <packaging>war</packaging>
<name>TodoMVC</name> <name>TodoMVC</name>
...@@ -17,25 +18,32 @@ ...@@ -17,25 +18,32 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<resourcesDir>${project.basedir}/src/main/resources</resourcesDir> <resourcesDir>${project.basedir}/src/main/resources</resourcesDir>
<staticapps.version>0.8.5</staticapps.version> <staticapps.version>0.9.11</staticapps.version>
<merge.version>0.5.2</merge.version> <merge.version>0.6.0</merge.version>
<duel.version>0.8.2</duel.version> <duel.version>0.9.7</duel.version>
<slf4j.version>1.6.4</slf4j.version> <slf4j.version>1.7.12</slf4j.version>
<javac.version>1.6</javac.version> <javac.version>1.7</javac.version>
<duel.clientPrefix>todos.views</duel.clientPrefix> <duel.clientPrefix>todos.views</duel.clientPrefix>
<duel.serverPrefix>com.example.todos.views</duel.serverPrefix> <duel.serverPrefix>com.todomvc.duel.views</duel.serverPrefix>
<duel.sourceDir>${resourcesDir}/views/</duel.sourceDir> <duel.sourceDir>${resourcesDir}/views/</duel.sourceDir>
<duel.clientPath>/js/</duel.clientPath> <duel.clientPath>/js/</duel.clientPath>
<merge.cdnMapFile>/cdn.properties</merge.cdnMapFile> <merge.cdnMapFile>/cdn.properties</merge.cdnMapFile>
<merge.cdnRoot>/cdn/</merge.cdnRoot> <merge.cdnRoot>/cdn/</merge.cdnRoot>
<merge.cdnFiles>.ico .png .jpg .gif .eot .woff .ttf .svg .svgz</merge.cdnFiles> <merge.cdnFiles>.ico .png .jpg .gif .cur .eot .woff .ttf .svg .svgz</merge.cdnFiles>
<staticapps.config>${project.basedir}/staticapp.json</staticapps.config> <staticapps.config>${project.basedir}/staticapp.json</staticapps.config>
</properties> </properties>
<dependencies> <dependencies>
<!-- SLF4J runtime -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- DUEL runtime --> <!-- DUEL runtime -->
<dependency> <dependency>
<groupId>org.duelengine</groupId> <groupId>org.duelengine</groupId>
...@@ -49,13 +57,6 @@ ...@@ -49,13 +57,6 @@
<artifactId>duel-staticapps</artifactId> <artifactId>duel-staticapps</artifactId>
<version>${staticapps.version}</version> <version>${staticapps.version}</version>
</dependency> </dependency>
<!-- SLF4J runtime -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
...@@ -75,10 +76,11 @@ ...@@ -75,10 +76,11 @@
<plugin> <plugin>
<groupId>org.apache.tomcat.maven</groupId> <groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId> <artifactId>tomcat7-maven-plugin</artifactId>
<version>2.0-beta-1</version> <version>2.2</version>
<configuration> <configuration>
<path>/</path> <path>/</path>
<port>8080</port> <port>8080</port>
<uriEncoding>UTF-8</uriEncoding>
<warSourceDirectory>${project.build.directory}/${project.build.finalName}/</warSourceDirectory> <warSourceDirectory>${project.build.directory}/${project.build.finalName}/</warSourceDirectory>
</configuration> </configuration>
</plugin> </plugin>
......
# DUEL TodoMVC Example # DUEL • [TodoMVC](http://todomvc.com)
> DUEL is a dual-side templating engine using HTML for layout and 100% pure JavaScript as the binding language. The same views may be executed both directly in the browser (client-side template) and on the server (server-side template). > DUEL is a dual-side templating engine using HTML for layout and 100% pure JavaScript as the binding language. The same views may be executed both directly in the browser (client-side template) and on the server (server-side template).
> _[DUEL - bitbucket.org/mckamey/duel/wiki/Home](http://bitbucket.org/mckamey/duel/wiki/Home)_
## Resources
## Learning DUEL - [Website](http://duelengine.org)
- [Documentation](http://bitbucket.org/mckamey/duel/wiki/Home)
- [Syntax](https://bitbucket.org/mckamey/duel/wiki/Syntax)
- [Examples](https://bitbucket.org/mckamey/duel/wiki/Examples)
- [Source](https://bitbucket.org/mckamey/duel/src)
The [DUEL website](http://bitbucket.org/mckamey/duel/wiki/Home) is a great resource for getting started. *Let us [know](https://github.com/tastejs/todomvc/issues) if you discover anything worth sharing.*
Here are some links you may find helpful:
* [Syntax](https://bitbucket.org/mckamey/duel/wiki/Syntax)
* [Examples](https://bitbucket.org/mckamey/duel/wiki/Examples)
* [DUEL on BitBucket](https://bitbucket.org/mckamey/duel/src)
_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._
## Implementation ## Implementation
...@@ -44,4 +40,8 @@ To run a debug-able version using Tomcat 7 as the web server, use this Maven com ...@@ -44,4 +40,8 @@ To run a debug-able version using Tomcat 7 as the web server, use this Maven com
mvn tomcat7:run mvn tomcat7:run
Then navigate your browser to http://localhost:8080/ Then navigate your browser to <http://localhost:8080/>
## Credit
Created by [Stephen McKamey](http://mck.me)
<view name="HomePage"><!doctype html> <view name="HomePage"><!doctype html>
<html lang="en" data-framework="duel"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>DUEL &bull; TodoMVC</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>DUEL • TodoMVC</title>
<link rel="stylesheet" href="/css/styles.merge"> <link rel="stylesheet" href="/css/styles.merge">
</head> </head>
<body> <body>
<footer id="info"> <footer class="info">
<p>Double-click to edit a todo</p> <p>Double-click to edit a todo</p>
<p>Ported to <a href="http://duelengine.org">DUEL</a> by <a href="http://mck.me">Stephen McKamey</a></p> <p>Ported to <a href="http://duelengine.org">DUEL</a> by <a href="http://mck.me">Stephen McKamey</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
......
<view name="Stats"> <view name="Stats">
<%-- this footer should hidden by default and shown when there are todos --%> <%-- This footer should hidden by default and shown when there are todos. --%>
<footer id="footer" if="<%= data.total %>"> <footer class="footer" if="<%= data.total %>">
<span id="todo-count"><strong><%= data.active %></strong> <%= (data.active === 1) ? 'item' : 'items' %> left</span> <span class="todo-count"><strong><%= data.active %></strong> <%= (data.active === 1) ? 'item' : 'items' %> left</span>
<%-- TODO: implement routing <ul class="filters">
<ul id="filters">
<li> <li>
<a class="selected" href="#/">All</a> <a href="#/" class="<%= !data.filter ? 'selected' : null %>">All</a>
</li> </li>
<li> <li>
<a href="#/active">Active</a> <a href="#/active" class="<%= data.filter === 'active' ? 'selected' : null %>">Active</a>
</li> </li>
<li> <li>
<a href="#/completed">Completed</a> <a href="#/completed" class="<%= data.filter === 'completed' ? 'selected' : null %>">Completed</a>
</li> </li>
</ul> </ul>
--%>
<button id="clear-completed" <%-- Hidden if no completed items are left ↓ --%>
<button class="clear-completed"
if="<%= data.completed %>" if="<%= data.completed %>"
onclick="<%= todos.actions.clear_click %>">Clear completed</button> onclick="<%= todos.actions.clearOnClick %>">Clear completed</button>
</footer> </footer>
<view name="Task"> <view name="Task">
<%-- could have embedded in 'tasks' for-loop, but this allows us to add single tasks --%> <%-- List items should get the class `editing` when editing and `completed` when marked as completed. --%>
<li class="<%= data.completed ? 'completed' : '' %>"> <li class="<%= data.completed ? 'completed' : '' %>">
<div class="view"> <div class="view">
<input class="toggle" type="checkbox" checked="<%= data.completed %>" <input class="toggle" type="checkbox" checked="<%= data.completed %>"
onchange="<%= todos.actions.completed_change(data.id) %>"> onchange="<%= todos.actions.completedOnChange(data.id) %>">
<label ondblclick="<%= todos.actions.editOnDblclick %>"><%= data.title %></label>
<label ondblclick="<%= todos.actions.content_dblclick(data.id) %>"><%= data.title %></label> <button class="destroy" onclick="<%= todos.actions.removeOnClick(data.id) %>"></button>
<button class="destroy" onclick="<%= todos.actions.remove_click(data.id) %>"></button>
</div> </div>
<input class="edit" type="text" value="<%= data.title %>" <input class="edit" type="text" value="<%= data.title %>"
onblur="<%= todos.actions.edit_blur(data.id) %>" onblur="<%= todos.actions.editOnBlur(data.id) %>"
onkeypress="<%= todos.actions.edit_keypress(data.id) %>"> onkeydown="<%= todos.actions.editOnKeydown(data.id) %>">
</li> </li>
<view name="Tasks"> <view name="Tasks">
<%-- this section should hidden by default and shown when there are todos --%> <%-- This section should be hidden by default and shown when there are todos. --%>
<section id="main" if="<%= data.tasks.length %>"> <section class="main" if="<%= data.tasks && data.tasks.length %>">
<input class="toggle-all" type="checkbox" checked="<%= !data.stats.active %>"
<input id="toggle-all" type="checkbox" checked="<%= !data.stats.active %>" onchange="<%= todos.actions.toggleOnChange %>">
onchange="<%= todos.actions.toggle_change %>">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<ul id="todo-list">
<for each="<%= data.tasks %>"> <for each="<%= data.tasks %>">
<call view="Task"> <call view="Task">
</for> </for>
......
<view name="TodoApp"> <view name="TodoApp">
<section id="todoapp"> <section class="todoapp">
<header id="header"> <header class="header">
<h1>todos</h1> <h1>todos</h1>
<input id="new-todo" <input class="new-todo" placeholder="What needs to be done?" autofocus
placeholder="What needs to be done?" onblur="<%= todos.actions.add_blur %>" onkeydown="<%= todos.actions.addOnKeydown %>">
autofocus
onblur="<%= todos.actions.add_blur %>"
onkeypress="<%= todos.actions.add_keypress %>">
</header> </header>
<call view="Tasks" data="data" /> <call view="Tasks" data="data" />
<call view="Stats" data="data.stats" /> <call view="Stats" data="data.stats" />
</section> </section>
...@@ -21,4 +21,17 @@ ...@@ -21,4 +21,17 @@
<servlet-name>routing-servlet</servlet-name> <servlet-name>routing-servlet</servlet-name>
<url-pattern>/</url-pattern> <url-pattern>/</url-pattern>
</servlet-mapping> </servlet-mapping>
<mime-mapping>
<extension>html</extension>
<mime-type>text/html;charset=utf-8</mime-type>
</mime-mapping>
<mime-mapping>
<extension>css</extension>
<mime-type>text/css;charset=utf-8</mime-type>
</mime-mapping>
<mime-mapping>
<extension>js</extension>
<mime-type>application/javascript;charset=utf-8</mime-type>
</mime-mapping>
</web-app> </web-app>
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #c5c5c5;
border-bottom: 1px dashed #f7f7f7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
#issue-count {
display: none;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}
...@@ -12,25 +12,27 @@ button { ...@@ -12,25 +12,27 @@ button {
font-size: 100%; font-size: 100%;
vertical-align: baseline; vertical-align: baseline;
font-family: inherit; font-family: inherit;
font-weight: inherit;
color: inherit; color: inherit;
-webkit-appearance: none; -webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none; appearance: none;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
} }
body { body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em; line-height: 1.4em;
background: #eaeaea url('bg.png'); background: #f5f5f5;
color: #4d4d4d; color: #4d4d4d;
width: 550px; min-width: 230px;
max-width: 550px;
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;
-o-font-smoothing: antialiased;
font-smoothing: antialiased; font-smoothing: antialiased;
font-weight: 300;
} }
button, button,
...@@ -38,85 +40,57 @@ input[type="checkbox"] { ...@@ -38,85 +40,57 @@ input[type="checkbox"] {
outline: none; outline: none;
} }
#todoapp { .hidden {
display: none;
}
.todoapp {
background: #fff; background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0; margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative; position: relative;
border-top-left-radius: 2px; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
border-top-right-radius: 2px; 0 25px 50px 0 rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
} }
#todoapp:before { .todoapp input::-webkit-input-placeholder {
content: ''; font-style: italic;
border-left: 1px solid #f5d6d6; font-weight: 300;
border-right: 1px solid #f5d6d6; color: #e6e6e6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
} }
#todoapp input::-webkit-input-placeholder { .todoapp input::-moz-placeholder {
font-style: italic; font-style: italic;
font-weight: 300;
color: #e6e6e6;
} }
#todoapp input::-moz-placeholder { .todoapp input::input-placeholder {
font-style: italic; font-style: italic;
color: #a9a9a9; font-weight: 300;
color: #e6e6e6;
} }
#todoapp h1 { .todoapp h1 {
position: absolute; position: absolute;
top: -120px; top: -155px;
width: 100%; width: 100%;
font-size: 70px; font-size: 100px;
font-weight: bold; font-weight: 100;
text-align: center; text-align: center;
color: #b3b3b3; color: rgba(175, 47, 47, 0.15);
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility; -webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility; -moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
#header { .new-todo,
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: 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 { .edit {
position: relative; position: relative;
margin: 0; margin: 0;
width: 100%; width: 100%;
font-size: 24px; font-size: 24px;
font-family: inherit; font-family: inherit;
font-weight: inherit;
line-height: 1.4em; line-height: 1.4em;
border: 0; border: 0;
outline: none; outline: none;
...@@ -124,89 +98,83 @@ input[type="checkbox"] { ...@@ -124,89 +98,83 @@ 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);
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-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;
-o-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.02); background: rgba(0, 0, 0, 0.003);
z-index: 2; box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
box-shadow: none;
} }
#main { .main {
position: relative; position: relative;
z-index: 2; z-index: 2;
border-top: 1px dotted #adadad; border-top: 1px solid #e6e6e6;
} }
label[for='toggle-all'] { label[for='toggle-all'] {
display: none; display: none;
} }
#toggle-all { .toggle-all {
position: absolute; position: absolute;
top: -42px; top: -55px;
left: -4px; left: -12px;
width: 40px; width: 60px;
height: 34px;
text-align: center; text-align: center;
/* Mobile Safari */ border: none; /* Mobile Safari */
border: none;
} }
#toggle-all:before { .toggle-all:before {
content: '»'; content: '';
font-size: 28px; font-size: 22px;
color: #d9d9d9; color: #e6e6e6;
padding: 0 25px 7px; 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 dotted #ccc; 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 */
...@@ -215,47 +183,35 @@ label[for='toggle-all'] { ...@@ -215,47 +183,35 @@ label[for='toggle-all'] {
top: 0; top: 0;
bottom: 0; bottom: 0;
margin: auto 0; margin: auto 0;
/* Mobile Safari */ border: none; /* Mobile Safari */
border: none;
-webkit-appearance: none; -webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none; appearance: none;
} }
#todo-list li .toggle:after { .todo-list li .toggle:after {
content: '✔'; 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>');
/* 40 + a couple of pixels visual adjustment */
line-height: 43px;
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
} }
#todo-list li .toggle:checked:after { .todo-list li .toggle:checked:after {
color: #85ada7; 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>');
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
} }
#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;
margin-left: 45px; margin-left: 45px;
display: block; display: block;
line-height: 1.2; line-height: 1.2;
-webkit-transition: color 0.4s;
transition: color 0.4s; transition: color 0.4s;
} }
#todo-list li.completed label { .todo-list li.completed label {
color: #a9a9a9; 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;
...@@ -264,68 +220,65 @@ label[for='toggle-all'] { ...@@ -264,68 +220,65 @@ label[for='toggle-all'] {
width: 40px; width: 40px;
height: 40px; height: 40px;
margin: auto 0; margin: auto 0;
font-size: 22px; font-size: 30px;
color: #a88a8a; color: #cc9a9a;
-webkit-transition: all 0.2s; margin-bottom: 11px;
transition: all 0.2s; transition: color 0.2s ease-out;
} }
#todo-list li .destroy:hover { .todo-list li .destroy:hover {
text-shadow: 0 0 1px #000, color: #af5b5e;
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
transform: scale(1.3);
} }
#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: 0 15px; padding: 10px 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px; height: 20px;
z-index: 1;
text-align: center; text-align: center;
border-top: 1px solid #e6e6e6;
} }
#footer:before { .footer:before {
content: ''; content: '';
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 31px; bottom: 0;
left: 0; left: 0;
height: 50px; height: 50px;
z-index: -1; overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 6px 0 -3px rgba(255, 255, 255, 0.8), 0 8px 0 -3px #f6f6f6,
0 7px 1px -3px rgba(0, 0, 0, 0.3), 0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 43px 0 -6px rgba(255, 255, 255, 0.8), 0 16px 0 -6px #f6f6f6,
0 44px 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;
} }
#filters { .todo-count strong {
font-weight: 300;
}
.filters {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
...@@ -334,69 +287,79 @@ label[for='toggle-all'] { ...@@ -334,69 +287,79 @@ label[for='toggle-all'] {
left: 0; left: 0;
} }
#filters li { .filters li {
display: inline; display: inline;
} }
#filters li a { .filters li a {
color: #83756f; color: inherit;
margin: 2px; margin: 3px;
padding: 3px 7px;
text-decoration: none; text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a.selected,
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
} }
#filters li a.selected { .filters li a.selected {
font-weight: bold; border-color: rgba(175, 47, 47, 0.2);
} }
#clear-completed { .clear-completed,
html .clear-completed:active {
float: right; float: right;
position: relative; position: relative;
line-height: 20px; line-height: 20px;
text-decoration: none; text-decoration: none;
background: rgba(0, 0, 0, 0.1); cursor: pointer;
font-size: 11px; position: relative;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
} }
#clear-completed:hover { .clear-completed:hover {
background: rgba(0, 0, 0, 0.15); text-decoration: underline;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
} }
#info { .info {
margin: 65px auto 0; margin: 65px auto 0;
color: #a6a6a6; color: #bfbfbf;
font-size: 12px; font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center; text-align: center;
} }
#info a { .info p {
line-height: 1;
}
.info a {
color: inherit; color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
} }
/* /*
Hack to remove background from Mobile Safari. Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera 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 {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);
transform: rotate(90deg); transform: rotate(90deg);
-webkit-appearance: none; -webkit-appearance: none;
...@@ -404,151 +367,12 @@ label[for='toggle-all'] { ...@@ -404,151 +367,12 @@ label[for='toggle-all'] {
} }
} }
.hidden { @media (max-width: 430px) {
display: none; .footer {
} height: 50px;
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #C5C5C5;
border-bottom: 1px dashed #F7F7F7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
-webkit-transition-property: left;
transition-property: left;
-webkit-transition-duration: 500ms;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
margin: 0 0 0 300px;
}
.learn-bar > .learn {
left: 8px;
} }
.learn-bar #todoapp { .filters {
width: 550px; bottom: 10px;
margin: 130px auto 40px auto;
} }
} }
# todomvc base file # todomvc base files
/bower_components/todomvc-common/base.css /css/base.css
/css/index.css
/*global window */ /*global window */
/** /**
* @license DUEL v0.8.2 http://duelengine.org * @license DUEL v0.9.7 http://duelengine.org
* Copyright (c)2006-2012 Stephen M. McKamey. * Copyright (c)2006-2012 Stephen M. McKamey.
* Licensed under The MIT License. * Licensed under The MIT License.
*/ */
/*jshint smarttabs:true */
/** /**
* @public * @public
...@@ -105,26 +106,6 @@ var duel = ( ...@@ -105,26 +106,6 @@ var duel = (
return (val instanceof Array); return (val instanceof Array);
}; };
/**
* Determines the type of the value
*
* @private
* @param {*} val the object being tested
* @return {number}
*/
function getType(val) {
switch (typeof val) {
case 'object':
return !val ? NUL : (isArray(val) ? ARY : ((val instanceof Markup) ? RAW : ((val instanceof Date) ? VAL : OBJ)));
case 'function':
return FUN;
case 'undefined':
return NUL;
default:
return VAL;
}
}
/** /**
* Determines if the value is a string * Determines if the value is a string
* *
...@@ -148,135 +129,23 @@ var duel = ( ...@@ -148,135 +129,23 @@ var duel = (
} }
/** /**
* String buffer * Determines the type of the value
*
* @private
* @this {Buffer}
* @constructor
*/
function Buffer() {
/**
* @type {Array|string}
* @private
*/
this.value = Buffer.FAST ? '' : [];
}
/**
* Only IE<9 benefits from Array.join()
*
* @private
* @constant
* @type {boolean}
*/
Buffer.FAST = !(scriptEngine && scriptEngine() < 9);
/**
* Appends to the internal value
*
* @public
* @this {Buffer}
* @param {null|string} v1
* @param {null|string=} v2
* @param {null|string=} v3
*/
Buffer.prototype.append = function(v1, v2, v3) {
if (Buffer.FAST) {
if (v1 !== null) {
this.value += v1;
if (v2 !== null && v2 !== undef) {
this.value += v2;
if (v3 !== null && v3 !== undef) {
this.value += v3;
}
}
}
} else {
this.value.push.apply(
// Closure Compiler type cast
/** @type{Array} */(this.value),
arguments);
}
};
/**
* Clears the internal value
*
* @public
* @this {Buffer}
*/
Buffer.prototype.clear = function() {
this.value = Buffer.FAST ? '' : [];
};
/**
* Renders the value
*
* @public
* @override
* @this {Buffer}
* @return {string} value
*/
Buffer.prototype.toString = function() {
return Buffer.FAST ?
// Closure Compiler type cast
/** @type{string} */(this.value) :
this.value.join('');
};
function digits(n) {
return (n < 10) ? '0'+n : n;
}
/**
* Formats the value as a string
* *
* @private * @private
* @param {*} val the object being rendered * @param {*} val the object being tested
* @return {string|null} * @return {number}
*/ */
function asString(val) { function getType(val) {
var buffer, needsDelim; switch (typeof val) {
switch (getType(val)) { case 'object':
case VAL: return !val ? NUL : (isArray(val) ? ARY : ((val instanceof Markup) ? RAW : ((val instanceof Date) ? VAL : OBJ)));
return ''+val; case 'function':
case NUL: return FUN;
return ''; case 'undefined':
case ARY: return NUL;
// flatten into simple list default:
buffer = new Buffer(); return VAL;
for (var i=0, length=val.length; i<length; i++) {
if (needsDelim) {
buffer.append(', ');
} else {
needsDelim = true;
}
buffer.append(asString(val[i]));
}
return buffer.toString();
case OBJ:
// format JSON-like
buffer = new Buffer();
buffer.append('{');
for (var key in val) {
if (val.hasOwnProperty(key)) {
if (needsDelim) {
buffer.append(', ');
} else {
needsDelim = true;
}
buffer.append(key, '=', asString(val[key]));
}
}
buffer.append('}');
return buffer.toString();
} }
// Closure Compiler type cast
return /** @type{string} */(val);
} }
/** /**
...@@ -402,6 +271,15 @@ var duel = ( ...@@ -402,6 +271,15 @@ var duel = (
*/ */
var NAME = 'name'; var NAME = 'name';
/**
* Callback allowed to modify the bound node
*
* @private
* @param {Array} elem bound node
* @return {Array}
*/
var bindFilter;
var bind; var bind;
/** /**
...@@ -679,7 +557,7 @@ var duel = ( ...@@ -679,7 +557,7 @@ var duel = (
return (v && isFunction(v.getView)) ? return (v && isFunction(v.getView)) ?
// Closure Compiler type cast // Closure Compiler type cast
bind(v.getView(), d, /** @type {number} */i, /** @type {number} */c, /** @type {string} */k, p) : null; bind(v.getView(), d, (/** @type {number} */i), (/** @type {number} */c), (/** @type {string} */k), p) : null;
} }
/** /**
...@@ -752,7 +630,7 @@ var duel = ( ...@@ -752,7 +630,7 @@ var duel = (
for (var i=1, length=node.length; i<length; i++) { for (var i=1, length=node.length; i<length; i++) {
append(elem, bind(node[i], data, index, count, key, parts)); append(elem, bind(node[i], data, index, count, key, parts));
} }
return elem; return bindFilter ? bindFilter(elem) : elem;
case OBJ: case OBJ:
// attribute map // attribute map
...@@ -770,18 +648,75 @@ var duel = ( ...@@ -770,18 +648,75 @@ var duel = (
return node; return node;
}; };
/**
* Boolean attribute map (used by both render.js & dom.js)
*
* @private
* @constant
* @type {Object.<number>}
*/
var ATTR_BOOL = {
'allowfullscreen': 1,
'async': 1,
'autofocus': 1,
'autoplay': 1,
'checked': 1,
'compact': 1,
'controls': 1,
'declare': 1,
'default': 1,
'defaultchecked': 1,
'defaultmuted': 1,
'defaultselected': 1,
'defer': 1,
'disabled': 1,
'draggable': 1,
'enabled': 1,
'formnovalidate': 1,
'hidden': 1,
'indeterminate': 1,
'inert': 1,
'ismap': 1,
'itemscope': 1,
'loop': 1,
'multiple': 1,
'muted': 1,
'nohref': 1,
'noresize': 1,
'noshade': 1,
'novalidate': 1,
'nowrap': 1,
'open': 1,
'pauseonexit': 1,
'readonly': 1,
'required': 1,
'reversed': 1,
'scoped': 1,
'seamless': 1,
'selected': 1,
'sortable': 1,
'spellcheck': 1,
'translate': 1,
'truespeed': 1,
'typemustmatch': 1,
'visible': 1
// update non-readonly attributes as spec changes
// curl -s "http://www.w3.org/TR/html51/single-page.html" | grep "attribute boolean" > boolean.txt
};
/* factory.js --------------------*/ /* factory.js --------------------*/
/** /**
* Renders an error as text * Renders an error directly as text
* *
* @private * @private
* @param {Error} ex The exception * @param {Error} ex The exception
* @return {string} * @return {string|Result}
*/ */
function onError(ex) { var onError = function(ex) {
return '['+ex+']'; return '[ '+ex+' ]';
} };
/** /**
* Wraps a view definition with binding method * Wraps a view definition with binding method
...@@ -816,9 +751,18 @@ var duel = ( ...@@ -816,9 +751,18 @@ var duel = (
isFinite(count) ? count : 1, isFinite(count) ? count : 1,
isString(key) ? key : null); isString(key) ? key : null);
return new Result(result); return new Result(result);
} catch (ex) { } catch (ex) {
// handle error with context // handle error with context
return new Result(onError(ex)); var errValue = onError(ex);
if (errValue instanceof Result) {
return errValue;
} else {
// render the error as a text node
return new Result(''+errValue);
}
} }
}; };
...@@ -845,6 +789,26 @@ var duel = ( ...@@ -845,6 +789,26 @@ var duel = (
return (isFunction(view) && isFunction(view.getView)) ? view : factory(view); return (isFunction(view) && isFunction(view.getView)) ? view : factory(view);
}; };
/**
* @public
* @param {string} value error callback
*/
duel.onerror = function(value) {
if (isFunction(value)) {
onError = value;
}
};
/**
* @public
* @param {string} value onbind filter callback
*/
duel.onbind = function(value) {
if (isFunction(value)) {
bindFilter = value;
}
};
/** /**
* @public * @public
* @param {string} value Markup text * @param {string} value Markup text
...@@ -861,69 +825,132 @@ var duel = ( ...@@ -861,69 +825,132 @@ var duel = (
* *
* @private * @private
* @constant * @constant
* @type {Object.<boolean>} * @type {Object.<number>}
*/ */
var VOID_TAGS = { var VOID_TAGS = {
'area' : true, 'area': 1,
'base' : true, 'base': 1,
'basefont' : true, 'basefont': 1,
'br' : true, 'br': 1,
'col' : true, 'col': 1,
'frame' : true, 'frame': 1,
'hr' : true, 'embed': 1,
'img' : true, 'hr': 1,
'input' : true, 'img': 1,
'isindex' : true, 'input': 1,
'keygen' : true, 'isindex': 1,
'link' : true, 'keygen': 1,
'meta' : true, 'link': 1,
'param' : true, 'menuitem': 1,
'source' : true, 'meta': 1,
'wbr' : true 'param': 1,
'source': 1,
'track': 1,
'wbr': 1
// update elements as spec changes
// http://www.w3.org/TR/html51/single-page.html#void-elements
}; };
/** /**
* Boolean attribute map * String buffer
*
* @private
* @this {Buffer}
* @constructor
*/
function Buffer() {
/**
* @type {Array|string}
* @private
*/
this.value = Buffer.FAST ? '' : [];
}
/**
* IE<9 benefits from Array.join() for large strings
* *
* @private * @private
* @constant * @constant
* @type {Object.<number>} * @type {boolean}
*/ */
var ATTR_BOOL = { Buffer.FAST = !(scriptEngine && scriptEngine() < 9);
'async': 1,
'checked': 1, /**
'defer': 1, * Appends to the internal value
'disabled': 1, *
'hidden': 1, * @public
'novalidate': 1, * @this {Buffer}
'formnovalidate': 1 * @param {string} a
// can add more attributes here as needed * @param {string=} b
* @param {string=} c
*/
Buffer.prototype.append = function(a, b, c) {
var args = arguments;
if (Buffer.FAST) {
var len = args.length;
if (len > 1) {
if (len > 2) {
b += c;
}
a += b;
}
this.value += a;
} else {
this.value.push.apply(
// Closure Compiler type cast
/** @type{Array} */(this.value),
args);
}
}; };
/**
* Renders the value
*
* @public
* @override
* @this {Buffer}
* @return {string} value
*/
Buffer.prototype.toString = function() {
return Buffer.FAST ?
// Closure Compiler type cast
/** @type{string} */(this.value) :
this.value.join('');
};
// /**
// * Resets the internal value
// *
// * @public
// * @this {Buffer}
// */
// Buffer.prototype.clear = function() {
// this.value = Buffer.FAST ? '' : [];
// };
/** /**
* Encodes invalid literal characters in strings * Encodes invalid literal characters in strings
* *
* @private * @private
* @param {Array|Object|string|number} val The value * @param {Array|Object|string|number} val The value
* @return {Array|Object|string|number} * @return {string}
*/ */
function htmlEncode(val) { function htmlEncode(val) {
if (!isString(val)) { if (!isString(val)) {
return val; return (val !== null && val !== undef) ? ''+val : '';
}
return val.replace(/[&<>]/g,
function(ch) {
switch(ch) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
default:
return ch;
} }
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;'
};
return val.replace(/[&<>]/g, function(ch) {
return map[ch] || ch;
}); });
} }
...@@ -932,27 +959,22 @@ var duel = ( ...@@ -932,27 +959,22 @@ var duel = (
* *
* @private * @private
* @param {Array|Object|string|number} val The value * @param {Array|Object|string|number} val The value
* @return {Array|Object|string|number} * @return {string}
*/ */
function attrEncode(val) { function attrEncode(val) {
if (!isString(val)) { if (!isString(val)) {
return val; return (val !== null && val !== undef) ? ''+val : '';
}
return val.replace(/[&<>"]/g,
function(ch) {
switch(ch) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;';
default:
return ch;
} }
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;'
};
return val.replace(/[&<>"]/g, function(ch) {
return map[ch] || ch;
}); });
} }
...@@ -964,9 +986,10 @@ var duel = ( ...@@ -964,9 +986,10 @@ var duel = (
* @param {Array} node The result tree * @param {Array} node The result tree
*/ */
function renderComment(buffer, node) { function renderComment(buffer, node) {
if (node[0] === '!DOCTYPE') { if (node[0].toLowerCase() === '!doctype') {
// emit doctype // emit doctype
buffer.append('<!DOCTYPE ', node[1], '>'); buffer.append('<!doctype ', node[1], '>');
} else { } else {
// emit HTML comment // emit HTML comment
buffer.append('<!--', node[1], '-->'); buffer.append('<!--', node[1], '-->');
...@@ -1002,20 +1025,23 @@ var duel = ( ...@@ -1002,20 +1025,23 @@ var duel = (
for (var name in child) { for (var name in child) {
if (child.hasOwnProperty(name)) { if (child.hasOwnProperty(name)) {
var val = child[name]; var val = child[name];
if (ATTR_BOOL[name]) { if (ATTR_BOOL[name.toLowerCase()]) {
if (val) { if (val) {
val = name; val = name;
} else { } else {
// falsey boolean attributes must not be present // falsey boolean attributes must not be present
continue; continue;
} }
} }
if (getType(val) === NUL) {
// null/undefined removes attributes
continue;
}
buffer.append(' ', name); buffer.append(' ', name);
if (getType(val) !== NUL) {
// Closure Compiler type cast // Closure Compiler type cast
buffer.append('="', /** @type{string} */(attrEncode(val)), '"'); buffer.append('="', attrEncode(val), '"');
}
} }
} }
i++; i++;
...@@ -1031,10 +1057,10 @@ var duel = ( ...@@ -1031,10 +1057,10 @@ var duel = (
child = node[i]; child = node[i];
if (isArray(child)) { if (isArray(child)) {
renderElem(buffer, child); renderElem(buffer, child);
} else { } else {
// encode string literals // encode string literals
// Closure Compiler type cast buffer.append(htmlEncode(child));
buffer.append(/** @type{string} */(htmlEncode(child)));
} }
} }
...@@ -1042,6 +1068,8 @@ var duel = ( ...@@ -1042,6 +1068,8 @@ var duel = (
// emit close tag // emit close tag
buffer.append('</', tag, '>'); buffer.append('</', tag, '>');
} }
return buffer;
} }
/** /**
...@@ -1053,12 +1081,19 @@ var duel = ( ...@@ -1053,12 +1081,19 @@ var duel = (
*/ */
function render(view) { function render(view) {
try { try {
var buffer = new Buffer(); return renderElem(new Buffer(), view).toString();
renderElem(buffer, view);
return buffer.toString();
} catch (ex) { } catch (ex) {
// handle error with context // handle error with context
return onError(ex); var errValue = onError(ex);
if (errValue instanceof Result) {
return render(errValue.value);
} else {
// render the error as a string
return (''+errValue);
}
} }
} }
...@@ -1111,17 +1146,40 @@ var duel = ( ...@@ -1111,17 +1146,40 @@ var duel = (
* @type {Object.<string>} * @type {Object.<string>}
*/ */
var ATTR_MAP = { var ATTR_MAP = {
'rowspan': 'rowSpan', 'allowfullscreen': 'allowFullscreen',
'colspan': 'colSpan', 'accesskey': 'accessKey',
'bgcolor': 'bgColor',
'cellpadding': 'cellPadding', 'cellpadding': 'cellPadding',
'cellspacing': 'cellSpacing', 'cellspacing': 'cellSpacing',
'tabindex': 'tabIndex', 'checked': 'defaultChecked',
'accesskey': 'accessKey', 'class': 'className',
'colspan': 'colSpan',
'contenteditable': 'contentEditable',
'defaultchecked': 'defaultChecked',
'defaultselected': 'defaultSelected',
'defaultmuted': 'defaultMuted',
'for': 'htmlFor',
'formnovalidate': 'formNoValidate',
'hidefocus': 'hideFocus', 'hidefocus': 'hideFocus',
'usemap': 'useMap', 'ismap': 'isMap',
'itemscope': 'itemScope',
'maxlength': 'maxLength', 'maxlength': 'maxLength',
'muted': 'defaultMuted',
'nohref': 'noHref',
'noresize': 'noResize',
'noshade': 'noShade',
'novalidate': 'noValidate',
'nowrap': 'noWrap',
'pauseonexit': 'pauseOnExit',
'readonly': 'readOnly', 'readonly': 'readOnly',
'contenteditable': 'contentEditable' 'rowspan': 'rowSpan',
'selected': 'defaultSelected',
'spellcheck': 'spellCheck',
'tabindex': 'tabIndex',
'truespeed': 'trueSpeed',
'typemustmatch': 'typeMustMatch',
'usemap': 'useMap',
'willvalidate': 'willValidate'
// can add more attributes here as needed // can add more attributes here as needed
}; };
...@@ -1134,8 +1192,22 @@ var duel = ( ...@@ -1134,8 +1192,22 @@ var duel = (
*/ */
var ATTR_DUP = { var ATTR_DUP = {
'enctype': 'encoding', 'enctype': 'encoding',
'onscroll': 'DOMMouseScroll', 'onscroll': 'DOMMouseScroll'
'checked': 'defaultChecked' // can add more attributes here as needed
};
/**
* Attributes to be set via DOM
*
* @private
* @constant
* @type {Object.<number>}
*/
var ATTR_DOM = {
'autocapitalize': 1,
'autocomplete': 1,
'autocorrect': 1,
'type': 1
// can add more attributes here as needed // can add more attributes here as needed
}; };
...@@ -1255,28 +1327,50 @@ var duel = ( ...@@ -1255,28 +1327,50 @@ var duel = (
* @param {function(Event)} handler The event handler * @param {function(Event)} handler The event handler
*/ */
function addHandler(elem, name, handler) { function addHandler(elem, name, handler) {
if (name.substr(0,2) === 'on') {
name = name.substr(2);
}
switch (typeof handler) { switch (typeof handler) {
case 'function': case 'function':
if (elem.addEventListener) { if (elem.addEventListener) {
// DOM Level 2 // DOM Level 2
elem.addEventListener((name.substr(0,2) === 'on') ? name.substr(2) : name, handler, false); elem.addEventListener(name, handler, false);
} else if (isFunction(window.jQuery) && getType(elem[name]) !== NUL) {
// cop out and patch IE6-8 with jQuery
var $elem = window.jQuery(elem);
if (isFunction($elem.on)) {
$elem.on(name, handler); // v1.7+
} else {
$elem.bind(name, handler); // pre-1.7
}
} else if (elem.attachEvent && getType(elem[name]) !== NUL) {
// IE legacy events
elem.attachEvent('on'+name, handler);
} else { } else {
// DOM Level 0 // DOM Level 0
elem[name] = handler; var old = elem['on'+name] || elem[name];
elem['on'+name] = elem[name] = !isFunction(old) ? handler :
function(e) {
return (old.call(this, e) !== false) && (handler.call(this, e) !== false);
};
} }
break; break;
case 'string': case 'string':
// inline functions are DOM Level 0 // inline functions are DOM Level 0
/*jslint evil:true */ /*jslint evil:true */
elem[name] = new Function('event', handler); elem['on'+name] = new Function('event', handler);
/*jslint evil:false */ /*jslint evil:false */
break; break;
} }
} }
/** /**
* Appends a child to an element * Appends an attribute to an element
* *
* @private * @private
* @param {Node} elem The element * @param {Node} elem The element
...@@ -1304,52 +1398,70 @@ var duel = ( ...@@ -1304,52 +1398,70 @@ var duel = (
if (name) { if (name) {
if (type === NUL) { if (type === NUL) {
value = ''; // null/undefined removes attributes
type = VAL; continue;
} }
name = ATTR_MAP[name.toLowerCase()] || name; name = ATTR_MAP[name.toLowerCase()] || name;
if (ATTR_BOOL[name]) {
elem[name] = !!value;
// also set duplicated attributes
if (ATTR_DUP[name]) {
elem[ATTR_DUP[name]] = !!value;
}
} else if (name === 'style') { if (name === 'style') {
if (typeof elem.style.cssText !== 'undefined') { if (getType(elem.style.cssText) !== NUL) {
elem.style.cssText = value; elem.style.cssText = value;
} else { } else {
elem.style = value; elem.style = value;
} }
} else if (name === 'class') {
elem.className = value;
} else if (name.substr(0,2) === 'on') { } else if (name.substr(0,2) === 'on') {
addHandler(elem, name, value); addHandler(elem, name, value);
// also set duplicated events // also set duplicated events
if (ATTR_DUP[name]) { name = ATTR_DUP[name];
addHandler(elem, ATTR_DUP[name], value); if (name) {
addHandler(elem, name, value);
} }
} else if (type === VAL && name.charAt(0) !== '$') { } else if (!ATTR_DOM[name.toLowerCase()] && (type !== VAL || name.charAt(0) === '$' || getType(elem[name]) !== NUL || getType(elem[ATTR_DUP[name]]) !== NUL)) {
elem.setAttribute(name, value); // direct setting of existing properties
try {
elem[name] = value;
// also set duplicated properties
name = ATTR_DUP[name];
if (name) {
elem[name] = value;
}
} catch(ex2) {
if (name.toLowerCase() === 'type' && elem.tagName.toLowerCase() === 'input') {
// IE9 doesn't like HTML5 input types
continue;
}
throw new Error('DOM property '+elem.tagName+'.'+name+': '+ex2);
}
} else if (ATTR_BOOL[name.toLowerCase()]) {
if (value) {
// boolean attributes
elem.setAttribute(name, name);
// also set duplicated attributes // also set duplicated attributes
if (ATTR_DUP[name]) { name = ATTR_DUP[name];
elem.setAttribute(ATTR_DUP[name], value); if (name) {
elem.setAttribute(name, name);
}
} }
} else { } else {
// allow direct setting of complex properties // http://www.quirksmode.org/dom/w3c_core.html#attributes
elem[name] = value;
// custom and 'data-*' attributes
elem.setAttribute(name, value);
// also set duplicated attributes // also set duplicated attributes
if (ATTR_DUP[name]) { name = ATTR_DUP[name];
elem[ATTR_DUP[name]] = value; if (name) {
elem.setAttribute(name, value);
} }
} }
} }
...@@ -1445,8 +1557,11 @@ var duel = ( ...@@ -1445,8 +1557,11 @@ var duel = (
try { try {
delete elem[key]; delete elem[key];
} catch (ex) { } catch (ex) {
// sometimes IE doesn't like deleting from DOM try {
elem[key] = undef; // IE7 doesn't like deleting from DOM
elem[key] = '';
elem.removeAttribute(key);
} catch (ex2) {}
} }
if (!isFunction(method)) { if (!isFunction(method)) {
...@@ -1454,7 +1569,7 @@ var duel = ( ...@@ -1454,7 +1569,7 @@ var duel = (
/*jslint evil:true */ /*jslint evil:true */
method = new Function(''+method); method = new Function(''+method);
/*jslint evil:false */ /*jslint evil:false */
} catch (ex2) { } catch (ex3) {
// filter // filter
method = null; method = null;
} }
...@@ -1559,17 +1674,6 @@ var duel = ( ...@@ -1559,17 +1674,6 @@ var duel = (
return elem; return elem;
} }
/**
* Renders an error as a text node
*
* @private
* @param {Error} ex The exception
* @return {Node}
*/
function onErrorDOM(ex) {
return document.createTextNode(onError(ex));
}
/** /**
* Returns result as DOM objects * Returns result as DOM objects
* *
...@@ -1582,9 +1686,14 @@ var duel = ( ...@@ -1582,9 +1686,14 @@ var duel = (
Result.prototype.toDOM = function(elem, merge) { Result.prototype.toDOM = function(elem, merge) {
// resolve the element ID // resolve the element ID
if (getType(elem) === VAL) { if (getType(elem) === VAL) {
// try as id, then as query selector
elem = document.getElementById( elem = document.getElementById(
// Closure Compiler type cast // Closure Compiler type cast
/** @type{string} */(elem)); /** @type{string} */(elem)) || (('querySelector' in document) ?
document.querySelector(
// Closure Compiler type cast
/** @type{string} */(elem)) :
null);
} }
var view; var view;
...@@ -1598,7 +1707,15 @@ var duel = ( ...@@ -1598,7 +1707,15 @@ var duel = (
} catch (ex) { } catch (ex) {
// handle error with context // handle error with context
view = onErrorDOM(ex); var errValue = onError(ex);
if (errValue instanceof Result) {
return errValue.toDOM(elem || view);
} else {
// render the error as a text node
view = document.createTextNode(''+errValue);
}
} }
if (elem && elem.parentNode) { if (elem && elem.parentNode) {
...@@ -1643,7 +1760,7 @@ var duel = ( ...@@ -1643,7 +1760,7 @@ var duel = (
} catch (ex) { } catch (ex) {
/*jslint evil:true*/ /*jslint evil:true*/
doc = doc.open('text/html'); doc = doc.open('text/html');
doc.write(this.toString()); doc.write(''+this);
doc.close(); doc.close();
/*jslint evil:false*/ /*jslint evil:false*/
} }
......
/* global _ */
(function () { (function () {
'use strict'; 'use strict';
/* jshint ignore:start */
// Underscore's Template Module // Underscore's Template Module
// Courtesy of underscorejs.org // Courtesy of underscorejs.org
var _ = (function (_) { var _ = (function (_) {
...@@ -112,8 +114,14 @@ ...@@ -112,8 +114,14 @@
})({}); })({});
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 */
function redirect() { function redirect() {
if (location.hostname === 'tastejs.github.io') { if (location.hostname === 'tastejs.github.io') {
...@@ -175,13 +183,17 @@ ...@@ -175,13 +183,17 @@
if (learnJSON.backend) { if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend; this.frameworkJSON = learnJSON.backend;
this.frameworkJSON.issueLabel = framework;
this.append({ this.append({
backend: true backend: true
}); });
} else if (learnJSON[framework]) { } else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework]; this.frameworkJSON = learnJSON[framework];
this.frameworkJSON.issueLabel = framework;
this.append(); this.append();
} }
this.fetchIssueCount();
} }
Learn.prototype.append = function (opts) { Learn.prototype.append = function (opts) {
...@@ -212,6 +224,26 @@ ...@@ -212,6 +224,26 @@
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
}; };
Learn.prototype.fetchIssueCount = function () {
var issueLink = document.getElementById('issue-count-link');
if (issueLink) {
var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos');
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) {
var count = parsedResponse.length;
if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline';
}
}
};
xhr.send();
}
};
redirect(); redirect();
getFile('learn.json', Learn); getFile('learn.json', Learn);
})(); })();
# todomvc base file # todomvc base file
/bower_components/todomvc-common/base.js #/js/lib/todos.js
# libraries # libraries
/js/lib/duel.js /js/lib/duel.js
# model # compiled views
/js/todos/model.js
# views
/js/todos/views/Stats.js /js/todos/views/Stats.js
/js/todos/views/Task.js /js/todos/views/Task.js
/js/todos/views/Tasks.js /js/todos/views/Tasks.js
/js/todos/views/TodoApp.js /js/todos/views/TodoApp.js
# model
/js/todos/model.js
# controller # controller
/js/todos/controller.js /js/todos/controller.js
...@@ -8,52 +8,36 @@ var todos = todos || {}; ...@@ -8,52 +8,36 @@ var todos = todos || {};
/*-- private members -------------------------------*/ /*-- private members -------------------------------*/
var ESC_KEY = 27;
var ENTER_KEY = 13; var ENTER_KEY = 13;
var STATS_ID = 'footer';
var TODOAPP_ID = 'todoapp';
var TASKS_ID = 'main';
var LIST_ID = 'todo-list';
var EDITING_CSS = 'editing';
function getById(id) {
return document.getElementById(id);
}
function refreshStats(stats) {
// get the data
var data = stats || todos.model.stats();
// build the view // Poor man's routing.
var view = todos.views.Stats(data).toDOM(); addEventListener('hashchange', refreshView, false);
// replace old stats function curFilter() {
var old = getById(STATS_ID); return document.location.hash.substr(2);
if (old) {
old.parentNode.replaceChild(view, old);
} else {
getById(TODOAPP_ID).appendChild(view);
} }
function find(selector, scope) {
return (scope || document).querySelector(selector);
} }
function refreshAll() { function refreshView() {
// get the data // get the data
var data = { var data = todos.model.viewData(curFilter());
tasks: todos.model.tasks(),
stats: todos.model.stats()
};
// build the view // build the view
var view = todos.views.Tasks(data).toDOM(); var view = todos.views.TodoApp(data).toDOM();
// replace old task list var old = find('.todoapp');
var old = getById(TASKS_ID);
if (old) { if (old) {
// replace old task list
old.parentNode.replaceChild(view, old); old.parentNode.replaceChild(view, old);
} else { } else {
getById(TODOAPP_ID).appendChild(view); // insert at top
document.body.insertBefore(view, document.body.firstChild);
} }
find('.new-todo').focus();
refreshStats(data.stats);
} }
function add(input) { function add(input) {
...@@ -64,16 +48,8 @@ var todos = todos || {}; ...@@ -64,16 +48,8 @@ var todos = todos || {};
return; return;
} }
var task = todos.model.add(title); todos.model.add(title);
refreshView();
var list = getById(LIST_ID);
if (list) {
// add new at the top
list.appendChild(todos.views.Task(task).toDOM());
refreshStats();
} else {
refreshAll();
}
} }
function edit(input, id) { function edit(input, id) {
...@@ -85,99 +61,91 @@ var todos = todos || {}; ...@@ -85,99 +61,91 @@ var todos = todos || {};
} else { } else {
todos.model.remove(id); todos.model.remove(id);
} }
refreshAll(); refreshView();
}
function reset(input, id) {
var task = todos.model.find(id);
if (task) {
input.value = task.title;
}
} }
/*-- export public interface -------------------------------*/ /*-- export public interface -------------------------------*/
// event handlers // event handlers
todos.actions = { todos.actions = {
addBlur: function () { addOnKeydown: function (e) {
add(this);
},
add_keypress: function (e) {
if (e.keyCode === ENTER_KEY) { if (e.keyCode === ENTER_KEY) {
add(this); add(this);
} else if (e.keyCode === ESC_KEY) {
refreshView();
} }
}, },
edit_blur: function (id) { editOnBlur: function (id) {
// create a closure around the ID // create a closure around the ID
return function () { return function () {
edit(this, id); edit(this, id);
}; };
}, },
edit_keypress: function () { editOnKeydown: function (id) {
// create a closure around the ID // create a closure around the ID
return function (e) { return function (e) {
if (e.keyCode === ENTER_KEY) { if (e.keyCode === ENTER_KEY) {
// just blur so doesn't get triggered twice // just blur so doesn't get triggered twice
this.blur(); this.blur();
} else if (e.keyCode === ESC_KEY) {
reset(this, id);
this.blur();
} }
}; };
}, },
remove_click: function (id) { removeOnClick: function (id) {
// create a closure around the ID // create a closure around the ID
return function () { return function () {
todos.model.remove(id); todos.model.remove(id);
refreshAll(); refreshView();
}; };
}, },
clear_click: function () { clearOnClick: function () {
todos.model.expunge(); todos.model.expunge();
refreshAll(); refreshView();
}, },
content_dblclick: function () { editOnDblclick: function () {
// create a closure around the ID var self = this;
var toggleEditingMode = function (li) { while (self.tagName !== 'LI') {
if (li.tagName !== 'LI') { self = self.parentNode;
return toggleEditingMode(li.parentNode);
} }
li.className = EDITING_CSS; self.className = 'editing';
var input = li.getElementsByTagName('input')[1]; var input = find('input[type=text]', self);
if (input) {
input.focus(); input.focus();
input.value = input.value; }
};
return function () {
toggleEditingMode(this);
};
}, },
completed_change: function (id) { completedOnChange: function (id) {
// create a closure around the ID // create a closure around the ID
return function () { return function () {
var checkbox = this; todos.model.toggle(id, this.checked);
todos.model.toggle(id, checkbox.checked); refreshView();
refreshAll();
}; };
}, },
toggle_change: function () { toggleOnChange: function () {
var checkbox = this; todos.model.toggleAll(this.checked);
todos.model.toggleAll(checkbox.checked); refreshView();
refreshAll();
} }
}; };
/*-- init task list -------------------------------*/ /*-- init task list -------------------------------*/
(function (body) { refreshView();
// build out task list
var view = todos.views.TodoApp({
tasks: todos.model.tasks(),
stats: todos.model.stats()
}).toDOM();
// insert at top
body.insertBefore(view, body.firstChild);
})(document.body);
})(todos, window.document); })(todos, window.document);
...@@ -32,6 +32,12 @@ var todos = todos || {}; ...@@ -32,6 +32,12 @@ var todos = todos || {};
}; };
} }
function find(id) {
return tasks.filter(function (task) {
return task.id === id;
})[0];
}
function save() { function save() {
// if doesn't support JSON then will be directly stored in polyfill // if doesn't support JSON then will be directly stored in polyfill
var value = typeof JSON !== 'undefined' ? JSON.stringify(tasks) : tasks; var value = typeof JSON !== 'undefined' ? JSON.stringify(tasks) : tasks;
...@@ -47,25 +53,40 @@ var todos = todos || {}; ...@@ -47,25 +53,40 @@ var todos = todos || {};
tasks = []; tasks = [];
} }
/*-- export public interface -------------------------------*/ function filterTasks(filter) {
if (filter === 'completed' || filter === 'active') {
todos.model = { var completed = filter === 'completed';
tasks: function () { return tasks.filter(function (task) {
return task.completed === completed;
});
}
return tasks; return tasks;
}, }
stats: function () {
var stats = {};
stats.total = tasks.length; function calcStats(filter) {
var stats = {
total: tasks.length,
stats.completed = tasks.filter(function (task) { completed: tasks.filter(function (task) {
return task.completed; return task.completed;
}).length; }).length,
filter: filter || ''
};
stats.active = stats.total - stats.completed; stats.active = stats.total - stats.completed;
return stats; return stats;
}
/*-- export public interface -------------------------------*/
todos.model = {
viewData: function (filter) {
return {
tasks: filterTasks(filter),
stats: calcStats(filter)
};
}, },
add: function (title) { add: function (title) {
...@@ -77,19 +98,17 @@ var todos = todos || {}; ...@@ -77,19 +98,17 @@ var todos = todos || {};
return task; return task;
}, },
find: find,
edit: function (id, title) { edit: function (id, title) {
tasks.filter(function (task) { (find(id) || {}).title = title;
return task.id === id;
})[0].title = title;
save(); save();
}, },
// toggle completion of task // toggle completion of task
toggle: function (id, completed) { toggle: function (id, completed) {
tasks.filter(function (task) { (find(id) || {}).completed = completed;
return task.id === id;
})[0].completed = completed;
save(); save();
}, },
...@@ -104,21 +123,23 @@ var todos = todos || {}; ...@@ -104,21 +123,23 @@ var todos = todos || {};
}, },
remove: function (id) { remove: function (id) {
tasks.forEach(function (task, index) { // traverse in reverse for removals
if (task.id === id) { for (var i = tasks.length - 1; i >= 0; i--) {
tasks.splice(index, 1); if (tasks[i].id === id) {
tasks.splice(i, 1);
}
} }
});
save(); save();
}, },
expunge: function () { expunge: function () {
tasks.forEach(function (task, index) { // traverse in reverse for removals
if (task.completed) { for (var i = tasks.length - 1; i >= 0; i--) {
tasks.splice(index, 1); if (tasks[i].completed) {
tasks.splice(i, 1);
}
} }
});
save(); save();
} }
......
{ {
"targetDir": "www/", "targetDir": "www/",
"sourceDir": "target/todomvc/", "sourceDir": "target/todomvc/",
"serverPrefix" : "com.example.todos.views", "serverPrefix" : "com.todomvc.duel.views",
"cdnMap": "cdn", "cdnMap": "cdn",
"cdnLinksMap": "cdnLinks", "cdnLinksMap": "cdnLinks",
"cdnHost": "./", "cdnHost": "./",
......
/*
DUEL v0.9.7 http://duelengine.org
Copyright (c)2006-2012 Stephen M. McKamey.
Licensed under The MIT License.
*/
var duel=function(m,C,D){function y(a){this.value=a}function l(a){return"function"===typeof a}function k(a){switch(typeof a){case "object":return!a?0:z(a)?2:a instanceof y?5:a instanceof Date?4:3;case "function":return 1;case "undefined":return 0;default:return 4}}function p(a){z(a)||(a=["",a]);this.value=a}function r(a,b){switch(k(b)){case 2:if(""===b[0])for(var d=1,f=b.length;d<f;d++)r(a,b[d]);else a.push(b);break;case 3:if(1===a.length)a.push(b);else if(d=a[1],3===k(d))for(f in b)b.hasOwnProperty(f)&&
(d[f]=b[f]);else a.splice(1,0,b);break;case 4:""!==b&&(b=""+b,d=a.length-1,0<d&&4===k(a[d])?a[d]+=b:a.push(b));break;case 0:break;default:a.push(b)}}function s(a,b,d,f,c,e){var g=3===k(a[1]);if(a.length===(g?3:2))return n(a[a.length-1],b,d,f,c,e);for(var h=[""],g=g?2:1,j=a.length;g<j;g++)r(h,n(a[g],b,d,f,c,e));return h}function E(a,b,d,f,c,e){for(var g=1,h=a.length;g<h;g++){var j=a[g],i=j[1].test;if(3===k(j[1])&&i&&(l(i)&&(i=i(b,d,f,c)),!i))continue;return s(j,b,d,f,c,e)}return null}function q(){this.value=
q.FAST?"":[]}function O(a){if("string"!==typeof a)return null!==a&&a!==D?""+a:"";var b={"&":"&amp;","<":"&lt;",">":"&gt;"};return a.replace(/[&<>]/g,function(a){return b[a]||a})}function P(a){if("string"!==typeof a)return null!==a&&a!==D?""+a:"";var b={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"};return a.replace(/[&<>"]/g,function(a){return b[a]||a})}function F(a,b){var d=b[0]||"",f=b.length,c=1,e,g=Q[d];if("!"===d.charAt(0))"!doctype"===b[0].toLowerCase()?a.append("<!doctype ",b[1],">"):a.append("<\!--",
b[1],"--\>");else{if(d){a.append("<",d);e=b[c];if(3===k(e)){for(var h in e)if(e.hasOwnProperty(h)){var j=e[h];if(G[h.toLowerCase()])if(j)j=h;else continue;0!==k(j)&&(a.append(" ",h),a.append('="',P(j),'"'))}c++}g&&a.append(" /");a.append(">")}for(;c<f;c++)e=b[c],z(e)?F(a,e):a.append(O(e));d&&!g&&a.append("</",d,">");return a}}function H(a){try{return F(new q,a).toString()}catch(b){return a=v(b),a instanceof p?H(a.value):""+a}}function t(a){if(a){if("!"===a.charAt(0))return m.createComment("!"===a?
"":a.substr(1)+" ")}else{if(m.createDocumentFragment)return m.createDocumentFragment();a=""}return"style"===a.toLowerCase()&&m.createStyleSheet?m.createStyleSheet():m.createElement(a)}function w(a,b){if(b){var d=(a.tagName||"").toLowerCase();if(8===a.nodeType)3===b.nodeType&&(a.nodeValue+=b.nodeValue);else if("table"===d&&a.tBodies)if(b.tagName)if((d=b.tagName.toLowerCase())&&"tbody"!==d&&"thead"!==d){var f=0<a.tBodies.length?a.tBodies[a.tBodies.length-1]:null;f||(f=t("th"===d?"thead":"tbody"),a.appendChild(f));
f.appendChild(b)}else!1!==a.canHaveChildren&&a.appendChild(b);else{if(11===b.nodeType)for(;b.firstChild;)w(a,b.removeChild(b.firstChild))}else if("style"===d&&m.createStyleSheet)a.cssText=b;else if(!1!==a.canHaveChildren)a.appendChild(b);else if("object"===d&&b.tagName&&"param"===b.tagName.toLowerCase()){try{a.appendChild(b)}catch(c){}try{a.object&&(a.object[b.name]=b.value)}catch(e){}}}}function I(a,b,d){"on"===b.substr(0,2)&&(b=b.substr(2));switch(typeof d){case "function":if(a.addEventListener)a.addEventListener(b,
d,!1);else if(l(window.jQuery)&&0!==k(a[b]))if(a=window.jQuery(a),l(a.on))a.on(b,d);else a.bind(b,d);else if(a.attachEvent&&0!==k(a[b]))a.attachEvent("on"+b,d);else{var f=a["on"+b]||a[b];a["on"+b]=a[b]=!l(f)?d:function(a){return!1!==f.call(this,a)&&!1!==d.call(this,a)}}break;case "string":a["on"+b]=new Function("event",d)}}function J(a){return!!a&&3===a.nodeType&&(!a.nodeValue||!/\S/.exec(a.nodeValue))}function K(a,b){a&&(3===a.nodeType&&b.exec(a.nodeValue))&&(a.nodeValue=a.nodeValue.replace(b,""))}
function A(a){if(a){for(;J(a.firstChild);)a.removeChild(a.firstChild);for(K(a.firstChild,R);J(a.lastChild);)a.removeChild(a.lastChild);K(a.lastChild,S)}}function L(a,b){var d=a[b];if(d){try{delete a[b]}catch(f){try{a[b]="",a.removeAttribute(b)}catch(c){}}if(!l(d))try{d=new Function(""+d)}catch(e){d=null}}return d}function M(a){if(a){var b=L(a,"$init");b&&b.call(a);(b=L(a,"$load"))?setTimeout(function(){b.call(a);b=a=null},0):b=a=null}}function N(a,b){for(var d=1,f=b.length;d<f;d++){var c=b[d];switch(k(c)){case 2:var e=
c[0],c=N(t(e),c);if("html"===e)return A(c),M(c),c;w(a,c);break;case 4:""!==c&&w(a,m.createTextNode(""+c));break;case 3:if(1===a.nodeType){var e=a,g=c;if(g.name&&m.attachEvent&&!e.parentNode)try{var h=t("<"+e.tagName+' name="'+g.name+'">');e.tagName===h.tagName&&(e=h)}catch(j){}c=void 0;for(c in g)if(g.hasOwnProperty(c)){var i=g[c],l=k(i);if(c&&0!==l)if(c=T[c.toLowerCase()]||c,"style"===c)0!==k(e.style.cssText)?e.style.cssText=i:e.style=i;else if("on"===c.substr(0,2))I(e,c,i),(c=u[c])&&I(e,c,i);else if(!U[c.toLowerCase()]&&
(4!==l||"$"===c.charAt(0)||0!==k(e[c])||0!==k(e[u[c]])))try{e[c]=i,(c=u[c])&&(e[c]=i)}catch(n){if(!("type"===c.toLowerCase()&&"input"===e.tagName.toLowerCase()))throw Error("DOM property "+e.tagName+"."+c+": "+n);}else G[c.toLowerCase()]?i&&(e.setAttribute(c,c),(c=u[c])&&e.setAttribute(c,c)):(e.setAttribute(c,i),(c=u[c])&&e.setAttribute(c,i))}a=e}break;case 5:e=w;g=a;i=c;c=t("div");c.innerHTML=""+i;A(c);if(1===c.childNodes.length)c=c.firstChild;else{for(i=t("");c.firstChild;)i.appendChild(c.firstChild);
c=i}e(g,c)}}A(a);M(a);11===a.nodeType&&1===a.childNodes.length&&(a=a.firstChild);return a}y.prototype.toString=function(){return this.value};var z=Array.isArray||function(a){return a instanceof Array},B,n;n=function(a,b,d,f,c,e){switch(k(a)){case 1:return a(b,d,f,c);case 2:var g=a[0]||"";switch(g){case "$for":a:{var h=a[1]||{},g=[""],j;if(h.hasOwnProperty("count")){j=h.count;l(j)&&(j=j(b,d,f,c));h.hasOwnProperty("data")?(h=h.data,l(h)&&(h=h(b,d,f,c))):h=b;for(b=0;b<j;b++)r(g,s(a,h,b,j,null,e))}else{if(h.hasOwnProperty("in")){var i=
h["in"];l(i)&&(i=i(b,d,f,c));if(3===k(i)){h=[];for(j in i)i.hasOwnProperty(j)&&h.push(j);b=0;for(j=h.length;b<j;b++)r(g,s(a,i[h[b]],b,j,h[b],e));a=g;break a}h=i}else h=h.each,l(h)&&(h=h(b,d,f,c));b=k(h);if(2===b){b=0;for(j=h.length;b<j;b++)r(g,s(a,h[b],b,j,null,e))}else 0!==b&&(g=s(a,h,0,1,null,e))}a=g}return a;case "$xor":return E(a,b,d,f,c,e);case "$if":return E(["$xor",a],b,d,f,c,e);case "$call":e=a[1]||{};if(e.view){g=n(e.view,b,d,f,c);h=e.hasOwnProperty("data")?n(e.data,b,d,f,c):b;j=e.hasOwnProperty("index")?
n(e.index,b,d,f,c):d;i=e.hasOwnProperty("count")?n(e.count,b,d,f,c):f;b=e.hasOwnProperty("key")?n(e.key,b,d,f,c):c;d={};for(f=a.length-1;2<=f;f--)c=a[f],e=c[1]||{},e.hasOwnProperty("name")&&(d[e.name]=c);a=g&&l(g.getView)?n(g.getView(),h,j,i,b,d):null}else a=null;return a;case "$part":return g=(a[1]||{}).name||"",g=e&&e.hasOwnProperty(g)?e[g]:a,s(g,b,d,f,c)}g=[g];h=1;for(j=a.length;h<j;h++)r(g,n(a[h],b,d,f,c,e));return B?B(g):g;case 3:e={};for(g in a)a.hasOwnProperty(g)&&(e[g]=n(a[g],b,d,f,c));return e}return a};
var G={allowfullscreen:1,async:1,autofocus:1,autoplay:1,checked:1,compact:1,controls:1,declare:1,"default":1,defaultchecked:1,defaultmuted:1,defaultselected:1,defer:1,disabled:1,draggable:1,enabled:1,formnovalidate:1,hidden:1,indeterminate:1,inert:1,ismap:1,itemscope:1,loop:1,multiple:1,muted:1,nohref:1,noresize:1,noshade:1,novalidate:1,nowrap:1,open:1,pauseonexit:1,readonly:1,required:1,reversed:1,scoped:1,seamless:1,selected:1,sortable:1,spellcheck:1,translate:1,truespeed:1,typemustmatch:1,visible:1},
v=function(a){return"[ "+a+" ]"},x=function(a){if(!l(a)||!l(a.getView)){var b=a;2!==k(b)&&(b=["",b]);a=function(a,f,c,e){try{var g=n(b,a,isFinite(f)?f:0,isFinite(c)?c:1,"string"===typeof e?e:null);return new p(g)}catch(h){return a=v(h),a instanceof p?a:new p(""+a)}};a.getView=function(){return b}}return a};x.onerror=function(a){l(a)&&(v=a)};x.onbind=function(a){l(a)&&(B=a)};x.raw=function(a){return new y(a)};var Q={area:1,base:1,basefont:1,br:1,col:1,frame:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,
link:1,menuitem:1,meta:1,param:1,source:1,track:1,wbr:1};q.FAST=!(C&&9>C());q.prototype.append=function(a,b,d){var f=arguments;q.FAST?(f=f.length,1<f&&(2<f&&(b+=d),a+=b),this.value+=a):this.value.push.apply(this.value,f)};q.prototype.toString=function(){return q.FAST?this.value:this.value.join("")};p.prototype.toString=function(){return H(this.value)};p.prototype.write=function(a){(a||m).write(""+this)};var T={allowfullscreen:"allowFullscreen",accesskey:"accessKey",bgcolor:"bgColor",cellpadding:"cellPadding",
cellspacing:"cellSpacing",checked:"defaultChecked","class":"className",colspan:"colSpan",contenteditable:"contentEditable",defaultchecked:"defaultChecked",defaultselected:"defaultSelected",defaultmuted:"defaultMuted","for":"htmlFor",formnovalidate:"formNoValidate",hidefocus:"hideFocus",ismap:"isMap",itemscope:"itemScope",maxlength:"maxLength",muted:"defaultMuted",nohref:"noHref",noresize:"noResize",noshade:"noShade",novalidate:"noValidate",nowrap:"noWrap",pauseonexit:"pauseOnExit",readonly:"readOnly",
rowspan:"rowSpan",selected:"defaultSelected",spellcheck:"spellCheck",tabindex:"tabIndex",truespeed:"trueSpeed",typemustmatch:"typeMustMatch",usemap:"useMap",willvalidate:"willValidate"},u={enctype:"encoding",onscroll:"DOMMouseScroll"},U={autocapitalize:1,autocomplete:1,autocorrect:1,type:1},R=/^[\r\n]+/,S=/[\r\n]+$/;p.prototype.toDOM=function(a,b){4===k(a)&&(a=m.getElementById(a)||("querySelector"in m?m.querySelector(a):null));var d;try{b&&(d=a,a=null),d=N(d||t(this.value[0]),this.value)}catch(f){var c=
v(f);if(c instanceof p)return c.toDOM(a||d);d=m.createTextNode(""+c)}a&&a.parentNode&&a.parentNode.replaceChild(d,a);return d};p.prototype.reload=function(){var a=m;try{var b=this.toDOM();a.replaceChild(b,a.documentElement);if(a.createStyleSheet){for(var d=b.firstChild;d&&"HEAD"!==(d.tagName||"");)d=d.nextSibling;for(var f=d&&d.firstChild;f;){if("LINK"===(f.tagName||""))f.href=f.href;f=f.nextSibling}}}catch(c){a=a.open("text/html"),a.write(""+this),a.close()}};return x}(document,window.ScriptEngineMajorVersion);var todos=todos||{};todos.views=todos.views||{};
todos.views.Stats=duel([""," ",["$if",{test:function(a){return a.total}},["footer",{"class":"footer"}," ",["span",{"class":"todo-count"},["strong",function(a){return a.active}]," ",function(a){return 1===a.active?"item":"items"}," left"]," ",["ul",{"class":"filters"}," ",["li"," ",["a",{href:"#/","class":function(a){return!a.filter?"selected":null}},"All"]," "]," ",["li"," ",["a",{href:"#/active","class":function(a){return"active"===a.filter?"selected":null}},"Active"]," "]," ",["li"," ",["a",{href:"#/completed",
"class":function(a){return"completed"===a.filter?"selected":null}},"Completed"]," "]," "]," "," ",["$if",{test:function(a){return a.completed}},["button",{"class":"clear-completed",onclick:function(){return todos.actions.clearOnClick}},"Clear completed"]]]]]);var todos=todos||{};todos.views=todos.views||{};
todos.views.Task=duel([""," ",["li",{"class":function(a){return a.completed?"completed":""}}," ",["div",{"class":"view"}," ",["input",{"class":"toggle",type:"checkbox",checked:function(a){return a.completed},onchange:function(a){return todos.actions.completedOnChange(a.id)}}]," ",["label",{ondblclick:function(){return todos.actions.editOnDblclick}},function(a){return a.title}]," ",["button",{"class":"destroy",onclick:function(a){return todos.actions.removeOnClick(a.id)}}]," "]," ",["input",{"class":"edit",
type:"text",value:function(a){return a.title},onblur:function(a){return todos.actions.editOnBlur(a.id)},onkeydown:function(a){return todos.actions.editOnKeydown(a.id)}}]]]);var todos=todos||{};todos.views=todos.views||{};
todos.views.Tasks=duel([""," ",["$if",{test:function(a){return a.tasks&&a.tasks.length}},["section",{"class":"main"}," ",["input",{"class":"toggle-all",type:"checkbox",checked:function(a){return!a.stats.active},onchange:function(){return todos.actions.toggleOnChange}}]," ",["label",{"for":"toggle-all"},"Mark all as complete"]," ",["ul",{"class":"todo-list"}," ",["$for",{each:function(a){return a.tasks}}," ",["$call",{view:function(){return todos.views.Task}}]]," "]]]]);var todos=todos||{};todos.views=todos.views||{};todos.views.TodoApp=duel(["section",{"class":"todoapp"}," ",["header",{"class":"header"}," ",["h1","todos"]," ",["input",{"class":"new-todo",placeholder:"What needs to be done?",autofocus:!0,onblur:function(){return todos.actions.add_blur},onkeydown:function(){return todos.actions.addOnKeydown}}]," "]," ",["$call",{view:function(){return todos.views.Tasks},data:function(a){return a}}]," ",["$call",{view:function(){return todos.views.Stats},data:function(a){return a.stats}}]]);var todos=todos||{};
(function(j,f,h){function g(a){return c.filter(function(b){return b.id===a})[0]}function d(){var a="undefined"!==typeof JSON?JSON.stringify(c):c;f.setItem(h,a)}var c,e;if(!(e=f)){var i={};e={getItem:function(a){return i[a]},setItem:function(a,b){i[a]=b}}}f=e;c=(e=f.getItem(h))?"undefined"!==typeof JSON?JSON.parse(e):e:[];j.model={viewData:function(a){var b;if("completed"===a||"active"===a){var d="completed"===a;b=c.filter(function(a){return a.completed===d})}else b=c;a={total:c.length,completed:c.filter(function(a){return a.completed}).length,
filter:a||""};a.active=a.total-a.completed;return{tasks:b,stats:a}},add:function(a){a={id:((new Date).getTime()+Math.random()).toString(36),title:a,completed:!1};c.push(a);d();return a},find:g,edit:function(a,b){(g(a)||{}).title=b;d()},toggle:function(a,b){(g(a)||{}).completed=b;d()},toggleAll:function(a){c.forEach(function(b){b.completed=a});d()},remove:function(a){for(var b=c.length-1;0<=b;b--)c[b].id===a&&c.splice(b,1);d()},expunge:function(){for(var a=c.length-1;0<=a;a--)c[a].completed&&c.splice(a,
1);d()}}})(todos,window.localStorage,"todos-duel");var todos=todos||{};
(function(b,e){function d(){var a=b.model.viewData(e.location.hash.substr(2)),a=b.views.TodoApp(a).toDOM(),c=e.querySelector(".todoapp");c?c.parentNode.replaceChild(a,c):e.body.insertBefore(a,e.body.firstChild);e.querySelector(".new-todo").focus()}addEventListener("hashchange",d,!1);b.actions={addOnKeydown:function(a){13===a.keyCode?(a=(this.value||"").trim(),this.value="",a&&(b.model.add(a),d())):27===a.keyCode&&d()},editOnBlur:function(a){return function(){var c=(this.value||"").trim();(this.value=
c)?b.model.edit(a,c):b.model.remove(a);d()}},editOnKeydown:function(a){return function(c){if(13===c.keyCode)this.blur();else if(27===c.keyCode){if(c=b.model.find(a))this.value=c.title;this.blur()}}},removeOnClick:function(a){return function(){b.model.remove(a);d()}},clearOnClick:function(){b.model.expunge();d()},editOnDblclick:function(){for(var a=this;"LI"!==a.tagName;)a=a.parentNode;a.className="editing";(a=(a||e).querySelector("input[type=text]"))&&a.focus()},completedOnChange:function(a){return function(){b.model.toggle(a,
this.checked);d()}},toggleOnChange:function(){b.model.toggleAll(this.checked);d()}};d()})(todos,window.document);
\ No newline at end of file
hr{margin:20px 0;border:0;border-top:1px dashed #c5c5c5;border-bottom:1px dashed #f7f7f7;}.learn a{font-weight:normal;text-decoration:none;color:#b83f45;}.learn a:hover{text-decoration:underline;color:#787e7e;}.learn h3,.learn h4,.learn h5{margin:10px 0;font-weight:500;line-height:1.2;color:#000;}.learn h3{font-size:24px;}.learn h4{font-size:18px;}.learn h5{margin-bottom:0;font-size:14px;}.learn ul{padding:0;margin:0 0 30px 25px;}.learn li{line-height:20px;}.learn p{font-size:15px;font-weight:300;line-height:1.3;margin-top:0;margin-bottom:0;}#issue-count{display:none;}.quote{border:none;margin:20px 0 60px 0;}.quote p{font-style:italic;}.quote p:before{content:'“';font-size:50px;opacity:0.15;position:absolute;top:-20px;left:3px;}.quote p:after{content:'”';font-size:50px;opacity:0.15;position:absolute;bottom:-42px;right:3px;}.quote footer{position:absolute;bottom:-40px;right:0;}.quote footer img{border-radius:3px;}.quote footer a{margin-left:5px;vertical-align:middle;}.speech-bubble{position:relative;padding:10px;background:rgba(0,0,0,0.04);border-radius:5px;}.speech-bubble:after{content:'';position:absolute;top:100%;right:30px;border:13px solid transparent;border-top-color:rgba(0,0,0,0.04);}.learn-bar>.learn{position:absolute;width:272px;top:8px;left:-300px;padding:10px;border-radius:5px;background-color:rgba(255,255,255,0.6);transition-property:left;transition-duration:500ms;}@media (min-width:899px){.learn-bar{width:auto;padding-left:300px;}.learn-bar>.learn{left:8px;}}html,body{margin:0;padding:0;}button{margin:0;padding:0;border:0;background:none;font-size:100%;vertical-align:baseline;font-family:inherit;font-weight:inherit;color:inherit;-webkit-appearance:none;appearance:none;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;font-smoothing:antialiased;}body{font:14px 'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4em;background:#f5f5f5;color:#4d4d4d;min-width:230px;max-width:550px;margin:0 auto;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;font-smoothing:antialiased;font-weight:300;}button,input[type="checkbox"]{outline:none;}.hidden{display:none;}.todoapp{background:#fff;margin:130px 0 40px 0;position:relative;box-shadow:0 2px 4px 0 rgba(0,0,0,0.2),0 25px 50px 0 rgba(0,0,0,0.1);}.todoapp input::-webkit-input-placeholder{font-style:italic;font-weight:300;color:#e6e6e6;}.todoapp input::-moz-placeholder{font-style:italic;font-weight:300;color:#e6e6e6;}.todoapp input::input-placeholder{font-style:italic;font-weight:300;color:#e6e6e6;}.todoapp h1{position:absolute;top:-155px;width:100%;font-size:100px;font-weight:100;text-align:center;color:rgba(175,47,47,0.15);-webkit-text-rendering:optimizeLegibility;-moz-text-rendering:optimizeLegibility;text-rendering:optimizeLegibility;}.new-todo,.edit{position:relative;margin:0;width:100%;font-size:24px;font-family:inherit;font-weight: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);box-sizing:border-box;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;font-smoothing:antialiased;}.new-todo{padding:16px 16px 16px 60px;border:none;background:rgba(0,0,0,0.003);box-shadow:inset 0 -2px 1px rgba(0,0,0,0.03);}.main{position:relative;z-index:2;border-top:1px solid #e6e6e6;}label[for='toggle-all']{display:none;}.toggle-all{position:absolute;top:-55px;left:-12px;width:60px;height:34px;text-align:center;border:none;}.toggle-all:before{content:'❯';font-size:22px;color:#e6e6e6;padding:10px 27px 10px 27px;}.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 solid #ededed;}.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;height:auto;position:absolute;top:0;bottom:0;margin:auto 0;border:none;-webkit-appearance:none;appearance:none;}.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>');}.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>');}.todo-list li label{white-space:pre;word-break:break-word;padding:15px 60px 15px 15px;margin-left:45px;display:block;line-height:1.2;transition:color 0.4s;}.todo-list li.completed label{color:#d9d9d9;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:30px;color:#cc9a9a;margin-bottom:11px;transition:color 0.2s ease-out;}.todo-list li .destroy:hover{color:#af5b5e;}.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:10px 15px;height:20px;text-align:center;border-top:1px solid #e6e6e6;}.footer:before{content:'';position:absolute;right:0;bottom:0;left:0;height:50px;overflow:hidden;box-shadow:0 1px 1px rgba(0,0,0,0.2),0 8px 0 -3px #f6f6f6,0 9px 1px -3px rgba(0,0,0,0.2),0 16px 0 -6px #f6f6f6,0 17px 2px -6px rgba(0,0,0,0.2);}.todo-count{float:left;text-align:left;}.todo-count strong{font-weight:300;}.filters{margin:0;padding:0;list-style:none;position:absolute;right:0;left:0;}.filters li{display:inline;}.filters li a{color:inherit;margin:3px;padding:3px 7px;text-decoration:none;border:1px solid transparent;border-radius:3px;}.filters li a.selected,.filters li a:hover{border-color:rgba(175,47,47,0.1);}.filters li a.selected{border-color:rgba(175,47,47,0.2);}.clear-completed,html .clear-completed:active{float:right;position:relative;line-height:20px;text-decoration:none;cursor:pointer;position:relative;}.clear-completed:hover{text-decoration:underline;}.info{margin:65px auto 0;color:#bfbfbf;font-size:10px;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-align:center;}.info p{line-height:1;}.info a{color:inherit;text-decoration:none;font-weight:400;}.info a:hover{text-decoration:underline;}@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{-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-appearance:none;appearance:none;}}@media (max-width:430px){.footer{height:50px;}.filters{bottom:10px;}}
\ No newline at end of file
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;-ms-appearance:none;-o-appearance:none;appearance:none;}body{font:14px 'Helvetica Neue',Helvetica,Arial,sans-serif;line-height:1.4em;background:#eaeaea url("0c975f0bb536597038b63a8cb0d85cff2222b0c9.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;}button,input[type="checkbox"]{outline:none;}#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: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);-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;}#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;height:auto;position:absolute;top:0;bottom:0;margin:auto 0;border:none;-webkit-appearance:none;-ms-appearance:none;-o-appearance:none;appearance:none;}#todo-list li .toggle:after{content:'✔';line-height:43px;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{white-space:pre;word-break:break-word;padding:15px 60px 15px 15px;margin-left:45px;display:block;line-height:1.2;-webkit-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;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);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;}@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;}hr{margin:20px 0;border:0;border-top:1px dashed #C5C5C5;border-bottom:1px dashed #F7F7F7;}.learn a{font-weight:normal;text-decoration:none;color:#b83f45;}.learn a:hover{text-decoration:underline;color:#787e7e;}.learn h3,.learn h4,.learn h5{margin:10px 0;font-weight:500;line-height:1.2;color:#000;}.learn h3{font-size:24px;}.learn h4{font-size:18px;}.learn h5{margin-bottom:0;font-size:14px;}.learn ul{padding:0;margin:0 0 30px 25px;}.learn li{line-height:20px;}.learn p{font-size:15px;font-weight:300;line-height:1.3;margin-top:0;margin-bottom:0;}.quote{border:none;margin:20px 0 60px 0;}.quote p{font-style:italic;}.quote p:before{content:'“';font-size:50px;opacity:0.15;position:absolute;top:-20px;left:3px;}.quote p:after{content:'”';font-size:50px;opacity:0.15;position:absolute;bottom:-42px;right:3px;}.quote footer{position:absolute;bottom:-40px;right:0;}.quote footer img{border-radius:3px;}.quote footer a{margin-left:5px;vertical-align:middle;}.speech-bubble{position:relative;padding:10px;background:rgba(0,0,0,0.04);border-radius:5px;}.speech-bubble:after{content:'';position:absolute;top:100%;right:30px;border:13px solid transparent;border-top-color:rgba(0,0,0,0.04);}.learn-bar>.learn{position:absolute;width:272px;top:8px;left:-300px;padding:10px;border-radius:5px;background-color:rgba(255,255,255,0.6);-webkit-transition-property:left;transition-property:left;-webkit-transition-duration:500ms;transition-duration:500ms;}@media (min-width:899px){.learn-bar{width:auto;margin:0 0 0 300px;}.learn-bar>.learn{left:8px;}.learn-bar #todoapp{width:550px;margin:130px auto 40px auto;}}
\ No newline at end of file
(function(){function i(){var a=location.href.indexOf("examples/");return location.href.substr(0,a)}function f(a,b){if(!(this instanceof f))return new f(a,b);var c,d;if("object"!==typeof a)try{a=JSON.parse(a)}catch(k){return}if(b)c=b.template,d=b.framework;if(!c&&a.templates)c=a.templates.todomvc;if(!d&&document.querySelector("[data-framework]"))d=document.querySelector("[data-framework]").dataset.framework;this.template=c;if(a.backend)this.frameworkJSON=a.backend,this.append({backend:!0});else if(a[d])this.frameworkJSON=
a[d],this.append()}var m=function(a){a.defaults=function(a){if(!a)return a;for(var b=1,c=arguments.length;b<c;b++){var d=arguments[b];if(d)for(var j in d)null==a[j]&&(a[j]=d[j])}return a};a.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var b=/(.)^/,c={"'":"'","\\":"\\","\r":"r","\n":"n","\t":"t","\u2028":"u2028","\u2029":"u2029"},d=/\\|'|\r|\n|\t|\u2028|\u2029/g;a.template=function(k,h,g){var f,g=a.defaults({},g,a.templateSettings),j=RegExp([(g.escape||
b).source,(g.interpolate||b).source,(g.evaluate||b).source].join("|")+"|$","g"),i=0,e="__p+='";k.replace(j,function(a,b,h,f,g){e+=k.slice(i,g).replace(d,function(a){return"\\"+c[a]});b&&(e+="'+\n((__t=("+b+"))==null?'':_.escape(__t))+\n'");h&&(e+="'+\n((__t=("+h+"))==null?'':__t)+\n'");f&&(e+="';\n"+f+"\n__p+='");i=g+a.length;return a});e+="';\n";g.variable||(e="with(obj||{}){\n"+e+"}\n");e="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+e+"return __p;\n";
try{f=new Function(g.variable||"obj","_",e)}catch(l){throw l.source=e,l;}if(h)return f(h,a);h=function(b){return f.call(this,b,a)};h.source="function("+(g.variable||"obj")+"){\n"+e+"}";return h};return a}({});if("todomvc.com"===location.hostname)window._gaq=[["_setAccount","UA-31081062-1"],["_trackPageview"]],function(a,b){var c=a.createElement(b),d=a.getElementsByTagName(b)[0];c.src="//www.google-analytics.com/ga.js";d.parentNode.insertBefore(c,d)}(document,"script");f.prototype.append=function(a){var b=
document.createElement("aside");b.innerHTML=m.template(this.template,this.frameworkJSON);b.className="learn";if(a&&a.backend){var a=b.querySelector(".source-links"),c=a.firstElementChild,d=a.lastElementChild,f=d.getAttribute("href");d.setAttribute("href",f.substr(f.lastIndexOf("http")));a.innerHTML=c.outerHTML+d.outerHTML}else a=b.querySelectorAll(".demo-link"),Array.prototype.forEach.call(a,function(a){"http"!==a.getAttribute("href").substr(0,4)&&a.setAttribute("href",i()+a.getAttribute("href"))});
document.body.className=(document.body.className+" learn-bar").trim();document.body.insertAdjacentHTML("afterBegin",b.outerHTML)};if("tastejs.github.io"===location.hostname)location.href=location.href.replace("tastejs.github.io/todomvc","todomvc.com");(function(a,b){if(!location.host)return console.info("Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.");var c=new XMLHttpRequest;c.open("GET",i()+a,!0);c.send();c.onload=function(){200===c.status&&b&&b(c.responseText)}})("learn.json",
f)})();/*
DUEL v0.8.2 http://duelengine.org
Copyright (c)2006-2012 Stephen M. McKamey.
Licensed under The MIT License.
*/
var duel=function(m,u,w){function x(a){this.value=a}function l(a){switch(typeof a){case "object":return!a?0:y(a)?2:a instanceof x?5:a instanceof Date?4:3;case "function":return 1;case "undefined":return 0;default:return 4}}function n(a){return"function"===typeof a}function o(){this.value=o.FAST?"":[]}function q(a){y(a)||(a=["",a]);this.value=a}function r(a,b){switch(l(b)){case 2:if(""===b[0])for(var c=1,f=b.length;c<f;c++)r(a,b[c]);else a.push(b);break;case 3:if(1===a.length)a.push(b);else if(c=a[1],
3===l(c))for(f in b)b.hasOwnProperty(f)&&(c[f]=b[f]);else a.splice(1,0,b);break;case 4:""!==b&&(b=""+b,c=a.length-1,0<c&&4===l(a[c])?a[c]+=b:a.push(b));break;case 0:break;default:a.push(b)}}function s(a,b,c,f,d,e){var g=3===l(a[1]);if(a.length===(g?3:2))return k(a[a.length-1],b,c,f,d,e);for(var h=[""],g=g?2:1,j=a.length;g<j;g++)r(h,k(a[g],b,c,f,d,e));return h}function A(a,b,c,f,d,e){for(var g=1,h=a.length;g<h;g++){var j=a[g],i=j[1].test;if(3===l(j[1])&&i&&(n(i)&&(i=i(b,c,f,d)),!i))continue;return s(j,
b,c,f,d,e)}return null}function J(a){2!==l(a)&&(a=["",a]);var b=function(b,f,d,e){try{var g=k(a,b,isFinite(f)?f:0,isFinite(d)?d:1,"string"===typeof e?e:null);return new q(g)}catch(h){return new q("["+h+"]")}};b.getView=function(){return a};return b}function K(a){return"string"!==typeof a?a:a.replace(/[&<>]/g,function(a){switch(a){case "&":return"&amp;";case "<":return"&lt;";case ">":return"&gt;";default:return a}})}function L(a){return"string"!==typeof a?a:a.replace(/[&<>"]/g,function(a){switch(a){case "&":return"&amp;";
case "<":return"&lt;";case ">":return"&gt;";case '"':return"&quot;";default:return a}})}function B(a,b){var c=b[0]||"",f=b.length,d=1,e,g=M[c];if("!"===c.charAt(0))"!DOCTYPE"===b[0]?a.append("<!DOCTYPE ",b[1],">"):a.append("<\!--",b[1],"--\>");else{if(c){a.append("<",c);e=b[d];if(3===l(e)){for(var h in e)if(e.hasOwnProperty(h)){var j=e[h];if(C[h])if(j)j=h;else continue;a.append(" ",h);0!==l(j)&&a.append('="',L(j),'"')}d++}g&&a.append(" /");a.append(">")}for(;d<f;d++)e=b[d],y(e)?B(a,e):a.append(K(e));
c&&!g&&a.append("</",c,">")}}function t(a){if(a){if("!"===a.charAt(0))return m.createComment("!"===a?"":a.substr(1)+" ")}else{if(m.createDocumentFragment)return m.createDocumentFragment();a=""}return"style"===a.toLowerCase()&&m.createStyleSheet?m.createStyleSheet():m.createElement(a)}function v(a,b){if(b){var c=(a.tagName||"").toLowerCase();if(8===a.nodeType)3===b.nodeType&&(a.nodeValue+=b.nodeValue);else if("table"===c&&a.tBodies)if(b.tagName)if((c=b.tagName.toLowerCase())&&"tbody"!==c&&"thead"!==
c){var f=0<a.tBodies.length?a.tBodies[a.tBodies.length-1]:null;f||(f=t("th"===c?"thead":"tbody"),a.appendChild(f));f.appendChild(b)}else!1!==a.canHaveChildren&&a.appendChild(b);else{if(11===b.nodeType)for(;b.firstChild;)v(a,b.removeChild(b.firstChild))}else if("style"===c&&m.createStyleSheet)a.cssText=b;else if(!1!==a.canHaveChildren)a.appendChild(b);else if("object"===c&&b.tagName&&"param"===b.tagName.toLowerCase()){try{a.appendChild(b)}catch(d){}try{if(a.object)a.object[b.name]=b.value}catch(e){}}}}
function D(a,b,c){switch(typeof c){case "function":a.addEventListener?a.addEventListener("on"===b.substr(0,2)?b.substr(2):b,c,!1):a[b]=c;break;case "string":a[b]=new Function("event",c)}}function E(a){return!!a&&3===a.nodeType&&(!a.nodeValue||!/\S/.exec(a.nodeValue))}function F(a,b){if(a&&3===a.nodeType&&b.exec(a.nodeValue))a.nodeValue=a.nodeValue.replace(b,"")}function z(a){if(a){for(;E(a.firstChild);)a.removeChild(a.firstChild);for(F(a.firstChild,N);E(a.lastChild);)a.removeChild(a.lastChild);F(a.lastChild,
O)}}function G(a,b){var c=a[b];if(c){try{delete a[b]}catch(f){a[b]=w}if(!n(c))try{c=new Function(""+c)}catch(d){c=null}}return c}function H(a){if(a){var b=G(a,"$init");b&&b.call(a);(b=G(a,"$load"))?setTimeout(function(){b.call(a);b=a=null},0):b=a=null}}function I(a,b){for(var c=1,f=b.length;c<f;c++){var d=b[c];switch(l(d)){case 2:var e=d[0],d=I(t(e),d);if("html"===e)return z(d),H(d),d;v(a,d);break;case 4:""!==d&&v(a,m.createTextNode(""+d));break;case 3:if(1===a.nodeType){var e=a,g=d;if(g.name&&m.attachEvent&&
!e.parentNode)try{var h=t("<"+e.tagName+' name="'+g.name+'">');e.tagName===h.tagName&&(e=h)}catch(j){}d=void 0;for(d in g)if(g.hasOwnProperty(d)){var i=g[d],k=l(i);if(d)0===k&&(i="",k=4),d=P[d.toLowerCase()]||d,C[d]?(e[d]=!!i,p[d]&&(e[p[d]]=!!i)):"style"===d?"undefined"!==typeof e.style.cssText?e.style.cssText=i:e.style=i:"class"===d?e.className=i:"on"===d.substr(0,2)?(D(e,d,i),p[d]&&D(e,p[d],i)):4===k&&"$"!==d.charAt(0)?(e.setAttribute(d,i),p[d]&&e.setAttribute(p[d],i)):(e[d]=i,p[d]&&(e[p[d]]=i))}a=
e}break;case 5:e=v;g=a;i=d;d=t("div");d.innerHTML=""+i;z(d);if(1===d.childNodes.length)d=d.firstChild;else{for(i=t("");d.firstChild;)i.appendChild(d.firstChild);d=i}e(g,d)}}z(a);H(a);if(11===a.nodeType&&1===a.childNodes.length)a=a.firstChild;return a}x.prototype.toString=function(){return this.value};var y=Array.isArray||function(a){return a instanceof Array};o.FAST=!(u&&9>u());o.prototype.append=function(a,b,c){o.FAST?null!==a&&(this.value+=a,null!==b&&b!==w&&(this.value+=b,null!==c&&c!==w&&(this.value+=
c))):this.value.push.apply(this.value,arguments)};o.prototype.clear=function(){this.value=o.FAST?"":[]};o.prototype.toString=function(){return o.FAST?this.value:this.value.join("")};var k;k=function(a,b,c,f,d,e){switch(l(a)){case 1:return a(b,c,f,d);case 2:var g=a[0]||"";switch(g){case "$for":a:{var h=a[1]||{},g=[""],j;if(h.hasOwnProperty("count")){j=h.count;n(j)&&(j=j(b,c,f,d));h.hasOwnProperty("data")?(h=h.data,n(h)&&(h=h(b,c,f,d))):h=b;for(b=0;b<j;b++)r(g,s(a,h,b,j,null,e))}else{if(h.hasOwnProperty("in")){var i=
h["in"];n(i)&&(i=i(b,c,f,d));if(3===l(i)){h=[];for(j in i)i.hasOwnProperty(j)&&h.push(j);for(b=0,j=h.length;b<j;b++)r(g,s(a,i[h[b]],b,j,h[b],e));a=g;break a}h=i}else h=h.each,n(h)&&(h=h(b,c,f,d));b=l(h);if(2===b)for(b=0,j=h.length;b<j;b++)r(g,s(a,h[b],b,j,null,e));else 0!==b&&(g=s(a,h,0,1,null,e))}a=g}return a;case "$xor":return A(a,b,c,f,d,e);case "$if":return A(["$xor",a],b,c,f,d,e);case "$call":e=a[1]||{};if(e.view){g=k(e.view,b,c,f,d);h=e.hasOwnProperty("data")?k(e.data,b,c,f,d):b;j=e.hasOwnProperty("index")?
k(e.index,b,c,f,d):c;i=e.hasOwnProperty("count")?k(e.count,b,c,f,d):f;b=e.hasOwnProperty("key")?k(e.key,b,c,f,d):d;c={};for(f=a.length-1;2<=f;f--)d=a[f],e=d[1]||{},e.hasOwnProperty("name")&&(c[e.name]=d);a=g&&n(g.getView)?k(g.getView(),h,j,i,b,c):null}else a=null;return a;case "$part":return g=(a[1]||{}).name||"",g=e&&e.hasOwnProperty(g)?e[g]:a,s(g,b,c,f,d)}g=[g];h=1;for(j=a.length;h<j;h++)r(g,k(a[h],b,c,f,d,e));return g;case 3:e={};for(g in a)a.hasOwnProperty(g)&&(e[g]=k(a[g],b,c,f,d));return e}return a};
u=function(a){return n(a)&&n(a.getView)?a:J(a)};u.raw=function(a){return new x(a)};var M={area:!0,base:!0,basefont:!0,br:!0,col:!0,frame:!0,hr:!0,img:!0,input:!0,isindex:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,wbr:!0},C={async:1,checked:1,defer:1,disabled:1,hidden:1,novalidate:1,formnovalidate:1};q.prototype.toString=function(){var a;var b=this.value;try{var c=new o;B(c,b);a=c.toString()}catch(f){a="["+f+"]"}return a};q.prototype.write=function(a){(a||m).write(""+this)};var P={rowspan:"rowSpan",
colspan:"colSpan",cellpadding:"cellPadding",cellspacing:"cellSpacing",tabindex:"tabIndex",accesskey:"accessKey",hidefocus:"hideFocus",usemap:"useMap",maxlength:"maxLength",readonly:"readOnly",contenteditable:"contentEditable"},p={enctype:"encoding",onscroll:"DOMMouseScroll",checked:"defaultChecked"},N=/^[\r\n]+/,O=/[\r\n]+$/;q.prototype.toDOM=function(a,b){4===l(a)&&(a=m.getElementById(a));var c;try{b&&(c=a,a=null),c=I(c||t(this.value[0]),this.value)}catch(f){c=m.createTextNode("["+f+"]")}a&&a.parentNode&&
a.parentNode.replaceChild(c,a);return c};q.prototype.reload=function(){var a=m;try{var b=this.toDOM();a.replaceChild(b,a.documentElement);if(a.createStyleSheet){for(var c=b.firstChild;c&&"HEAD"!==(c.tagName||"");)c=c.nextSibling;for(var f=c&&c.firstChild;f;){if("LINK"===(f.tagName||""))f.href=f.href;f=f.nextSibling}}}catch(d){a=a.open("text/html"),a.write(this.toString()),a.close()}};return u}(document,window.ScriptEngineMajorVersion);var todos=todos||{};
(function(h,e,g){function d(){var a="undefined"!==typeof JSON?JSON.stringify(b):b;e.setItem(g,a)}var b,e=e||function(){var a={};return{getItem:function(c){return a[c]},setItem:function(c,b){a[c]=b}}}(),f=e.getItem(g);b=f?"undefined"!==typeof JSON?JSON.parse(f):f:[];h.model={tasks:function(){return b},stats:function(){var a={};a.total=b.length;a.completed=b.filter(function(a){return a.completed}).length;a.active=a.total-a.completed;return a},add:function(a){a={id:((new Date).getTime()+Math.random()).toString(36),
title:a,completed:!1};b.push(a);d();return a},edit:function(a,c){b.filter(function(b){return b.id===a})[0].title=c;d()},toggle:function(a,c){b.filter(function(b){return b.id===a})[0].completed=c;d()},toggleAll:function(a){b.forEach(function(b){b.completed=a});d()},remove:function(a){b.forEach(function(c,d){c.id===a&&b.splice(d,1)});d()},expunge:function(){b.forEach(function(a,c){a.completed&&b.splice(c,1)});d()}}})(todos,window.localStorage,"todos-duel");var todos=todos||{};todos.views=todos.views||{};todos.views.Stats=duel([""," ",["$if",{test:function(a){return a.total}},["footer",{id:"footer"}," ",["span",{id:"todo-count"},["strong",function(a){return a.active}]," ",function(a){return 1===a.active?"item":"items"}," left"]," "," ",["$if",{test:function(a){return a.completed}},["button",{id:"clear-completed",onclick:function(){return todos.actions.clear_click}},"Clear completed"]]]]]);var todos=todos||{};todos.views=todos.views||{};
todos.views.Task=duel([""," ",["li",{"class":function(a){return a.completed?"completed":""}}," ",["div",{"class":"view"}," ",["input",{"class":"toggle",type:"checkbox",checked:function(a){return a.completed},onchange:function(a){return todos.actions.completed_change(a.id)}}]," ",["label",{ondblclick:function(a){return todos.actions.content_dblclick(a.id)}},function(a){return a.title}]," ",["button",{"class":"destroy",onclick:function(a){return todos.actions.remove_click(a.id)}}]," "]," ",["input",
{"class":"edit",type:"text",value:function(a){return a.title},onblur:function(a){return todos.actions.edit_blur(a.id)},onkeypress:function(a){return todos.actions.edit_keypress(a.id)}}]]]);var todos=todos||{};todos.views=todos.views||{};todos.views.Tasks=duel([""," ",["$if",{test:function(a){return a.tasks.length}},["section",{id:"main"}," ",["input",{id:"toggle-all",type:"checkbox",checked:function(a){return!a.stats.active},onchange:function(){return todos.actions.toggle_change}}]," ",["label",{"for":"toggle-all"},"Mark all as complete"]," ",["ul",{id:"todo-list"}," ",["$for",{each:function(a){return a.tasks}}," ",["$call",{view:function(){return todos.views.Task}}]]," "]]]]);var todos=todos||{};todos.views=todos.views||{};todos.views.TodoApp=duel(["section",{id:"todoapp"}," ",["header",{id:"header"}," ",["h1","todos"]," ",["input",{id:"new-todo",placeholder:"What needs to be done?",autofocus:null,onblur:function(){return todos.actions.add_blur},onkeypress:function(){return todos.actions.add_keypress}}]," "]," ",["$call",{view:function(){return todos.views.Tasks},data:function(a){return a}}]," ",["$call",{view:function(){return todos.views.Stats},data:function(a){return a.stats}}]]);var todos=todos||{};
(function(c,e){function f(a){var a=a||c.model.stats(),a=c.views.Stats(a).toDOM(),b=e.getElementById(i);b?b.parentNode.replaceChild(a,b):e.getElementById(g).appendChild(a)}function d(){var a={tasks:c.model.tasks(),stats:c.model.stats()},b=c.views.Tasks(a).toDOM(),d=e.getElementById(j);d?d.parentNode.replaceChild(b,d):e.getElementById(g).appendChild(b);f(a.stats)}function h(a){var b=(a.value||"").trim();a.value="";b&&(a=c.model.add(b),(b=e.getElementById(k))?(b.appendChild(c.views.Task(a).toDOM()),f()):
d())}var i="footer",g="todoapp",j="main",k="todo-list";c.actions={addBlur:function(){h(this)},add_keypress:function(a){13===a.keyCode&&h(this)},edit_blur:function(a){return function(){var b=(this.value||"").trim();(this.value=b)?c.model.edit(a,b):c.model.remove(a);d()}},edit_keypress:function(){return function(a){13===a.keyCode&&this.blur()}},remove_click:function(a){return function(){c.model.remove(a);d()}},clear_click:function(){c.model.expunge();d()},content_dblclick:function(){var a=function(b){if("LI"!==
b.tagName)return a(b.parentNode);b.className="editing";b=b.getElementsByTagName("input")[1];b.focus();b.value=b.value};return function(){a(this)}},completed_change:function(a){return function(){c.model.toggle(a,this.checked);d()}},toggle_change:function(){c.model.toggleAll(this.checked);d()}};(function(a){var b=c.views.TodoApp({tasks:c.model.tasks(),stats:c.model.stats()}).toDOM();a.insertBefore(b,a.firstChild)})(e.body)})(todos,window.document);
\ No newline at end of file
<!DOCTYPE html> <!doctype html>
<html lang="en" data-framework="duel"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8">
<title>DUEL &#x2022; TodoMVC</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./cdn/bada2673af7edc043a573c3e20ac2d8ce9d10037.css" /> <title>DUEL &bull; TodoMVC</title>
<link rel="stylesheet" href="./cdn/b12c1274056c76efb21a375280fdd622eb22b845.css">
</head> </head>
<body> <body>
<footer id="info"> <footer class="info">
<p>Double-click to edit a todo</p> <p>Double-click to edit a todo</p>
<p>Ported to <a href="http://duelengine.org">DUEL</a> by <a href="http://mck.me">Stephen McKamey</a></p> <p>Ported to <a href="http://duelengine.org">DUEL</a> by <a href="http://mck.me">Stephen McKamey</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer> </footer>
<script src="./cdn/d10574a720ccdf2d2c810f0c45afb5f159264a1f.js"></script> <script src="./cdn/3aba0c24fc2dfb2ef530691bf611672891b75c6d.js"></script>
</body> </body>
</html> </html>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment