Commit e4786809 authored by Stephen McKamey's avatar Stephen McKamey Committed by Sindre Sorhus

Closes #136: DUEL app

parent 8990986b
TodoMVC implemented in DUEL
===========================
Prerequisites
-------------
This example requires [Apache Maven 3](http://maven.apache.org/download.html) to build.
About DUEL
----------
[DUEL](http://duelengine.org) is a duel-sided template engine. Views written as markup get precompiled into both
JavaScript (client-side templates) and Java (server-side templates).
The client-side templates are executed as functions directly from JavaScript. The result
can be rendered as either text markup or as DOM objects. This example generates DOM objects for views.
This particular example only uses the server-side templates for debugging. They have been generated into
the `target/generated-sources/duel/` directory.
How to build
------------
Run a standard Maven build command in the directory that contains the `pom.xml`:
mvn clean package
Maven will download any dependencies, clean out any previously built files, and generate a new static app in the `www/` directory.
How to run debug version
------------------------
To run a debug-able version using Tomcat 7 as the web server, use this Maven command:
mvn tomcat7:run
Then navigate your browser to: http://127.0.0.1:8080/
<?xml version="1.0" encoding="UTF-8"?>
<project
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>
<artifactId>todomvc</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
<name>TodoMVC</name>
<description>TodoMVC example written in DUEL</description>
<properties>
<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>
<duel.clientPrefix>todos.views</duel.clientPrefix>
<duel.serverPrefix>com.example.todos.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>
<staticapps.config>${project.basedir}/staticapp.json</staticapps.config>
</properties>
<dependencies>
<!-- DUEL runtime -->
<dependency>
<groupId>org.duelengine</groupId>
<artifactId>duel-runtime</artifactId>
<version>${duel.version}</version>
</dependency>
<!-- DUEL staticapps -->
<dependency>
<groupId>org.duelengine</groupId>
<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>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>${resourcesDir}</directory>
<excludes>
<!-- exclude DUEL sources from target output -->
<exclude>**/*.duel</exclude>
</excludes>
</resource>
</resources>
<plugins>
<!-- Tomcat 7 config -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.0-beta-1</version>
<configuration>
<path>/</path>
<port>8080</port>
<warSourceDirectory>${project.build.directory}/${project.build.finalName}/</warSourceDirectory>
</configuration>
</plugin>
<!-- DUEL Compiler -->
<plugin>
<groupId>org.duelengine</groupId>
<artifactId>duel-maven-plugin</artifactId>
<version>${duel.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<clientPrefix>${duel.clientPrefix}</clientPrefix>
<serverPrefix>${duel.serverPrefix}</serverPrefix>
<inputDir>${duel.sourceDir}</inputDir>
<outputClientPath>${duel.clientPath}</outputClientPath>
</configuration>
</execution>
</executions>
</plugin>
<!-- Merge Builder -->
<plugin>
<groupId>org.duelengine</groupId>
<artifactId>merge-maven-plugin</artifactId>
<version>${merge.version}</version>
<executions>
<execution>
<goals>
<goal>merge</goal>
</goals>
<configuration>
<cdnMapFile>${merge.cdnMapFile}</cdnMapFile>
<cdnRoot>${merge.cdnRoot}</cdnRoot>
<cdnFiles>${merge.cdnFiles}</cdnFiles>
</configuration>
</execution>
</executions>
</plugin>
<!-- Static App Builder -->
<plugin>
<groupId>org.duelengine</groupId>
<artifactId>duel-staticapps-maven-plugin</artifactId>
<version>${staticapps.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<configPath>${staticapps.config}</configPath>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!-- Java compiler -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${javac.version}</source>
<target>${javac.version}</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
<view name="HomePage"><!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>DUEL &bull; TodoMVC</title>
<link rel="stylesheet" href="/css/styles.merge">
<!--[if lt IE 9]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
</head>
<body>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
<p>Ported to <a href="http://duelengine.org">DUEL</a> by <a href="http://mck.me">Stephen McKamey</a></p>
</footer>
<script src="/js/scripts.merge"></script>
</body>
</html>
<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>
<%-- TODO: implement routing
<ul id="filters">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
--%>
<button id="clear-completed"
if="<%= data.completed %>"
onclick="<%= todos.actions.clear_click %>">Clear completed (<%= data.completed %>)</button>
</footer>
<view name="Task">
<%-- could have embedded in 'tasks' for-loop, but this allows us to add single tasks --%>
<li class="<%= data.completed ? 'complete' : '' %>"
ondblclick="<%= todos.actions.content_dblclick(data.id) %>">
<div class="view">
<input class="toggle" type="checkbox" checked="<%= data.completed %>"
onchange="<%= todos.actions.completed_change(data.id) %>">
<label><%= data.title %></label>
<button class="destroy" onclick="<%= todos.actions.remove_click(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) %>">
</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 %>">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<for each="<%= data.tasks %>">
<call view="Task">
</for>
</ul>
</section>
<view name="TodoApp">
<section id="todoapp">
<header id="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 %>">
</header>
<call view="Tasks" data="data" />
<call view="Stats" data="data.stats" />
</section>
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>routing-servlet</servlet-name>
<servlet-class>org.duelengine.duel.staticapps.RoutingServlet</servlet-class>
<init-param>
<param-name>config-path</param-name>
<param-value>staticapp.json</param-value>
</init-param>
<init-param>
<param-name>dev-mode-override</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>routing-servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eeeeee url('bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
#todoapp 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;
}
#todoapp header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-radius: inherit;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp input:-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#new-todo,
.edit {
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
position: relative;
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: 12px;
text-align: center;
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
-webkit-transform: rotate(90deg);
/*-moz-transform: rotate(90deg);*/
-ms-transform: rotate(90deg);
/*-o-transform: rotate(90deg);*/
transform: rotate(90deg);
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
#toggle-all:checked:before {
color: #737373;
}
/* Need this ugly hack, since only
WebKit supports styling of inputs */
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all {
top: -52px;
left: -11px;
}
}
#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: 35px;
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
font-size: 18px;
content: '✔';
line-height: 40px;
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
}
#todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
#todo-list li label {
word-break: break-word;
margin: 20px 15px;
display: inline-block;
-webkit-transition: color 0.4s;
-moz-transition: color 0.4s;
-ms-transition: color 0.4s;
-o-transition: color 0.4s;
transition: color 0.4s;
}
#todo-list li.complete label {
color: #a9a9a9;
text-decoration: line-through;
}
#todo-list li .destroy {
display: none;
position: absolute;
top: 10px;
right: 10px;
width: 40px;
height: 40px;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
-ms-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
#todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
-ms-transform: scale(1.3);
-o-transform: scale(1.3);
transform: scale(1.3);
}
#todo-list li .destroy:after {
content: '✖';
}
#todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
z-index: 1;
text-align: center;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
left: 0;
height: 100px;
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 42px 0 -6px rgba(255, 255, 255, 0.8),
0 43px 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;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
font-size: 11px;
padding: 0 10px;
position: relative;
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;
}
\ No newline at end of file
# template styles
/css/base.css
# app overrides (not used)
#/css/app.css
\ No newline at end of file
This diff is collapsed.
# libraries
/js/lib/duel.js
# model
/js/todos/model.js
# views
/js/todos/views/Stats.js
/js/todos/views/Task.js
/js/todos/views/Tasks.js
/js/todos/views/TodoApp.js
# controller
/js/todos/controller.js
var todos = todos || {};
(function( todos, document ) {
/*-- private members -------------------------------*/
var ENTER_KEY = 13,
STATS_ID = 'footer',
TODOAPP_ID = 'todoapp',
TASKS_ID = 'main',
LIST_ID = 'todo-list',
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
var view = todos.views.Stats( data ).toDOM();
// replace old stats
var old = getById( STATS_ID );
if ( old ) {
old.parentNode.replaceChild( view, old );
} else {
getById( TODOAPP_ID ).appendChild( view );
}
}
function refreshAll() {
// get the data
var data = {
tasks: todos.model.tasks(),
stats: todos.model.stats()
};
// build the view
var view = todos.views.Tasks( data ).toDOM();
// replace old task list
var old = getById( TASKS_ID );
if ( old ) {
old.parentNode.replaceChild( view, old );
} else {
getById( TODOAPP_ID ).appendChild( view );
}
refreshStats( data.stats );
}
function add( input ) {
var title = (input.value || '').trim();
input.value = '';
if ( !title ) {
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();
}
}
function edit( input, id ) {
var title = (input.value || '').trim();
input.value = title;
if ( title ) {
todos.model.edit( id, title );
} else {
todos.model.remove( id );
}
refreshAll();
}
/*-- export public interface -------------------------------*/
// event handlers
todos.actions = {
add_blur: function( e ) {
add( this );
},
add_keypress: function( e ) {
if ( e.keyCode === ENTER_KEY ) {
add( this );
}
},
edit_blur: function( id ) {
// create a closure around the ID
return function( e ) {
edit( this, id );
};
},
edit_keypress: 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();
}
};
},
remove_click: function( id ) {
// create a closure around the ID
return function( e ) {
todos.model.remove( id );
refreshAll();
};
},
clear_click: function() {
todos.model.expunge();
refreshAll();
},
content_dblclick: function( id ) {
// create a closure around the ID
return function( e ) {
var li = this;
li.className = EDITING_CSS;
li.getElementsByTagName( 'input' )[1].focus();
};
},
completed_change: function( id ) {
// create a closure around the ID
return function( e ) {
var checkbox = this;
todos.model.toggle( id, checkbox.checked );
refreshAll();
};
},
toggle_change: function( e ) {
var checkbox = this;
todos.model.toggleAll( checkbox.checked );
refreshAll();
}
};
/*-- 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 );
})( todos, document );
var todos = todos || {};
(function( todos, localStorage, KEY ) {
/*-- private members -------------------------------*/
var tasks;
// model uses localStorage as the underlying data store
// this creates a poor man's localStorage polyfill
localStorage = localStorage || (function() {
var storage = {};
return {
getItem: function( key ) {
return storage[ key ];
},
setItem: function( key, value ) {
storage[ key ] = value;
}
};
})();
function create( title, completed ) {
return {
// fast, compact, non-repeating, unique ID: e.g., 'c2wwu0vz.pz4zpvi'
id: (new Date().getTime() + Math.random()).toString( 36 ),
title: title,
completed: !!completed
};
}
function save() {
// if doesn't support JSON then will be directly stored in polyfill
var value = typeof JSON !== 'undefined' ? JSON.stringify( tasks ) : tasks;
localStorage.setItem( KEY, value );
}
// initialize storage
var value = localStorage.getItem( KEY );
if ( value ) {
// if doesn't support JSON then will be directly stored in polyfill
tasks = typeof JSON !== 'undefined' ? JSON.parse( value ) : value;
} else {
tasks = [];
}
/*-- export public interface -------------------------------*/
todos.model = {
tasks: function() {
return tasks;
},
stats: function() {
var stats = {
total: tasks.length,
active: tasks.length,
completed: 0
};
var i = tasks.length;
while ( i-- ) {
if ( tasks[i].completed ) {
stats.completed++;
}
}
stats.active -= stats.completed;
return stats;
},
add: function( title ) {
var task = create( title, false );
tasks.push( task );
save();
return task;
},
edit: function( id, title ) {
var i = tasks.length;
while ( i-- ) {
if ( tasks[i].id === id ) {
tasks[i].title = title;
save();
return;
}
}
},
// toggle completion of task
toggle: function( id, completed ) {
var i = tasks.length;
while ( i-- ) {
if ( tasks[i].id === id ) {
tasks[i].completed = completed;
save();
return;
}
}
},
// toggle completion of all tasks
toggleAll: function( completed ) {
var i = tasks.length;
while ( i-- ) {
tasks[i].completed = completed;
}
save();
},
remove: function( id ) {
var i = tasks.length;
while ( i-- ) {
if ( tasks[i].id === id ) {
tasks.splice( i, 1 );
save();
return;
}
}
},
expunge: function() {
var i = tasks.length;
while ( i-- ) {
if ( tasks[i].completed ) {
tasks.splice( i, 1 );
}
}
save();
}
};
})( todos, window.localStorage, 'todos-duel' );
{
"targetDir": "www/",
"sourceDir": "target/todomvc/",
"serverPrefix" : "com.example.todos.views",
"cdnMap": "cdn",
"cdnLinksMap": "cdnLinks",
"cdnHost": "./",
"isDevMode": false,
"views": {
"index.html":
{
"view": "HomePage",
"data": {},
"extras": {}
}
},
"files": [
"robots.txt",
"favicon.ico"
]
}
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:#EEE 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;}#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 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;}#todoapp header:before{content:'';position:absolute;top:0;right:0;left:0;height:15px;z-index:2;border-bottom:1px solid #6c615c;background:#8d7d77;background:-webkit-gradient(linear,left top,left bottom,from(rgba(132,110,100,0.8)),to(rgba(101,84,76,0.8)));background:-webkit-linear-gradient(top,rgba(132,110,100,0.8),rgba(101,84,76,0.8));background:-moz-linear-gradient(top,rgba(132,110,100,0.8),rgba(101,84,76,0.8));background:-o-linear-gradient(top,rgba(132,110,100,0.8),rgba(101,84,76,0.8));background:-ms-linear-gradient(top,rgba(132,110,100,0.8),rgba(101,84,76,0.8));background:linear-gradient(top,rgba(132,110,100,0.8),rgba(101,84,76,0.8));filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83',EndColorStr='#847670');border-radius:inherit;}#todoapp input::-webkit-input-placeholder{font-style:italic;}#todoapp input:-moz-placeholder{font-style:italic;color:#a9a9a9;}#new-todo,.edit{margin:0;width:100%;font-size:24px;font-family:inherit;line-height:1.4em;border:0;outline:none;color:inherit;padding:6px;border:1px solid #999;box-shadow:inset 0 -1px 5px 0 rgba(0,0,0,0.2);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;-ms-font-smoothing:antialiased;-o-font-smoothing:antialiased;font-smoothing:antialiased;}#new-todo{padding:16px 16px 16px 60px;border:none;background:rgba(0,0,0,0.02);position:relative;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:12px;text-align:center;-webkit-appearance:none;-ms-appearance:none;-o-appearance:none;appearance:none;-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);}#toggle-all:before{content:'»';font-size:28px;color:#d9d9d9;padding:0 25px 7px;}#toggle-all:checked:before{color:#737373;}@media screen and (-webkit-min-device-pixel-ratio:0){#toggle-all{top:-52px;left:-11px;}}#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:35px;-webkit-appearance:none;-ms-appearance:none;-o-appearance:none;appearance:none;}#todo-list li .toggle:after{font-size:18px;content:'✔';line-height:40px;font-size:20px;color:#d9d9d9;text-shadow:0 -1px 0 #bfbfbf;}#todo-list li .toggle:checked:after{color:#85ada7;text-shadow:0 1px 0 #669991;bottom:1px;position:relative;}#todo-list li label{word-break:break-word;margin:20px 15px;display:inline-block;-webkit-transition:color 0.4s;-moz-transition:color 0.4s;-ms-transition:color 0.4s;-o-transition:color 0.4s;transition:color 0.4s;}#todo-list li.complete label{color:#a9a9a9;text-decoration:line-through;}#todo-list li .destroy{display:none;position:absolute;top:10px;right:10px;width:40px;height:40px;font-size:22px;color:#a88a8a;-webkit-transition:all 0.2s;-moz-transition:all 0.2s;-ms-transition:all 0.2s;-o-transition:all 0.2s;transition:all 0.2s;}#todo-list li .destroy:hover{text-shadow:0 0 1px #000,0 0 10px rgba(199,107,107,0.8);-webkit-transform:scale(1.3);-moz-transform:scale(1.3);-ms-transform:scale(1.3);-o-transform:scale(1.3);transform:scale(1.3);}#todo-list li .destroy:after{content:'✖';}#todo-list li:hover .destroy{display:block;}#todo-list li .edit{display:none;}#todo-list li.editing:last-child{margin-bottom:-1px;}#footer{color:#777;padding:0 15px;position:absolute;right:0;bottom:-31px;left:0;z-index:1;text-align:center;}#footer:before{content:'';position:absolute;right:0;bottom:31px;left:0;height:100px;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 42px 0 -6px rgba(255,255,255,0.8),0 43px 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;line-height:20px;text-decoration:none;background:rgba(0,0,0,0.1);font-size:11px;padding:0 10px;position:relative;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;}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>DUEL &bull; TodoMVC</title>
<link rel="stylesheet" href="./cdn/502117dcda6b1a71004e818c348c9de5fc80966a.css" />
<!--[if lt IE 9]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
</head>
<body>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
<p>Ported to <a href="http://duelengine.org">DUEL</a> by <a href="http://mck.me">Stephen McKamey</a></p>
</footer>
<script src="./cdn/1de7392f10ea2465d68b839144090d158ba7730f.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