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 @@
"excludeFiles": [
"**/node_modules/**",
"**/bower_components/**",
"examples/vanilladart/**/*.js"
"examples/vanilladart/**/*.js",
"examples/duel/www/**",
"examples/duel/src/main/webapp/js/lib/**"
],
"requireSpaceBeforeBlockStatements": true,
"requireParenthesesAroundIIFE": true,
......
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 @@
xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.todos</groupId>
<groupId>com.todomvc.duel</groupId>
<artifactId>todomvc</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
<packaging>war</packaging>
<name>TodoMVC</name>
......@@ -17,25 +18,32 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<resourcesDir>${project.basedir}/src/main/resources</resourcesDir>
<staticapps.version>0.8.5</staticapps.version>
<merge.version>0.5.2</merge.version>
<duel.version>0.8.2</duel.version>
<slf4j.version>1.6.4</slf4j.version>
<javac.version>1.6</javac.version>
<staticapps.version>0.9.11</staticapps.version>
<merge.version>0.6.0</merge.version>
<duel.version>0.9.7</duel.version>
<slf4j.version>1.7.12</slf4j.version>
<javac.version>1.7</javac.version>
<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.clientPath>/js/</duel.clientPath>
<merge.cdnMapFile>/cdn.properties</merge.cdnMapFile>
<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>
</properties>
<dependencies>
<!-- SLF4J runtime -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- DUEL runtime -->
<dependency>
<groupId>org.duelengine</groupId>
......@@ -49,13 +57,6 @@
<artifactId>duel-staticapps</artifactId>
<version>${staticapps.version}</version>
</dependency>
<!-- SLF4J runtime -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
<build>
......@@ -75,10 +76,11 @@
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.0-beta-1</version>
<version>2.2</version>
<configuration>
<path>/</path>
<port>8080</port>
<uriEncoding>UTF-8</uriEncoding>
<warSourceDirectory>${project.build.directory}/${project.build.finalName}/</warSourceDirectory>
</configuration>
</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 - 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.
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)._
*Let us [know](https://github.com/tastejs/todomvc/issues) if you discover anything worth sharing.*
## Implementation
......@@ -44,4 +40,8 @@ To run a debug-able version using Tomcat 7 as the web server, use this Maven com
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>
<html lang="en" data-framework="duel">
<html lang="en">
<head>
<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">
</head>
<body>
<footer id="info">
<footer class="info">
<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>Part of <a href="http://todomvc.com">TodoMVC</a></p>
......
<view name="Stats">
<%-- this footer should hidden by default and shown when there are todos --%>
<footer id="footer" if="<%= data.total %>">
<span id="todo-count"><strong><%= data.active %></strong> <%= (data.active === 1) ? 'item' : 'items' %> left</span>
<%-- This footer should hidden by default and shown when there are todos. --%>
<footer class="footer" if="<%= data.total %>">
<span class="todo-count"><strong><%= data.active %></strong> <%= (data.active === 1) ? 'item' : 'items' %> left</span>
<%-- TODO: implement routing
<ul id="filters">
<ul class="filters">
<li>
<a class="selected" href="#/">All</a>
<a href="#/" class="<%= !data.filter ? 'selected' : null %>">All</a>
</li>
<li>
<a href="#/active">Active</a>
<a href="#/active" class="<%= data.filter === 'active' ? 'selected' : null %>">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
<a href="#/completed" class="<%= data.filter === 'completed' ? 'selected' : null %>">Completed</a>
</li>
</ul>
--%>
<button id="clear-completed"
<%-- Hidden if no completed items are left ↓ --%>
<button class="clear-completed"
if="<%= data.completed %>"
onclick="<%= todos.actions.clear_click %>">Clear completed</button>
onclick="<%= todos.actions.clearOnClick %>">Clear completed</button>
</footer>
<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' : '' %>">
<div class="view">
<input class="toggle" type="checkbox" checked="<%= data.completed %>"
onchange="<%= todos.actions.completed_change(data.id) %>">
<label ondblclick="<%= todos.actions.content_dblclick(data.id) %>"><%= data.title %></label>
<button class="destroy" onclick="<%= todos.actions.remove_click(data.id) %>"></button>
onchange="<%= todos.actions.completedOnChange(data.id) %>">
<label ondblclick="<%= todos.actions.editOnDblclick %>"><%= data.title %></label>
<button class="destroy" onclick="<%= todos.actions.removeOnClick(data.id) %>"></button>
</div>
<input class="edit" type="text" value="<%= data.title %>"
onblur="<%= todos.actions.edit_blur(data.id) %>"
onkeypress="<%= todos.actions.edit_keypress(data.id) %>">
onblur="<%= todos.actions.editOnBlur(data.id) %>"
onkeydown="<%= todos.actions.editOnKeydown(data.id) %>">
</li>
<view name="Tasks">
<%-- this section should hidden by default and shown when there are todos --%>
<section id="main" if="<%= data.tasks.length %>">
<input id="toggle-all" type="checkbox" checked="<%= !data.stats.active %>"
onchange="<%= todos.actions.toggle_change %>">
<%-- This section should be hidden by default and shown when there are todos. --%>
<section class="main" if="<%= data.tasks && data.tasks.length %>">
<input class="toggle-all" type="checkbox" checked="<%= !data.stats.active %>"
onchange="<%= todos.actions.toggleOnChange %>">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<ul class="todo-list">
<for each="<%= data.tasks %>">
<call view="Task">
</for>
......
<view name="TodoApp">
<section id="todoapp">
<header id="header">
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input id="new-todo"
placeholder="What needs to be done?"
autofocus
onblur="<%= todos.actions.add_blur %>"
onkeypress="<%= todos.actions.add_keypress %>">
<input class="new-todo" placeholder="What needs to be done?" autofocus
onblur="<%= todos.actions.add_blur %>" onkeydown="<%= todos.actions.addOnKeydown %>">
</header>
<call view="Tasks" data="data" />
<call view="Stats" data="data.stats" />
</section>
......@@ -21,4 +21,17 @@
<servlet-name>routing-servlet</servlet-name>
<url-pattern>/</url-pattern>
</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>
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;
}
}
# todomvc base file
/bower_components/todomvc-common/base.css
# todomvc base files
/css/base.css
/css/index.css
/* global _ */
(function () {
'use strict';
/* jshint ignore:start */
// Underscore's Template Module
// Courtesy of underscorejs.org
var _ = (function (_) {
......@@ -112,8 +114,14 @@
})({});
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() {
if (location.hostname === 'tastejs.github.io') {
......@@ -175,13 +183,17 @@
if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend;
this.frameworkJSON.issueLabel = framework;
this.append({
backend: true
});
} else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.frameworkJSON.issueLabel = framework;
this.append();
}
this.fetchIssueCount();
}
Learn.prototype.append = function (opts) {
......@@ -212,6 +224,26 @@
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();
getFile('learn.json', Learn);
})();
# todomvc base file
/bower_components/todomvc-common/base.js
#/js/lib/todos.js
# libraries
/js/lib/duel.js
# model
/js/todos/model.js
# views
# compiled views
/js/todos/views/Stats.js
/js/todos/views/Task.js
/js/todos/views/Tasks.js
/js/todos/views/TodoApp.js
# model
/js/todos/model.js
# controller
/js/todos/controller.js
......@@ -8,52 +8,36 @@ var todos = todos || {};
/*-- private members -------------------------------*/
var ESC_KEY = 27;
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();
// Poor man's routing.
addEventListener('hashchange', refreshView, false);
// build the view
var view = todos.views.Stats(data).toDOM();
function curFilter() {
return document.location.hash.substr(2);
}
// replace old stats
var old = getById(STATS_ID);
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
var data = {
tasks: todos.model.tasks(),
stats: todos.model.stats()
};
var data = todos.model.viewData(curFilter());
// build the view
var view = todos.views.Tasks(data).toDOM();
var view = todos.views.TodoApp(data).toDOM();
// replace old task list
var old = getById(TASKS_ID);
var old = find('.todoapp');
if (old) {
// replace old task list
old.parentNode.replaceChild(view, old);
} else {
getById(TODOAPP_ID).appendChild(view);
// insert at top
document.body.insertBefore(view, document.body.firstChild);
}
refreshStats(data.stats);
find('.new-todo').focus();
}
function add(input) {
......@@ -64,16 +48,8 @@ var todos = todos || {};
return;
}
var task = todos.model.add(title);
var list = getById(LIST_ID);
if (list) {
// add new at the top
list.appendChild(todos.views.Task(task).toDOM());
refreshStats();
} else {
refreshAll();
}
todos.model.add(title);
refreshView();
}
function edit(input, id) {
......@@ -85,99 +61,91 @@ var todos = todos || {};
} else {
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 -------------------------------*/
// event handlers
todos.actions = {
addBlur: function () {
add(this);
},
add_keypress: function (e) {
addOnKeydown: function (e) {
if (e.keyCode === ENTER_KEY) {
add(this);
} else if (e.keyCode === ESC_KEY) {
refreshView();
}
},
edit_blur: function (id) {
editOnBlur: function (id) {
// create a closure around the ID
return function () {
edit(this, id);
};
},
edit_keypress: function () {
editOnKeydown: function (id) {
// create a closure around the ID
return function (e) {
if (e.keyCode === ENTER_KEY) {
// just blur so doesn't get triggered twice
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
return function () {
todos.model.remove(id);
refreshAll();
refreshView();
};
},
clear_click: function () {
clearOnClick: function () {
todos.model.expunge();
refreshAll();
refreshView();
},
content_dblclick: function () {
// create a closure around the ID
var toggleEditingMode = function (li) {
if (li.tagName !== 'LI') {
return toggleEditingMode(li.parentNode);
}
editOnDblclick: function () {
var self = this;
while (self.tagName !== 'LI') {
self = self.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.value = input.value;
};
return function () {
toggleEditingMode(this);
};
}
},
completed_change: function (id) {
completedOnChange: function (id) {
// create a closure around the ID
return function () {
var checkbox = this;
todos.model.toggle(id, checkbox.checked);
refreshAll();
todos.model.toggle(id, this.checked);
refreshView();
};
},
toggle_change: function () {
var checkbox = this;
todos.model.toggleAll(checkbox.checked);
refreshAll();
toggleOnChange: function () {
todos.model.toggleAll(this.checked);
refreshView();
}
};
/*-- init task list -------------------------------*/
(function (body) {
// 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);
refreshView();
})(todos, window.document);
......@@ -32,6 +32,12 @@ var todos = todos || {};
};
}
function find(id) {
return tasks.filter(function (task) {
return task.id === id;
})[0];
}
function save() {
// if doesn't support JSON then will be directly stored in polyfill
var value = typeof JSON !== 'undefined' ? JSON.stringify(tasks) : tasks;
......@@ -47,25 +53,40 @@ var todos = todos || {};
tasks = [];
}
/*-- export public interface -------------------------------*/
function filterTasks(filter) {
if (filter === 'completed' || filter === 'active') {
var completed = filter === 'completed';
return tasks.filter(function (task) {
return task.completed === completed;
});
}
return tasks;
}
todos.model = {
tasks: function () {
return tasks;
},
function calcStats(filter) {
var stats = {
total: tasks.length,
completed: tasks.filter(function (task) {
return task.completed;
}).length,
stats: function () {
var stats = {};
filter: filter || ''
};
stats.total = tasks.length;
stats.active = stats.total - stats.completed;
stats.completed = tasks.filter(function (task) {
return task.completed;
}).length;
return stats;
}
stats.active = stats.total - stats.completed;
/*-- export public interface -------------------------------*/
return stats;
todos.model = {
viewData: function (filter) {
return {
tasks: filterTasks(filter),
stats: calcStats(filter)
};
},
add: function (title) {
......@@ -77,19 +98,17 @@ var todos = todos || {};
return task;
},
find: find,
edit: function (id, title) {
tasks.filter(function (task) {
return task.id === id;
})[0].title = title;
(find(id) || {}).title = title;
save();
},
// toggle completion of task
toggle: function (id, completed) {
tasks.filter(function (task) {
return task.id === id;
})[0].completed = completed;
(find(id) || {}).completed = completed;
save();
},
......@@ -104,21 +123,23 @@ var todos = todos || {};
},
remove: function (id) {
tasks.forEach(function (task, index) {
if (task.id === id) {
tasks.splice(index, 1);
// traverse in reverse for removals
for (var i = tasks.length - 1; i >= 0; i--) {
if (tasks[i].id === id) {
tasks.splice(i, 1);
}
});
}
save();
},
expunge: function () {
tasks.forEach(function (task, index) {
if (task.completed) {
tasks.splice(index, 1);
// traverse in reverse for removals
for (var i = tasks.length - 1; i >= 0; i--) {
if (tasks[i].completed) {
tasks.splice(i, 1);
}
});
}
save();
}
......
{
"targetDir": "www/",
"sourceDir": "target/todomvc/",
"serverPrefix" : "com.example.todos.views",
"serverPrefix" : "com.todomvc.duel.views",
"cdnMap": "cdn",
"cdnLinksMap": "cdnLinks",
"cdnHost": "./",
......
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
<!DOCTYPE html>
<html lang="en" data-framework="duel">
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>DUEL &#x2022; TodoMVC</title>
<link rel="stylesheet" href="./cdn/bada2673af7edc043a573c3e20ac2d8ce9d10037.css" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DUEL &bull; TodoMVC</title>
<link rel="stylesheet" href="./cdn/b12c1274056c76efb21a375280fdd622eb22b845.css">
</head>
<body>
<footer id="info">
<footer class="info">
<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>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="./cdn/d10574a720ccdf2d2c810f0c45afb5f159264a1f.js"></script>
<script src="./cdn/3aba0c24fc2dfb2ef530691bf611672891b75c6d.js"></script>
</body>
</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