Commit 5b56b09a authored by Pascal Hartig's avatar Pascal Hartig

Merge branch 'pr/694' into gh-pages

Close #694
parents 526bb377 4550d2b7
......@@ -5,6 +5,7 @@
- New since 1.2:
- Durandal
- Exoskeleton
- Atma.js
## 1.2 - 2013-08-06
......
......@@ -145,6 +145,9 @@
<li class="routing labs">
<a href="labs/architecture-examples/exoskeleton/" data-source="http://exosjs.com/" data-content="A faster and leaner Backbone for your HTML5 apps.">Exoskeleton</a>
</li>
<li class="routing labs">
<a href="labs/architecture-examples/atmajs/" data-source="http://atmajs.com/" data-content="HMVC and the component-based architecture for building client, server or hybrid applications">Atma.js</a>
</li>
</ul>
<ul class="legend">
<li><b>*</b> <span class="label">R</span> = App also demonstrates routing</li>
......
{
"name": "todomvc-atmajs",
"version": "0.1.0",
"dependencies": {
"todomvc-common": "~0.1.4",
"jquery": "~2.0.2"
}
}
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('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;
}
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;
/* Mobile Safari */
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;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
/* Mobile Safari */
border: none;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: '✔';
/* 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 {
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);
-ms-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;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@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);
-ms-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: .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 {
width: 550px;
margin: 130px auto 40px auto;
}
}
(function () {
'use strict';
// Underscore's Template Module
// Courtesy of underscorejs.org
var _ = (function (_) {
_.defaults = function (object) {
if (!object) {
return object;
}
for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
var iterable = arguments[argsIndex];
if (iterable) {
for (var key in iterable) {
if (object[key] == null) {
object[key] = iterable[key];
}
}
}
}
return object;
}
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
return _;
})({});
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 redirect() {
if (location.hostname === 'tastejs.github.io') {
location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com');
}
}
function findRoot() {
var base;
[/labs/, /\w*-examples/].forEach(function (href) {
var match = location.href.match(href);
if (!base && match) {
base = location.href.indexOf(match);
}
});
return location.href.substr(0, base);
}
function getFile(file, callback) {
if (!location.host) {
return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.');
}
var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true);
xhr.send();
xhr.onload = function () {
if (xhr.status === 200 && callback) {
callback(xhr.responseText);
}
};
}
function Learn(learnJSON, config) {
if (!(this instanceof Learn)) {
return new Learn(learnJSON, config);
}
var template, framework;
if (typeof learnJSON !== 'object') {
try {
learnJSON = JSON.parse(learnJSON);
} catch (e) {
return;
}
}
if (config) {
template = config.template;
framework = config.framework;
}
if (!template && learnJSON.templates) {
template = learnJSON.templates.todomvc;
}
if (!framework && document.querySelector('[data-framework]')) {
framework = document.querySelector('[data-framework]').getAttribute('data-framework');
}
if (template && learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.template = template;
this.append();
}
}
Learn.prototype.append = function () {
var aside = document.createElement('aside');
aside.innerHTML = _.template(this.template, this.frameworkJSON);
aside.className = 'learn';
// Localize demo links
var demoLinks = aside.querySelectorAll('.demo-link');
Array.prototype.forEach.call(demoLinks, function (demoLink) {
demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href'));
});
document.body.className = (document.body.className + ' learn-bar').trim();
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
};
redirect();
getFile('learn.json', Learn);
})();
<!doctype html>
<html lang="en" data-framework="atmajs">
<head>
<meta charset="utf-8">
<title>Atma.js • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css">
</head>
<body>
<script type="mask/template" id="layout">
/*
TodoMVC Atma.js Application
1. Read readme.md - you will get basic information about the libraries
2. Hint: Viewing *.mask files enable javascript or less syntax highlighting in your IDE
The application structure guide:
Controls Overview:
todo:input;
Components Overview:
:app\
:filter;
:todoList\
:todoTask;
Scripts overview (sorted from the most atomic parts up to the application component):
js/
model/Todos.js
cntrl/input.js
filter/filter.js
todoList/
todoTask/todoTask.js
todoList.js
app.js
_If the controller loads a template, do not forget to review that._
*/
// Application Template
section #todoapp {
header #header {
h1 > 'todos'
todo:input #new-todo
autofocus
x-signal = 'enter: newTask'
placeholder = 'What needs to be done?'
;
}
section #main >
:todoList;
footer #footer xx-visible = 'status.count' {
span #todo-count {
strong > '~[bind: status.todoCount]'
span > ' item~[bind: status.todoCount != 1 ? "s"] left'
}
:filter;
button #clear-completed
xx-visible = 'status.completedCount > 0'
x-signal = 'click: removeAllCompleted' {
'Clear completed (~[bind:status.completedCount])'
}
}
}
footer #info {
p { 'Double-click to edit a todo' }
p { 'Created by ' a href='http://github.com/tenbits' > 'tenbits' }
p { 'Part of ' a href='http://todomvc.com' > 'TodoMVC' }
}
</script>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/jquery/jquery.js"></script>
<script src="lib/atma-globals-dev.js"></script>
<script src="js/app.js"></script>
</body>
</html>
/*jshint newcap:false */
/*global include, Compo */
'use strict';
/**
* Application Entry Point
*
* - load immediate dependecies
* - define and initialize Application Component
*
*/
include
// Global route namespaces, to simplify resource dependency loading
.routes({
model: 'model/{0}.js',
cntrl: 'cntrl/{0}.js',
compo: 'compo/{0}/{1}.js'
})
.cfg({
// Load `/foo.bar` from current _directory_ path, and not from domain's root
lockToFolder: true
})
.js({
model: 'Todos',
cntrl: 'input',
compo: ['todoList', 'filter']
})
.ready(function (resp) {
/* Initialize and load the model from the Store */
var todos = resp.Todos.fetch();
var Application = Compo({
template: '#layout',
slots: {
newTask: function (event, title) {
if (title) {
this.model.create(title);
}
},
removeAllCompleted: function () {
this
.model
.del(function (x) {
return x.completed === true;
});
}
}
});
Compo.initialize(Application, todos, document.body);
});
/*jshint newcap:false */
/*global mask, Compo */
/**
* Extend INPUT tag to edit a todo's title
* - format string
* - complete edit on ENTER
* - complete edit on BLUR
*
* Used as
* - a main application's input
* - single todo item editor
*
* Public Events
* - cancel: input interrupted
* - enter : input formatted and completed
*
*/
(function () {
'use strict';
var ENTER_KEY = 13;
var ESCAPE_KEY = 27;
mask.registerHandler('todo:input', Compo({
tagName: 'input',
attr: {
type: 'text',
value: '~[title]',
// Clear input after edit, `true` for main input, `false` for todo's edit.
preserve: false
},
events: {
'keydown': function (event) {
switch (event.which) {
case ENTER_KEY:
this.save();
// prevent IE from button click - `Clear Completed`
event.preventDefault();
break;
case ESCAPE_KEY:
this.cancel();
break;
}
},
'blur': 'save'
},
focus: function focus() {
this.$.focus();
},
cancel: function cancel() {
this.$.trigger('cancel');
this.afterEdit();
},
save: function save() {
var value = this.$.val().trim();
this.$.trigger('enter', value);
this.afterEdit();
},
afterEdit: function () {
this.$.val(this.attr.preserve ? this.model.title : '');
}
}));
}());
/*global include, mask, Compo, ruta */
/*
* Filter Presentation:
* - HASH-change listener
* - navigation(filter) buttons
*
*/
include
.load('filter.mask::Template')
.done(function (resp) {
'use strict';
// `Compo` function creates components constructor,
// but it doesnt have ClassJS features, like inhertince and
// others. With `Compo.createClass`(_if ClassJS is used_) we
// can use those features in components constructor
mask.registerHandler(':filter', Compo.createClass({
template: resp.load.Template,
onRenderStart: function () {
ruta
// (RutaJS) Default router is the History API,
// but for this APP use hashes
.setRouterType('hash')
// Note: we do not bind to `this` compo instance,
// as applyFilter is already bound to it.
.add('/?:action', this.applyFilter)
;
// Define filters model
this.model = [{
title: 'All',
action: ''
}, {
title: 'Active',
action: 'active'
}, {
title: 'Completed',
action: 'completed'
}];
this.applyFilter(ruta.current());
},
Self: {
applyFilter: function (route) {
this.action = _setSelectedFilter(route.current, this.model);
// Emit a signal 'action' in a 'filter' pipe
Compo.pipe('filter').emit('action', this.action);
}
}
}));
function _setSelectedFilter(route, filters) {
var action = route.params.action || '';
// Update View via Binding
filters.forEach(function (filter) {
filter.selected = action === filter.action ? 'selected' : '';
});
return action;
}
});
ul #filters >
// take each filter item from the array model
% each = '.' >
li >
a.~[bind:selected] href = '#~[action]' >
'~[title]'
\ No newline at end of file
/*jshint newcap:false */
/*global include, mask, Compo*/
/*
* Todos Collection Component
*
* Collection is passed as a model to this component
*/
include
.load('todoList.mask')
.js('todoTask/todoTask.js')
.done(function (response) {
'use strict';
mask.registerHandler(':todoList', Compo({
template: response.load.todoList,
action: '',
pipes: {
// To manage the communication within hierarchical components
// (ancestors/descendants) you should use `slot-signal` pattern.
//
// To bind any components in an app use `piped_slot-signal` feature.
// Here we listen for signals in the `filter` pipe, which are
// emitted by `:filter` component
filter: {
action: function (action) {
this.action = action;
}
}
},
slots: {
// Component's slots for the signals,
// defined in the template and in a single tasks's template
toggleAll: function (event) {
this
.model
.each(function (task) {
task.completed = event.currentTarget.checked;
})
.save();
},
taskChanged: function () {
this.model.save();
},
taskRemoved: function (event, task) {
this.model.del(task);
}
}
}));
});
input#toggle-all
type = 'checkbox'
checked = '~[bind: status.todoCount == 0 ? "checked" ]'
x-signal = 'click: toggleAll'
// bind and hide/show accordingly
xx-visible = 'status.count'
;
label for = 'toggle-all' >
'Mark all as complete'
ul#todo-list {
// binded todos array
%% each = '.' >
:todoTask;
}
\ No newline at end of file
/*jshint newcap:false */
/*global include, mask, Compo */
/**
* Single Todo Item Component
* - View
* - Edit
*
* Public signals -
* taskChanged: Todo's state or title was changed
* taskRemoved: Todo was removed
* @arguments: Todo Model
*
*/
include
.load('todoTask.mask::Template')
.done(function (response) {
'use strict';
var STATE_VIEW = '';
var STATE_EDIT = 'editing';
mask.registerHandler(':todoTask', Compo({
//= Properties
state: STATE_VIEW,
//= Component Definition
template: response.load.Template,
slots: {
inputCanceled: '_editEnd',
taskChanged: function () {
if (!this.model.title) {
// [emitIn/emitOut] signal propagation begins from a sender
this.emitOut('taskRemoved');
// stop propagation of the `taskChanged` signal
return false;
}
this._editEnd();
},
taskRemoved: function () {
// remove component
this.remove();
// add arguments to the signal
return [this.model];
},
edit: function () {
this.state = STATE_EDIT;
this.compos.input.focus();
// stop signal propagation (example purpose)
return false;
}
},
compos: {
input: 'compo: todo:input'
},
//= Private Methods
_editEnd: function () {
this.state = STATE_VIEW;
},
_isVisible: function (completed, action) {
if ('completed' === action && !completed) {
return false;
}
if ('active' === action && completed) {
return false;
}
return true;
}
}));
});
li.~[bind:completed ? 'completed'].~[bind:state]
// emit `edit` on `dblclick` event
x-signal = 'dblclick: edit'
// bind to `completed` status and parent's `action` + change display property
xx-visible = '_isVisible(completed, closest(":todoList").action)' {
.view {
input.toggle type = 'checkbox' {
:dualbind
value = 'completed'
// emit signal when INPUTs state changes via user input
x-signal = 'dom: taskChanged'
;
}
label > '~[bind:title]';
button.destroy x-signal = 'click: taskRemoved';
}
// custom `todo:input` control
todo:input.edit
preserve
// emit signal on custom event `cancel`
// Note: we could also listen direct for the `cancel` event in the
// controller
x-signal = 'cancel: inputCanceled' {
:dualbind
value = 'title'
// change model on custom event `enter` defined in the control
change-event = 'enter'
// emit `taskChange` signal each time model is changed
// via user input
x-signal = 'dom: taskChanged'
;
}
}
\ No newline at end of file
/*jshint newcap:false */
/*global Class, ruqq, include */
(function () {
'use strict';
var Todo = Class({
Base: Class.Serializable,
// Properties with default values
title: '',
completed: false
});
include.exports = Class.Collection(Todo, {
Store: Class.LocalStore('todos-atmajs'),
create: function (title) {
// `push` initilizes the `Task` instance. It does the same
// as if we would do this via `new Task({title: title})`
return this
.push({
title: title
})
.save();
},
status: {
count: 0,
todoCount: 0,
completedCount: 0,
},
Override: {
// Override mutators and recalculate status,
// which will be use lately in M-V bindings
save: function () {
return this
.super(arguments)
.calcStatus();
},
del: function () {
return this
.super(arguments)
.calcStatus();
},
fetch: function () {
return this
.super(arguments)
.calcStatus();
}
},
calcStatus: function () {
this.status.count = this.length;
this.status.todoCount = ruqq.arr.count(this, 'completed', '==', false);
this.status.completedCount = ruqq.arr.count(this, 'completed', '==', true);
return this;
}
});
}());
This diff is collapsed.
# Atma.js TodoMVC Example
> Fast, elegant and component oriented framework for desktops, mobiles or servers _(node.js)_
> _[Atma - atmajs.com](http://atmajs.com)_
The framework consists of several stand-alone libraries. This approach not only reduces barriers to entry, but also
allows developers to exclude or replace any library with other third party one.
The goal of the framework is to deliver the component-based development and to provide libraries for making their composition easily and with blazing fast performance _(even on mobile CPUs)_.
## Learning Atma.js
#### ClassJS
[web-page](http://atmajs.com/class) [github](http://github.com/atmajs/ClassJS)
— is a class-model implementation. A business logic layer for applications. It accumulates best practices of the OOP and supports model de-/serialization with the persistence to localStorage, RESTful service or MongoDB. Any additional adapter can be created by the developer.
#### MaskJS
[web-page](http://atmajs.com/mask) [github](https://github.com/atmajs/MaskJS)
— is the core library of the Atma.js framework. It brings HMVC engine into play and everything starts with the markup. Instead of HTML, more compact and component-oriented syntax is used, which is similar to LESS and Zen Coding. But not the syntax is the advantage of the mask markup, but the DOM creation approach. It allows very fast to parse the templates to tiny MaskDOM (_json with the tree structure_). And while traversing the MaskDOM, the builder creates DOM Elements and initializes components. As the MaskDom structure is extremely lightweight, each component can easily manipulate the MaskDOM. So the all dynamic behavior, like interpolation, 1-/2way-binding, component's nesting and many other things, are almost for free in sens of the performance. Beyond fast DOM creation there are other distinguishing features:
- model agnostic
- components hierarchy
- better referencing via ```find/closest``` search _in a jquery way)_
- better communication via signals and slots. _Piped-signals are used to bind components, that are not in ascendant-descendant relation, but anywhere in an app_
- one-/two-way bindings with complex object observers, so even if deep, nested path to the property is used, any manipulations with the model preserve observers in place.
- custom attribute handlers
- designed to be used with other libraries. For example, with small wrappers we can encapsulate twitter bootstrap markups and widgets initializations
- high code reuse
To mention is, that the templates and the components can be rendered on the server side.
#### IncludeJS
[web-page](http://atmajs.com/include) [github](https://github.com/atmajs/IncludeJS)
— is created to load component's resources and to work in browsers and node.js the same way.
Some key points of the library are:
- no requirements for the module definition, but supports several: CommonJS and ```include.exports```
- in-place dependency declaration with nice namespaced routing
- custom loaders. _Already implemented ```coffee, less, yml, json```_
- lazy modules
- better debugging: loads javascript in browsers via ```script src='x'```
- for production builder can combine and optimize all resources into single ```*.js``` and single ```*.css```. All the templates are embedded into main ```*.html```. _Developing a web page using Atma node application module, builder also creates additionally single js/css/html files per page from the components that are specific to a page_
##### µTest
[github](https://github.com/atmajs/utest)
— _inspired by Buster.JS_ Simplifies unit test creation and runs them in node.js or in browser-slave(s) environments. All the Atma.js libraries are tested using the µTest.
##### Ruta
[github](https://github.com/atmajs/Ruta)
— is not only an url routing via History API or ```hashchange```, but it implements a Route-Value Collection for adding/retrieving any object by the route.
#### Atma.Toolkit
[github](https://github.com/atmajs/Atma.Toolkit)
— command-line tool, which runs unit tests, builds applications, runs node.js ```bash``` scripts, creates static file server with live reload feature, etc.
### Mask.Animation
[github](https://github.com/atmajs/mask-animation)
— CSS3 and sprite animations for MaskJS.
### Atma.js Server Application
[web-page](http://atmajs.com/atma-server) [github](https://github.com/atmajs/atma-server)
— a connect middle-ware. All the routes are declared in configuration files, and there are 3 types of endpoints:
- Handlers
- RESTful services
- Pages
Pages benefits from component based approach. Each component's controller can define caching settings, so that the component renders only once. Developer can restrict any component for the server side rendering only, so that the controller itself and any sensitive data is not sent to the client. When a component is rendered, then only HTML is sent to the client, _where all bindings and event listeners are attached_. So it is extremely SEO friendly.
Here are some links you may find helpful:
- [Get Started](http://atmajs.com/get/github)
- [Mask Markup Live Test](http://atmajs.com/mask-try)
- [Atma.js on GitHub](https://github.com/atmajs)
_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
The Application is split into components hierarchy, and so the application structure consists of a Component and an Application Layer. Any component has the resources (_that are component specific, like styles / templates and other nested components_) in the same folder and sub-. These makes it easer to _reuse_ them in other applications and makes it easer to develop and test them.
For the application globals dev build of Atma.js was used.
### Run
To run the main example, file access should be allowed in browser, as ```include``` loads templates with
```XMLHttpRequest```. But you can also start a built-in local server:
```bash
$ npm install -g atma # install atma.toolkit
$ atma server
```
navigate to ``` http://localhost:5777/ ```
### Build
To build the application for release, run ``` $ atma ```. We provide also a compiled version in 'build/' directory, so you
can see how the application looks like for production.
## Contact
- [team@atmajs.com](mailto:team@atmajs.com)
- [Google Group QA](https://groups.google.com/forum/#!forum/atmajs)
\ No newline at end of file
......@@ -161,6 +161,40 @@
}]
}]
},
"atmajs": {
"name": "Atma.js",
"description": "HMVC and the component-based architecture for building client, server or hybrid applications",
"homepage": "atmajs.com",
"examples": [{
"name": "Architecture Example",
"url": "labs/architecture-examples/atmajs/"
}],
"link_groups": [{
"heading": "Official Resources",
"links": [{
"name": "Get Started",
"url": "http://atmajs.com/get/github"
}, {
"name": "Atma.js on GitHub",
"url": "https://github.com/atmajs"
}]
}, {
"heading": "Overview",
"links": [{
"name": "Libraries",
"url": "https://github.com/tastejs/todomvc/blob/gh-pages/labs/architecture-examples/atmajs/readme.md"
}]
}, {
"heading": "Community",
"links": [{
"name": "Atma.js on StackOverflow",
"url": "http://stackoverflow.com/questions/tagged/atmajs"
}, {
"name": "Mailing list on Google Groups",
"url": "https://groups.google.com/forum/?fromgroups#!forum/atmajs"
}]
}]
},
"backbonejs": {
"name": "Backbone.js",
"description": "Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.",
......
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