Commit 987bc94c authored by Sam Saccone's avatar Sam Saccone

Merge pull request #1337 from tenbits/atmajs-update

update (Atma.js) rewrite the app
parents 7be1d104 8651418c
...@@ -4,11 +4,6 @@ node_modules/atma-class/* ...@@ -4,11 +4,6 @@ node_modules/atma-class/*
node_modules/includejs/* node_modules/includejs/*
!node_modules/includejs/lib/include.js !node_modules/includejs/lib/include.js
node_modules/jquery/*
!node_modules/jquery/dist
node_modules/jquery/dist/*
!node_modules/jquery/dist/jquery.js
node_modules/maskjs/* node_modules/maskjs/*
!node_modules/maskjs/lib/mask.js !node_modules/maskjs/lib/mask.js
......
...@@ -3,52 +3,21 @@ ...@@ -3,52 +3,21 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Atma.js • TodoMVC</title> <title>Atma.js • TodoMVC</title>
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
</head> </head>
<body> <body>
<!-- <!-- Loads App component from `js/App.mask` on start -->
<script type="text/mask" data-run="auto">
TodoMVC Atma.js Application import from 'node_modules/todomvc-common/base.css';
import from 'node_modules/todomvc-app-css/index.css';
1. Read readme.md - you will get basic information about the libraries import from 'node_modules/todomvc-common/base.js';
2. Hint: Viewing *.mask files enable javascript or less syntax highlighting in your IDE import App from 'js/app';
App;
The application structure guide: </script>
- Controls
todo:input;
- Components
: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._
-->
<script src="node_modules/todomvc-common/base.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script src="node_modules/includejs/lib/include.js"></script> <script src="node_modules/includejs/lib/include.js"></script>
<script src="node_modules/atma-class/lib/class.js"></script> <script src="node_modules/atma-class/lib/class.js"></script>
<script src="node_modules/maskjs/lib/mask.js"></script> <script src="node_modules/maskjs/lib/mask.js"></script>
<script src="node_modules/ruta/lib/ruta.js"></script> <script src="node_modules/ruta/lib/ruta.js"></script>
<script src="js/app.js"></script>
</body> </body>
</html> </html>
/*
* Filter Presentation:
* - HASH-change listener
* - navigation(filter) buttons
*/
define Filter {
var filters = {
'' : 'All',
active: 'Active',
completed: 'Completed'
};
ul .filters {
for ((key, val) in filters) {
// compare with the scoped value `action`
li >
a .~[bind: action == key ? 'selected' ] href = '#~key' >
'~val'
}
}
}
/**
* Extend INPUT tag to edit a todo's title
* - format string
* - complete edit on ENTER
* - complete edit on BLUR
*
* Used as
* - the main application's input
* - single todo item editor
*
* Public Signals
* - cancel: input interrupted
* - submit: input formatted and completed
*/
define TodoInput as (input type='text') {
event blur (e) {
this.submit();
}
event press:esc (e) {
this.cancel();
}
event press:enter (e) {
this.submit();
// prevent IE from button click - `Clear Completed`
e.preventDefault();
}
function submit () {
var str = this.$.val().trim();
this.emitOut('submit', str);
this.afterEdit();
}
function cancel () {
this.emitOut('cancel');
this.afterEdit();
}
function afterEdit () {
this.$.val(this.attr.preserve ? this.model.title : '');
}
}
...@@ -31,10 +31,15 @@ ...@@ -31,10 +31,15 @@
}) })
.save(); .save();
}, },
removeCompleted: function () {
this.del(function (x) {
return x.completed === true;
});
},
status: { status: {
count: 0, count: 0,
todoCount: 0, todoCount: 0,
completedCount: 0, completedCount: 0
}, },
Override: { Override: {
// Override mutators and recalculate status, // Override mutators and recalculate status,
...@@ -58,7 +63,7 @@ ...@@ -58,7 +63,7 @@
calcStatus: function () { calcStatus: function () {
var todos = 0; var todos = 0;
var completed = 0; var completed = 0;
this.forEach(function(todo){ this.forEach(function (todo) {
todo.completed && ++completed || ++todos; todo.completed && ++completed || ++todos;
}); });
......
import TodoTask from 'TodoTask';
define TodoList {
slot toggleAll (event) {
var completed = event.currentTarget.checked;
this.model.toggleAll(completed);
}
slot taskChanged () {
this.model.save();
}
slot taskRemoved (event, task) {
this.model.del(task);
}
input .toggle-all
type = checkbox
checked = '~[bind: status.todoCount == 0 ? "checked" ]'
x-tap = toggleAll
;
label for='toggle-all' > 'Mark all as complete'
ul .todo-list {
// bind todos collection
+each (.) > TodoTask;
}
}
...@@ -13,23 +13,17 @@ ...@@ -13,23 +13,17 @@
* *
*/ */
include (function () {
.load('todoTask.mask::Template')
.done(function (response) {
'use strict'; 'use strict';
var STATE_VIEW = ''; var STATE_VIEW = '';
var STATE_EDIT = 'editing'; var STATE_EDIT = 'editing';
include.exports = {
mask.registerHandler(':todoTask', Compo({
scope: { scope: {
state: STATE_VIEW state: STATE_VIEW
}, },
template: response.load.Template,
slots: { slots: {
inputCanceled: '_editEnd',
taskChanged: function () { taskChanged: function () {
if (!this.model.title) { if (!this.model.title) {
...@@ -40,7 +34,7 @@ include ...@@ -40,7 +34,7 @@ include
return false; return false;
} }
this._editEnd(); this.scope.state = STATE_VIEW;
}, },
taskRemoved: function () { taskRemoved: function () {
// remove component // remove component
...@@ -49,31 +43,33 @@ include ...@@ -49,31 +43,33 @@ include
// add arguments to the signal // add arguments to the signal
return [this.model]; return [this.model];
}, },
cancel: function () {
this.scope.state = STATE_VIEW;
},
submit: function () {
// do not send the signal to the app
return false;
},
edit: function () { edit: function () {
this.scope.state = STATE_EDIT; this.scope.state = STATE_EDIT;
this.compos.input.focus(); this.compos.input.$.focus();
} }
}, },
compos: { compos: {
input: 'compo: todo:input' input: 'compo: TaskEdit'
}, },
//= Private Methods //= Private Methods
_editEnd: function () {
this.scope.state = STATE_VIEW;
},
_isVisible: function (completed, action) { _isVisible: function (completed, action) {
if ('completed' === action && !completed) { if (action === 'completed' && !completed) {
return false; return false;
} }
if ('active' === action && completed) { if (action === 'active' && completed) {
return false; return false;
} }
return true; return true;
} }
})); };
});
})();
import * as TodoTaskController from 'TodoTask.js';
import TodoInput from '../Controls/TodoInput';
define TodoTask extends TodoTaskController {
let TaskView as (.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-tap = 'taskRemoved';
}
let TaskEdit as (input.edit preserve) extends TodoInput {
dualbind
value = 'title'
dom-slot = submit
// emit `taskChange` signal each time model is changed
// via user input
x-signal = 'dom: taskChanged'
;
}
/* `+visible` is same as `+if` with one difference:
* by falsy condition it still renders the nodes (with display:none)
* and `+if` first renders only when the condition becomes true.
*/
+visible ($._isVisible(completed, action)) {
li
.~[bind:completed ? 'completed']
.~[bind:state]
// emit `edit` on `dblclick` event
x-signal = 'dblclick: edit'
{
TaskView;
TaskEdit;
}
}
}
...@@ -4,58 +4,33 @@ ...@@ -4,58 +4,33 @@
'use strict'; 'use strict';
/** /**
* Application Entry Point * Controller for the App Component
*
* - load immediate dependecies
* - define and initialize Application Component
* *
* - load model dependecies
*/ */
include include
.js('Store/Todos.js')
.done(function (resp) {
// Global route namespaces, to simplify resource dependency loading include.exports = {
.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']
})
.load('./app.mask::Template')
.ready(function (resp) {
mask.registerHandler(':app', Compo({
template: resp.load.Template,
model: resp.Todos.fetch(), model: resp.Todos.fetch(),
scope: { scope: {
action: '' action: ''
}, },
slots: { slots: {
newTask: function (event, title) { submit: function (event, title) {
if (title) { if (title) {
this.model.create(title); this.model.create(title);
} }
}, },
removeAllCompleted: function () { removeAllCompleted: function () {
this.model.del(function (x) { this.model.removeCompleted();
return x.completed === true;
});
} }
}, },
onRenderStart: function () { onRenderStart: function () {
// (RutaJS) Default router is the History API, // (RutaJS) Default router is the History API,
// but for this app spec enable hashes // but for this app-spec we enable hashes
ruta ruta
.setRouterType('hash') .setRouterType('hash')
.add('/?:action', this.applyFilter.bind(this)) .add('/?:action', this.applyFilter.bind(this))
...@@ -65,7 +40,5 @@ include ...@@ -65,7 +40,5 @@ include
applyFilter: function (route, params) { applyFilter: function (route, params) {
this.scope.action = params.action || ''; this.scope.action = params.action || '';
} }
})); };
});
Compo.initialize(':app', document.body);
});
\ No newline at end of file
section #todoapp { import * as AppController from 'app.js';
header #header { import TodoInput from 'Controls/TodoInput';
h1 > 'todos' import TodoList from 'Todos/TodoList';
import Filter from 'Controls/Filter';
todo:input #new-todo define App extends AppController {
autofocus section .todoapp {
placeholder = 'What needs to be done?' header {
x-signal = 'enter: newTask' h1 > 'todos'
;
} TodoInput .new-todo
autofocus
+if (status.count) { placeholder = 'What needs to be done?'
section #main > ;
:todoList; }
footer #footer { +if (status.count) {
section .main >
span #todo-count { TodoList;
strong > '~[bind: status.todoCount]'
span > ' item~[bind: status.todoCount != 1 ? "s"] left' footer .footer {
}
span .todo-count {
:filter; strong > '~[bind: status.todoCount]'
span > ' item~[bind: status.todoCount != 1 ? "s"] left'
+if (status.completedCount > 0) { }
button #clear-completed x-signal = 'click: removeAllCompleted' >
'Clear completed' Filter;
+if (status.completedCount > 0) {
button .clear-completed x-tap = 'removeAllCompleted' >
'Clear completed'
}
} }
} }
} }
footer .info {
p { 'Double-click to edit a todo' }
p { 'Created by ' a href='http://github.com/tenbits' > 'Alex Kit' }
p { 'Part of ' a href='http://todomvc.com' > 'TodoMVC' }
}
} }
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' }
}
\ No newline at end of file
/*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 () {
this.$.focus();
},
cancel: function () {
this.$.trigger('cancel');
this.afterEdit();
},
save: function () {
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';
mask.registerHandler(':filter', Compo.createClass({
template: resp.load.Template
}));
});
var filters = {
'' : 'All',
'active': 'Active',
'completed': 'Completed'
};
ul #filters >
for ((key, val) in filters) {
// compare with the scoped value `action`
li >
a .~[bind: action == key ? 'selected' ] href = '#~[key]' >
'~[val]'
}
\ 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,
slots: {
// Component's slots for the signals
toggleAll: function (event) {
var completed = event.currentTarget.checked;
this.model.toggleAll(completed);
},
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'
;
label for='toggle-all' > 'Mark all as complete'
ul #todo-list {
// bind todos collection
+each (.) > :todoTask;
}
\ No newline at end of file
define :todoTask:view > .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';
}
define :todoTask:edit >
todo:input.edit
// do not clear input after edit
preserve
// emit signal on custom event `cancel`
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'
;
}
+if($c._isVisible(completed, action)) >
li
.~[bind:completed ? 'completed']
.~[bind:state]
// emit `edit` on `dblclick` event
x-signal = 'dblclick: edit'
{
:todoTask:view;
:todoTask:edit;
}
\ No newline at end of file
// source /src/license.txt // source /src/license.txt
/*! /*!
* ClassJS v1.0.67 * ClassJS v1.0.68
* Part of the Atma.js Project * Part of the Atma.js Project
* http://atmajs.com/ * http://atmajs.com/
* *
* MIT license * MIT license
* http://opensource.org/licenses/MIT * http://opensource.org/licenses/MIT
* *
* (c) 2012, 2014 Atma.js and other contributors * (c) 2012, 2015 Atma.js and other contributors
*/ */
// end:source /src/license.txt // end:source /src/license.txt
// source /src/umd.js // source /src/umd.js
...@@ -21,14 +21,11 @@ ...@@ -21,14 +21,11 @@
_exports _exports
; ;
_exports = root || _global; _exports = root || _global;
function construct(){ function construct(){
return factory(_global, _exports); return factory(_global, _exports);
}; }
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
...@@ -376,7 +373,6 @@ ...@@ -376,7 +373,6 @@
return; return;
} }
var Static; var Static;
if (is_Function(mix)) if (is_Function(mix))
Static = mix; Static = mix;
...@@ -407,17 +403,27 @@ ...@@ -407,17 +403,27 @@
} }
if (_extends != null) { if (_extends != null) {
arr_each(_extends, function(x){ arr_each(
x = proto_getProto(x); _extends,
proto_extendDefaultsDelegate(protoValue, key)
if (is_rawObject(x[key])) );
obj_defaults(protoValue, x[key]);
});
} }
} }
} }
// PRIVATE // PRIVATE
function proto_extendDefaultsDelegate(target, key) {
return function(source){
var proto = proto_getProto(source),
val = proto[key];
if (is_rawObject(val)) {
obj_defaults(target, val);
}
}
}
function proto_extend(proto, source) { function proto_extend(proto, source) {
if (source == null) if (source == null)
return; return;
...@@ -430,6 +436,9 @@ ...@@ -430,6 +436,9 @@
var key, val; var key, val;
for (key in source) { for (key in source) {
if (key === 'constructor')
continue;
val = source[key]; val = source[key];
if (val != null) if (val != null)
proto[key] = val; proto[key] = val;
...@@ -450,7 +459,6 @@ ...@@ -450,7 +459,6 @@
return fn_apply(super_, this, args); return fn_apply(super_, this, args);
} }
} else{ } else{
proxy = fn_doNothing; proxy = fn_doNothing;
} }
...@@ -491,6 +499,29 @@ ...@@ -491,6 +499,29 @@
_class.prototype = prototype; _class.prototype = prototype;
} }
function inherit_Object_create(_class, _base, _extends, original, _overrides, defaults) {
if (_base != null) {
_class.prototype = Object.create(_base.prototype);
obj_extendDescriptors(_class.prototype, original);
} else {
_class.prototype = Object.create(original);
}
_class.prototype.constructor = _class;
if (_extends != null) {
arr_each(_extends, function(x) {
obj_defaults(_class.prototype, x);
});
}
var proto = _class.prototype;
obj_defaults(proto, defaults);
for (var key in _overrides) {
proto[key] = proto_override(proto[key], _overrides[key]);
}
}
// browser that doesnt support __proto__ // browser that doesnt support __proto__
...@@ -608,13 +639,6 @@ ...@@ -608,13 +639,6 @@
json[asKey] = val.toJSON(); json[asKey] = val.toJSON();
continue; continue;
//@removed - serialize any if toJSON is implemented
//if (toJSON === json_proto_toJSON || toJSON === json_proto_arrayToJSON) {
// json[asKey] = val.toJSON();
// continue;
//}
break;
} }
json[asKey] = val; json[asKey] = val;
...@@ -806,7 +830,7 @@ ...@@ -806,7 +830,7 @@
define(target, key, descr); define(target, key, descr);
} }
return target; return target;
}; }
}()); }());
...@@ -1289,7 +1313,7 @@ ...@@ -1289,7 +1313,7 @@
_arguments[3], _arguments[3],
_arguments[4] _arguments[4]
); );
}; }
return fn.apply(ctx, _arguments); return fn.apply(ctx, _arguments);
}; };
...@@ -1500,6 +1524,7 @@ ...@@ -1500,6 +1524,7 @@
p.done(e_PRAPAIR_DATA, ''); p.done(e_PRAPAIR_DATA, '');
return p; return p;
} }
break;
default: default:
// @TODO notify not supported content type // @TODO notify not supported content type
// -> fallback to url encode // -> fallback to url encode
...@@ -2826,10 +2851,9 @@ ...@@ -2826,10 +2851,9 @@
}; };
} }
function Remote(route){ function Remote(route){
return new XHRRemote(route); return new XHRRemote(route);
}; }
Remote.onBefore = storageEvents_onBefore; Remote.onBefore = storageEvents_onBefore;
Remote.onAfter = storageEvents_onAfter; Remote.onAfter = storageEvents_onAfter;
...@@ -3083,9 +3107,11 @@ ...@@ -3083,9 +3107,11 @@
} }
} }
// eqeq to match by type diffs. /*jshint eqeqeq: false*/
if (value != matcher) if (value != matcher) {
return false; return false;
}
/*jshint eqeqeq: true*/
} }
return true; return true;
......
...@@ -1076,7 +1076,7 @@ ...@@ -1076,7 +1076,7 @@
} }
} }
if (currentInclude != null){ if (currentInclude != null && currentInclude.type === 'js'){
global.include = currentInclude; global.include = currentInclude;
} }
}, },
...@@ -1370,7 +1370,7 @@ ...@@ -1370,7 +1370,7 @@
return resource; return resource;
} }
resource = new Resource(); resource = new Resource('package');
resource.state = 4; resource.state = 4;
resource.location = path_getDir(path_normalize(url)); resource.location = path_getDir(path_normalize(url));
resource.parent = parent; resource.parent = parent;
...@@ -1468,7 +1468,7 @@ ...@@ -1468,7 +1468,7 @@
allDone: function(callback){ allDone: function(callback){
ScriptStack.complete(function(){ ScriptStack.complete(function(){
var pending = include.getPending('js'), var pending = include.getPending(),
await = pending.length; await = pending.length;
if (await === 0) { if (await === 0) {
callback(); callback();
...@@ -1478,7 +1478,7 @@ ...@@ -1478,7 +1478,7 @@
var i = -1, var i = -1,
imax = await; imax = await;
while( ++i < imax ){ while( ++i < imax ){
pending[i].on(4, check); pending[i].on(4, check, null, 'push');
} }
function check() { function check() {
......
This diff is collapsed.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
// source ../src/vars.js // source ../src/vars.js
var mask = global.mask || Mask; var mask = global.mask || (typeof Mask !== 'undefined' ? Mask : null);
// settings // settings
...@@ -27,6 +27,33 @@ ...@@ -27,6 +27,33 @@
var _cfg_isStrict = true, var _cfg_isStrict = true,
_Array_slice = Array.prototype.slice; _Array_slice = Array.prototype.slice;
// end:source ../src/vars.js // end:source ../src/vars.js
// source ../src/utils/obj.js
var obj_extend,
obj_create;
(function(){
obj_extend = function(a, b){
if (b == null)
return a || {};
if (a == null)
return obj_create(b);
for(var key in b){
a[key] = b[key];
}
return a;
};
obj_create = Object.create || function(x) {
var Ctor = function(){};
Ctor.prototype = x;
return new Ctor;
};
}());
// end:source ../src/utils/obj.js
// source ../src/utils/log.js // source ../src/utils/log.js
var log_error; var log_error;
(function(){ (function(){
...@@ -43,7 +70,9 @@ ...@@ -43,7 +70,9 @@
var path_normalize, var path_normalize,
path_split, path_split,
path_join, path_join,
path_fromCLI path_fromCLI,
path_getQuery,
path_setQuery
; ;
(function(){ (function(){
...@@ -93,6 +122,26 @@ ...@@ -93,6 +122,26 @@
return parts_serialize(parts); return parts_serialize(parts);
}; };
path_getQuery = function(path){
var i = path.indexOf('?');
if (i === -1)
return null;
var query = path.substring(i + 1);
return query_deserialize(query, '&');
};
path_setQuery = function(path, mix){
var query = typeof mix !== 'string'
? query_serialize(mix, '&')
: mix;
var i = path.indexOf('?');
if (i !== -1) {
path = path.substring(0, i);
}
return path + '?' + query;
};
// == private // == private
...@@ -892,12 +941,29 @@ ...@@ -892,12 +941,29 @@
} }
HistoryEmitter.prototype = { HistoryEmitter.prototype = {
navigate: function(url){ navigate: function(url, opts){
if (url == null) { if (url == null) {
this.changed(); this.changed();
return; return;
} }
if (opts != null && opts.extend === true) {
var query = path_getQuery(url),
current = path_getQuery(location.search);
if (current != null && query != null) {
for (var key in current) {
// strict compare
if (query[key] !== void 0 && query[key] === null) {
delete current[key];
}
}
query = obj_extend(current, query);
url = path_setQuery(url, query);
}
}
history.pushState({}, null, url); history.pushState({}, null, url);
this.initial = null; this.initial = null;
this.changed(); this.changed();
...@@ -949,8 +1015,8 @@ ...@@ -949,8 +1015,8 @@
route.value(route, current && current.params); route.value(route, current && current.params);
} }
}, },
navigate: function(url){ navigate: function(url, opts){
this.emitter.navigate(url); this.emitter.navigate(url, opts);
}, },
current: function(){ current: function(){
return this.collection.get( return this.collection.get(
...@@ -965,8 +1031,28 @@ ...@@ -965,8 +1031,28 @@
return Location; return Location;
}()); }());
// end:source ../src/emit/Location.js // end:source ../src/emit/Location.js
// source ../src/ruta.js
// source ../src/api/utils.js
var ApiUtils = {
/*
* Format URI path from CLI command:
* some action -foo bar === /some/action?foo=bar
*/
pathFromCLI: path_fromCLI,
query: {
serialize: query_serialize,
deserialize: query_deserialize,
get: function(path_){
var path = path_ == null
? location.search
: path_;
return path_getQuery(path);
}
}
};
// end:source ../src/api/utils.js
// source ../src/ruta.js
var routes = new Routes(), var routes = new Routes(),
router; router;
...@@ -1001,8 +1087,12 @@ ...@@ -1001,8 +1087,12 @@
get: function(path){ get: function(path){
return routes.get(path); return routes.get(path);
}, },
navigate: function(path){ navigate: function(mix, opts){
router_ensure().navigate(path); var path = mix;
if (mix != null && typeof mix === 'object') {
path = '?' + query_serialize(mix, '&');
}
router_ensure().navigate(path, opts);
return this; return this;
}, },
current: function(){ current: function(){
...@@ -1019,18 +1109,11 @@ ...@@ -1019,18 +1109,11 @@
parse: Routes.parse, parse: Routes.parse,
$utils: { /*
/* * @deprecated - use `_` instead
* Format URI path from CLI command: */
* some action -foo bar === /some/action?foo=bar $utils: ApiUtils,
*/ _ : ApiUtils,
pathFromCLI: path_fromCLI,
query: {
serialize: query_serialize,
deserialize: query_deserialize
}
}
}; };
...@@ -1039,6 +1122,9 @@ ...@@ -1039,6 +1122,9 @@
// source ../src/mask/attr/anchor-dynamic.js // source ../src/mask/attr/anchor-dynamic.js
(function() { (function() {
if (mask == null) {
return;
}
mask.registerAttrHandler('x-dynamic', function(node, value, model, ctx, tag){ mask.registerAttrHandler('x-dynamic', function(node, value, model, ctx, tag){
tag.onclick = navigate; tag.onclick = navigate;
......
...@@ -44,7 +44,7 @@ input[type="checkbox"] { ...@@ -44,7 +44,7 @@ input[type="checkbox"] {
display: none; display: none;
} }
#todoapp { .todoapp {
background: #fff; background: #fff;
margin: 130px 0 40px 0; margin: 130px 0 40px 0;
position: relative; position: relative;
...@@ -52,25 +52,25 @@ input[type="checkbox"] { ...@@ -52,25 +52,25 @@ input[type="checkbox"] {
0 25px 50px 0 rgba(0, 0, 0, 0.1); 0 25px 50px 0 rgba(0, 0, 0, 0.1);
} }
#todoapp input::-webkit-input-placeholder { .todoapp input::-webkit-input-placeholder {
font-style: italic; font-style: italic;
font-weight: 300; font-weight: 300;
color: #e6e6e6; color: #e6e6e6;
} }
#todoapp input::-moz-placeholder { .todoapp input::-moz-placeholder {
font-style: italic; font-style: italic;
font-weight: 300; font-weight: 300;
color: #e6e6e6; color: #e6e6e6;
} }
#todoapp input::input-placeholder { .todoapp input::input-placeholder {
font-style: italic; font-style: italic;
font-weight: 300; font-weight: 300;
color: #e6e6e6; color: #e6e6e6;
} }
#todoapp h1 { .todoapp h1 {
position: absolute; position: absolute;
top: -155px; top: -155px;
width: 100%; width: 100%;
...@@ -83,7 +83,7 @@ input[type="checkbox"] { ...@@ -83,7 +83,7 @@ input[type="checkbox"] {
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
#new-todo, .new-todo,
.edit { .edit {
position: relative; position: relative;
margin: 0; margin: 0;
...@@ -104,14 +104,14 @@ input[type="checkbox"] { ...@@ -104,14 +104,14 @@ input[type="checkbox"] {
font-smoothing: antialiased; font-smoothing: antialiased;
} }
#new-todo { .new-todo {
padding: 16px 16px 16px 60px; padding: 16px 16px 16px 60px;
border: none; border: none;
background: rgba(0, 0, 0, 0.003); background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
} }
#main { .main {
position: relative; position: relative;
z-index: 2; z-index: 2;
border-top: 1px solid #e6e6e6; border-top: 1px solid #e6e6e6;
...@@ -121,7 +121,7 @@ label[for='toggle-all'] { ...@@ -121,7 +121,7 @@ label[for='toggle-all'] {
display: none; display: none;
} }
#toggle-all { .toggle-all {
position: absolute; position: absolute;
top: -55px; top: -55px;
left: -12px; left: -12px;
...@@ -131,50 +131,50 @@ label[for='toggle-all'] { ...@@ -131,50 +131,50 @@ label[for='toggle-all'] {
border: none; /* Mobile Safari */ border: none; /* Mobile Safari */
} }
#toggle-all:before { .toggle-all:before {
content: '❯'; content: '❯';
font-size: 22px; font-size: 22px;
color: #e6e6e6; color: #e6e6e6;
padding: 10px 27px 10px 27px; padding: 10px 27px 10px 27px;
} }
#toggle-all:checked:before { .toggle-all:checked:before {
color: #737373; color: #737373;
} }
#todo-list { .todo-list {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
#todo-list li { .todo-list li {
position: relative; position: relative;
font-size: 24px; font-size: 24px;
border-bottom: 1px solid #ededed; border-bottom: 1px solid #ededed;
} }
#todo-list li:last-child { .todo-list li:last-child {
border-bottom: none; border-bottom: none;
} }
#todo-list li.editing { .todo-list li.editing {
border-bottom: none; border-bottom: none;
padding: 0; padding: 0;
} }
#todo-list li.editing .edit { .todo-list li.editing .edit {
display: block; display: block;
width: 506px; width: 506px;
padding: 13px 17px 12px 17px; padding: 13px 17px 12px 17px;
margin: 0 0 0 43px; margin: 0 0 0 43px;
} }
#todo-list li.editing .view { .todo-list li.editing .view {
display: none; display: none;
} }
#todo-list li .toggle { .todo-list li .toggle {
text-align: center; text-align: center;
width: 40px; width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */ /* auto, since non-WebKit browsers doesn't support input styling */
...@@ -188,15 +188,15 @@ label[for='toggle-all'] { ...@@ -188,15 +188,15 @@ label[for='toggle-all'] {
appearance: none; appearance: none;
} }
#todo-list li .toggle:after { .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>'); 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 { .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>'); 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 { .todo-list li label {
white-space: pre; white-space: pre;
word-break: break-word; word-break: break-word;
padding: 15px 60px 15px 15px; padding: 15px 60px 15px 15px;
...@@ -206,12 +206,12 @@ label[for='toggle-all'] { ...@@ -206,12 +206,12 @@ label[for='toggle-all'] {
transition: color 0.4s; transition: color 0.4s;
} }
#todo-list li.completed label { .todo-list li.completed label {
color: #d9d9d9; color: #d9d9d9;
text-decoration: line-through; text-decoration: line-through;
} }
#todo-list li .destroy { .todo-list li .destroy {
display: none; display: none;
position: absolute; position: absolute;
top: 0; top: 0;
...@@ -226,27 +226,27 @@ label[for='toggle-all'] { ...@@ -226,27 +226,27 @@ label[for='toggle-all'] {
transition: color 0.2s ease-out; transition: color 0.2s ease-out;
} }
#todo-list li .destroy:hover { .todo-list li .destroy:hover {
color: #af5b5e; color: #af5b5e;
} }
#todo-list li .destroy:after { .todo-list li .destroy:after {
content: '×'; content: '×';
} }
#todo-list li:hover .destroy { .todo-list li:hover .destroy {
display: block; display: block;
} }
#todo-list li .edit { .todo-list li .edit {
display: none; display: none;
} }
#todo-list li.editing:last-child { .todo-list li.editing:last-child {
margin-bottom: -1px; margin-bottom: -1px;
} }
#footer { .footer {
color: #777; color: #777;
padding: 10px 15px; padding: 10px 15px;
height: 20px; height: 20px;
...@@ -254,7 +254,7 @@ label[for='toggle-all'] { ...@@ -254,7 +254,7 @@ label[for='toggle-all'] {
border-top: 1px solid #e6e6e6; border-top: 1px solid #e6e6e6;
} }
#footer:before { .footer:before {
content: ''; content: '';
position: absolute; position: absolute;
right: 0; right: 0;
...@@ -269,16 +269,16 @@ label[for='toggle-all'] { ...@@ -269,16 +269,16 @@ label[for='toggle-all'] {
0 17px 2px -6px rgba(0, 0, 0, 0.2); 0 17px 2px -6px rgba(0, 0, 0, 0.2);
} }
#todo-count { .todo-count {
float: left; float: left;
text-align: left; text-align: left;
} }
#todo-count strong { .todo-count strong {
font-weight: 300; font-weight: 300;
} }
#filters { .filters {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
...@@ -287,11 +287,11 @@ label[for='toggle-all'] { ...@@ -287,11 +287,11 @@ label[for='toggle-all'] {
left: 0; left: 0;
} }
#filters li { .filters li {
display: inline; display: inline;
} }
#filters li a { .filters li a {
color: inherit; color: inherit;
margin: 3px; margin: 3px;
padding: 3px 7px; padding: 3px 7px;
...@@ -300,17 +300,17 @@ label[for='toggle-all'] { ...@@ -300,17 +300,17 @@ label[for='toggle-all'] {
border-radius: 3px; border-radius: 3px;
} }
#filters li a.selected, .filters li a.selected,
#filters li a:hover { .filters li a:hover {
border-color: rgba(175, 47, 47, 0.1); border-color: rgba(175, 47, 47, 0.1);
} }
#filters li a.selected { .filters li a.selected {
border-color: rgba(175, 47, 47, 0.2); border-color: rgba(175, 47, 47, 0.2);
} }
#clear-completed, .clear-completed,
html #clear-completed:active { html .clear-completed:active {
float: right; float: right;
position: relative; position: relative;
line-height: 20px; line-height: 20px;
...@@ -319,11 +319,11 @@ html #clear-completed:active { ...@@ -319,11 +319,11 @@ html #clear-completed:active {
position: relative; position: relative;
} }
#clear-completed:hover { .clear-completed:hover {
text-decoration: underline; text-decoration: underline;
} }
#info { .info {
margin: 65px auto 0; margin: 65px auto 0;
color: #bfbfbf; color: #bfbfbf;
font-size: 10px; font-size: 10px;
...@@ -331,17 +331,17 @@ html #clear-completed:active { ...@@ -331,17 +331,17 @@ html #clear-completed:active {
text-align: center; text-align: center;
} }
#info p { .info p {
line-height: 1; line-height: 1;
} }
#info a { .info a {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
font-weight: 400; font-weight: 400;
} }
#info a:hover { .info a:hover {
text-decoration: underline; text-decoration: underline;
} }
...@@ -350,16 +350,16 @@ html #clear-completed:active { ...@@ -350,16 +350,16 @@ html #clear-completed:active {
Can't use it globally since it destroys checkboxes in Firefox Can't use it globally since it destroys checkboxes in Firefox
*/ */
@media screen and (-webkit-min-device-pixel-ratio:0) { @media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all, .toggle-all,
#todo-list li .toggle { .todo-list li .toggle {
background: none; background: none;
} }
#todo-list li .toggle { .todo-list li .toggle {
height: 40px; height: 40px;
} }
#toggle-all { .toggle-all {
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);
transform: rotate(90deg); transform: rotate(90deg);
-webkit-appearance: none; -webkit-appearance: none;
...@@ -368,11 +368,11 @@ html #clear-completed:active { ...@@ -368,11 +368,11 @@ html #clear-completed:active {
} }
@media (max-width: 430px) { @media (max-width: 430px) {
#footer { .footer {
height: 50px; height: 50px;
} }
#filters { .filters {
bottom: 10px; bottom: 10px;
} }
} }
...@@ -114,7 +114,12 @@ ...@@ -114,7 +114,12 @@
})({}); })({});
if (location.hostname === 'todomvc.com') { if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-31081062-1', 'auto');
ga('send', 'pageview');
} }
/* jshint ignore:end */ /* jshint ignore:end */
...@@ -228,7 +233,7 @@ ...@@ -228,7 +233,7 @@
xhr.onload = function (e) { xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText); var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) { if (parsedResponse instanceof Array) {
var count = parsedResponse.length var count = parsedResponse.length;
if (count !== 0) { if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues'; issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline'; document.getElementById('issue-count').style.display = 'inline';
......
{ {
"private": true, "private": true,
"dependencies": { "dependencies": {
"atma-class": "^1.0.68", "atma-class": "^1.1.69",
"includejs": "^0.9.10", "includejs": "^0.9.14",
"jquery": "^2.1.3", "maskjs": "^0.52.0",
"maskjs": "^0.10.1", "ruta": "^0.1.16",
"ruta": "^0.1.12", "todomvc-app-css": "^2.0.1",
"todomvc-common": "^1.0.1", "todomvc-common": "^1.0.2"
"todomvc-app-css": "^1.0.0"
} }
} }
This diff is collapsed.
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