Commit 7b621ff1 authored by okappes's avatar okappes

Aria Templates implementation of the TodoMVC app.

parent 14489548
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eaeaea url('../../../../assets/bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp input:-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#todoapp h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#header {
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
}
#main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
#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;
}
#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;
}
.hidden{
display:none;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Aria Templates • TodoMVC</title>
<script src="js/lib/aria/ariatemplates-1.3.5.js"></script>
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<section id="todoapp"></section>
<script>
// Update the path to the Todo module here when necessary
aria.core.DownloadMgr.updateRootMap({
js: {
'*': '/labs/architecture-examples/ariatemplates/'
}
});
// Here be todos
Aria.loadTemplate({
div: 'todoapp',
classpath: 'js.view.Todo',
moduleCtrl: {
classpath: 'js.TodoCtrl'
}
});
</script>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="http://ariatemplates.com">Olaf Kappes</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="../../../assets/base.js"></script>
</body>
</html>
/* global Aria:true */
'use strict';
Aria.interfaceDefinition({
$classpath: 'js.ITodoCtrl',
$extends: 'aria.templates.IModuleCtrl',
$interface: {
saveTasks: function () {},
addTask: function (description) {},
deleteTask: function (idx) {}
}
});
/* global aria:true, Aria:true */
'use strict';
Aria.classDefinition({
$classpath: 'js.TodoCtrl',
$extends: 'aria.templates.ModuleCtrl',
$implements: ['js.ITodoCtrl'],
$dependencies: ['aria.storage.LocalStorage'],
$statics: {
STORAGE_NAME: 'todos-ariatemplates'
},
$constructor: function (storagename) {
var tasklist;
this.$ModuleCtrl.constructor.call(this);
this._storage = new aria.storage.LocalStorage();
this.__storagename = storagename || this.STORAGE_NAME;
tasklist = this._storage.getItem(this.__storagename);
this.setData({
todolist: (tasklist ? tasklist : [])
});
},
$prototype: {
$publicInterfaceName: 'js.ITodoCtrl',
saveTasks: function () {
this._storage.setItem(this.__storagename, this._data.todolist);
},
addTask: function (description) {
this.json.add(this._data.todolist, {title: description, completed: false});
},
deleteTask: function (idx) {
this.json.removeAt(this._data.todolist, idx);
}
}
});
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{Template {
$classpath: 'js.view.Todo',
$hasScript: true,
$css: ['js.view.TodoStyle'],
$wlibs: {
html: 'aria.html.HtmlLibrary'
}
}}
{macro main()}
<header id="header">
<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" {on keydown {fn: "newTaskOnEnter", scope: this}/}>
</header>
{section {
macro: "mainDisplay",
type: "div",
bindRefreshTo: [{to: "emptylist", inside: data}]
}/}
{/macro}
{macro mainDisplay()}
{if !data.emptylist}
<section id="main">
{@html:CheckBox {
attributes: {
classList: ["toggle-all"]
},
bind: {
checked: {
to: "toggleall",
inside: data,
transform: {
fromWidget: toggleAll
}
}
}
}/}
<label id="label-toggle-all">Mark all as complete</label>
{repeater {
id: "tasklist",
content: data.todolist,
type: "ul",
attributes: {
classList: (data.route.length > 0 ? ["todo-list", "filter-" + data.route] : ["todo-list"])
},
childSections: {
id: "task",
type: "li",
macro: "taskDisplay",
bindRefreshTo: function (e) { return [{to: "title", inside: e.item}] },
attributes: function (e) {
return { classList: e.item.completed ? ["completed"] : [] }
}
}
}/}
</section>
<footer id="footer">
{section {
type: "span",
attributes: {
classList: ["todo-count"]
},
macro: "itemsleft",
bindRefreshTo: [{to: "itemsleft", inside: data}]
}/}
{section {
attributes: {
classList: ["filters"]
},
macro: "routing",
type: "ul",
bindRefreshTo: [{to: "route", inside: data}]
}/}
{section {
type: "span",
macro: "itemsclear",
bindRefreshTo: [{to: "itemscompleted", inside: data}]
}/}
</footer>
{/if}
{/macro}
{macro routing()}
<li>
<a {if data.route.length==0}class="selected"{/if} href="#/">All</a>
</li>
<li>
<a {if data.route == "active"}class="selected"{/if} href="#/active">Active</a>
</li>
<li>
<a {if data.route == "completed"}class="selected"{/if} href="#/completed">Completed</a>
</li>
{/macro}
{macro itemsleft()}
<strong>${data.itemsleft}</strong> ${data.itemsleft == 1 ? "item" : "items"} left
{/macro}
{macro itemsclear()}
{if data.itemscompleted > 0}
<button id="clear-completed" {on click "clearCompleted"/}>Clear completed (${data.itemscompleted})</button>
{/if}
{/macro}
{macro taskDisplay(iter)}
{if data.editedTask == iter.sectionId}
<input class="edit" value="${iter.item.title|escapeForHTML}"
{id "editbox"/}
{on blur {fn: "stopEdit", scope: this, args: iter}/}
{on keydown {fn: "confirmEdit", scope: this, args: iter}/}>
{else/}
<div class="view">
{@html:CheckBox {
attributes: {
classList: ["toggle"]
},
bind: {
checked: {
to: "completed",
inside: iter.item,
transform: function (v) { return changeTaskStyle(v, iter.sectionId) }
}
}
}/}
<label {on dblclick {fn: "editTask", scope: this, args: iter}/}>${iter.item.title|escapeForHTML}</label>
<button class="destroy" {on click {fn: "deleteTask", scope: this, args:iter}/}></button>
</div>
{/if}
{/macro}
{/Template}
/* global aria:true, Aria:true */
'use strict';
Aria.tplScriptDefinition({
$classpath: 'js.view.TodoScript',
$dependencies: ['aria.utils.HashManager'],
$prototype: {
$dataReady: function () {
this.getRoute();
this.data.editedTask = null;
this.pauselistener = false;
this.todolistUpdateHandler();
this.$json.addListener(this.data, 'todolist', {fn: this.todolistUpdateHandler, scope: this}, false, true);
aria.utils.HashManager.addCallback({fn: 'routeManager', scope: this});
},
$viewReady: function () {
document.getElementById('new-todo').focus();
},
getRoute: function () {
var route = aria.utils.HashManager.getHashString();
this.$json.setValue(this.data, 'route', route[0] === '/' ? route.substr(1) : route);
},
routeManager: function () {
var el = this.$getElementById('tasklist');
this.getRoute();
el.classList.setClassName('todo-list' + (this.data.route.length > 0 ? ' filter-' + this.data.route : ''));
},
changeTaskStyle: function (val, where) {
var el = this.$getElementById(where);
if (el) { el.classList.setClassName(val ? 'completed' : ''); }
return val;
},
newTaskOnEnter: function (evt) {
var val;
if (evt.keyCode === evt.KC_ENTER) {
val = aria.utils.String.trim(evt.target.getValue());
if (val.length > 0) {
this.moduleCtrl.addTask(val);
evt.target.setValue('');
}
}
},
deleteTask: function (evt, e) {
this.moduleCtrl.deleteTask(e.index);
},
toggleAll: function (val) {
var i;
this.pauselistener = true;
for (i = 0; i < this.data.todolist.length; i++) {
this.$json.setValue(this.data.todolist[i], 'completed', val);
}
this.pauselistener = false;
this.todolistUpdateHandler();
return val;
},
clearCompleted: function () {
var i;
aria.templates.RefreshManager.stop();
this.pauselistener = true;
for (i = this.data.todolist.length - 1; i >= 0; i--) {
if (this.data.todolist[i].completed) { this.deleteTask(null, {index: i}); }
}
this.pauselistener = false;
this.todolistUpdateHandler();
aria.templates.RefreshManager.resume();
},
editTask: function (evt, e) {
var el = null;
this.data.editedTask = e.sectionId;
el = this.$getElementById(e.sectionId);
if (el) { el.classList.add('editing'); }
this.$refresh({outputSection: e.sectionId});
this.$focus('editbox');
},
confirmEdit: function (evt, e) {
if (evt.keyCode === evt.KC_ENTER) { this.stopEdit(evt, e); }
},
stopEdit: function (evt, e) {
var el, val;
this.data.editedTask = null;
el = this.$getElementById(e.sectionId);
if (el) { el.classList.remove('editing'); }
val = aria.utils.String.trim(evt.target.getValue());
if (val.length > 0) {
if (val === e.item.title) {
this.$refresh({outputSection: e.sectionId});
}
else {
this.$json.setValue(e.item, 'title', val);
}
}
else {
this.deleteTask(evt, e);
}
},
todolistUpdateHandler: function () {
var size;
if (this.pauselistener) { return; }
aria.templates.RefreshManager.stop();
size = this.data.todolist.length;
this.$json.setValue(this.data, 'emptylist', size === 0);
this.$json.setValue(this.data, 'itemsleft', this.data.todolist.filter(function (e) { return !(e.completed); }).length);
this.$json.setValue(this.data, 'itemscompleted', size - this.data.itemsleft);
this.$json.setValue(this.data, 'toggleall', size === this.data.itemscompleted);
aria.templates.RefreshManager.resume();
this.moduleCtrl.saveTasks();
}
}
});
{CSSTemplate {
$classpath: 'js.view.TodoStyle',
$dependencies: ['aria.core.Browser']
}}
{macro main ()}
.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.completed label {
color: #a9a9a9;
text-decoration: line-through;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
.todo-list li .toggle:after {
content: '✔';
line-height: 43px; /* 40 + a couple of pixels visual adjustment */
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
}
.todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
.todo-list li label {
word-break: break-word;
padding: 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
-webkit-transition: color 0.4s;
-moz-transition: color 0.4s;
-ms-transition: color 0.4s;
-o-transition: color 0.4s;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #a9a9a9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
-ms-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
.todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
-ms-transform: scale(1.3);
-o-transform: scale(1.3);
transform: scale(1.3);
}
.todo-list li .destroy:after {
content: '✖';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-count {
float: left;
text-align: left;
}
#label-toggle-all {
display: none;
}
.toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
border: none; /* Mobile Safari */
}
.toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
.toggle-all:checked:before {
color: #737373;
}
.todo-list 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.editing:last-child {
margin-bottom: -1px;
}
.filter-active li.completed {
display: none
}
.filter-completed li {
display: none
}
.filter-completed li.completed {
display: block
}
.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;
}
/*
This replaces the original hack to remove background from Mobile Safari.
*/
{if aria.core.Browser.isWebkit}
.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;
}
{/if}
{/macro}
{/CSSTemplate}
\ 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