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/*
node_modules/includejs/*
!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/lib/mask.js
......
......@@ -3,52 +3,21 @@
<head>
<meta charset="utf-8">
<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>
<body>
<!--
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
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>
<!-- Loads App component from `js/App.mask` on start -->
<script type="text/mask" data-run="auto">
import from 'node_modules/todomvc-common/base.css';
import from 'node_modules/todomvc-app-css/index.css';
import from 'node_modules/todomvc-common/base.js';
import App from 'js/app';
App;
</script>
<script src="node_modules/includejs/lib/include.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/ruta/lib/ruta.js"></script>
<script src="js/app.js"></script>
</body>
</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 @@
})
.save();
},
removeCompleted: function () {
this.del(function (x) {
return x.completed === true;
});
},
status: {
count: 0,
todoCount: 0,
completedCount: 0,
completedCount: 0
},
Override: {
// Override mutators and recalculate status,
......@@ -58,7 +63,7 @@
calcStatus: function () {
var todos = 0;
var completed = 0;
this.forEach(function(todo){
this.forEach(function (todo) {
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 @@
*
*/
include
.load('todoTask.mask::Template')
.done(function (response) {
(function () {
'use strict';
var STATE_VIEW = '';
var STATE_EDIT = 'editing';
mask.registerHandler(':todoTask', Compo({
include.exports = {
scope: {
state: STATE_VIEW
},
template: response.load.Template,
slots: {
inputCanceled: '_editEnd',
taskChanged: function () {
if (!this.model.title) {
......@@ -40,7 +34,7 @@ include
return false;
}
this._editEnd();
this.scope.state = STATE_VIEW;
},
taskRemoved: function () {
// remove component
......@@ -49,31 +43,33 @@ include
// add arguments to the signal
return [this.model];
},
cancel: function () {
this.scope.state = STATE_VIEW;
},
submit: function () {
// do not send the signal to the app
return false;
},
edit: function () {
this.scope.state = STATE_EDIT;
this.compos.input.focus();
this.compos.input.$.focus();
}
},
compos: {
input: 'compo: todo:input'
input: 'compo: TaskEdit'
},
//= Private Methods
_editEnd: function () {
this.scope.state = STATE_VIEW;
},
_isVisible: function (completed, action) {
if ('completed' === action && !completed) {
if (action === 'completed' && !completed) {
return false;
}
if ('active' === action && completed) {
if (action === 'active' && completed) {
return false;
}
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 @@
'use strict';
/**
* Application Entry Point
*
* - load immediate dependecies
* - define and initialize Application Component
* Controller for the App Component
*
* - load model dependecies
*/
include
.js('Store/Todos.js')
.done(function (resp) {
// 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']
})
.load('./app.mask::Template')
.ready(function (resp) {
mask.registerHandler(':app', Compo({
template: resp.load.Template,
include.exports = {
model: resp.Todos.fetch(),
scope: {
action: ''
},
slots: {
newTask: function (event, title) {
submit: function (event, title) {
if (title) {
this.model.create(title);
}
},
removeAllCompleted: function () {
this.model.del(function (x) {
return x.completed === true;
});
this.model.removeCompleted();
}
},
onRenderStart: function () {
// (RutaJS) Default router is the History API,
// but for this app spec enable hashes
// but for this app-spec we enable hashes
ruta
.setRouterType('hash')
.add('/?:action', this.applyFilter.bind(this))
......@@ -65,7 +40,5 @@ include
applyFilter: function (route, params) {
this.scope.action = params.action || '';
}
}));
Compo.initialize(':app', document.body);
});
\ No newline at end of file
};
});
section #todoapp {
header #header {
h1 > 'todos'
import * as AppController from 'app.js';
import TodoInput from 'Controls/TodoInput';
import TodoList from 'Todos/TodoList';
import Filter from 'Controls/Filter';
todo:input #new-todo
autofocus
placeholder = 'What needs to be done?'
x-signal = 'enter: newTask'
;
}
+if (status.count) {
section #main >
:todoList;
footer #footer {
span #todo-count {
strong > '~[bind: status.todoCount]'
span > ' item~[bind: status.todoCount != 1 ? "s"] left'
}
:filter;
+if (status.completedCount > 0) {
button #clear-completed x-signal = 'click: removeAllCompleted' >
'Clear completed'
define App extends AppController {
section .todoapp {
header {
h1 > 'todos'
TodoInput .new-todo
autofocus
placeholder = 'What needs to be done?'
;
}
+if (status.count) {
section .main >
TodoList;
footer .footer {
span .todo-count {
strong > '~[bind: status.todoCount]'
span > ' item~[bind: status.todoCount != 1 ? "s"] left'
}
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
/*!
* ClassJS v1.0.67
* ClassJS v1.0.68
* Part of the Atma.js Project
* http://atmajs.com/
*
* MIT license
* 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
// source /src/umd.js
......@@ -21,14 +21,11 @@
_exports
;
_exports = root || _global;
function construct(){
return factory(_global, _exports);
};
}
if (typeof define === 'function' && define.amd) {
......@@ -376,7 +373,6 @@
return;
}
var Static;
if (is_Function(mix))
Static = mix;
......@@ -407,17 +403,27 @@
}
if (_extends != null) {
arr_each(_extends, function(x){
x = proto_getProto(x);
if (is_rawObject(x[key]))
obj_defaults(protoValue, x[key]);
});
arr_each(
_extends,
proto_extendDefaultsDelegate(protoValue, key)
);
}
}
}
// 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) {
if (source == null)
return;
......@@ -430,6 +436,9 @@
var key, val;
for (key in source) {
if (key === 'constructor')
continue;
val = source[key];
if (val != null)
proto[key] = val;
......@@ -450,7 +459,6 @@
return fn_apply(super_, this, args);
}
} else{
proxy = fn_doNothing;
}
......@@ -491,6 +499,29 @@
_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__
......@@ -608,13 +639,6 @@
json[asKey] = val.toJSON();
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;
......@@ -806,7 +830,7 @@
define(target, key, descr);
}
return target;
};
}
}());
......@@ -1289,7 +1313,7 @@
_arguments[3],
_arguments[4]
);
};
}
return fn.apply(ctx, _arguments);
};
......@@ -1500,6 +1524,7 @@
p.done(e_PRAPAIR_DATA, '');
return p;
}
break;
default:
// @TODO notify not supported content type
// -> fallback to url encode
......@@ -2826,10 +2851,9 @@
};
}
function Remote(route){
function Remote(route){
return new XHRRemote(route);
};
}
Remote.onBefore = storageEvents_onBefore;
Remote.onAfter = storageEvents_onAfter;
......@@ -3083,9 +3107,11 @@
}
}
// eqeq to match by type diffs.
if (value != matcher)
/*jshint eqeqeq: false*/
if (value != matcher) {
return false;
}
/*jshint eqeqeq: true*/
}
return true;
......
......@@ -1076,7 +1076,7 @@
}
}
if (currentInclude != null){
if (currentInclude != null && currentInclude.type === 'js'){
global.include = currentInclude;
}
},
......@@ -1370,7 +1370,7 @@
return resource;
}
resource = new Resource();
resource = new Resource('package');
resource.state = 4;
resource.location = path_getDir(path_normalize(url));
resource.parent = parent;
......@@ -1468,7 +1468,7 @@
allDone: function(callback){
ScriptStack.complete(function(){
var pending = include.getPending('js'),
var pending = include.getPending(),
await = pending.length;
if (await === 0) {
callback();
......@@ -1478,7 +1478,7 @@
var i = -1,
imax = await;
while( ++i < imax ){
pending[i].on(4, check);
pending[i].on(4, check, null, 'push');
}
function check() {
......
This diff is collapsed.
......@@ -15,7 +15,7 @@
// source ../src/vars.js
var mask = global.mask || Mask;
var mask = global.mask || (typeof Mask !== 'undefined' ? Mask : null);
// settings
......@@ -27,6 +27,33 @@
var _cfg_isStrict = true,
_Array_slice = Array.prototype.slice;
// 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
var log_error;
(function(){
......@@ -43,7 +70,9 @@
var path_normalize,
path_split,
path_join,
path_fromCLI
path_fromCLI,
path_getQuery,
path_setQuery
;
(function(){
......@@ -93,6 +122,26 @@
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
......@@ -892,12 +941,29 @@
}
HistoryEmitter.prototype = {
navigate: function(url){
navigate: function(url, opts){
if (url == null) {
this.changed();
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);
this.initial = null;
this.changed();
......@@ -949,8 +1015,8 @@
route.value(route, current && current.params);
}
},
navigate: function(url){
this.emitter.navigate(url);
navigate: function(url, opts){
this.emitter.navigate(url, opts);
},
current: function(){
return this.collection.get(
......@@ -965,8 +1031,28 @@
return Location;
}());
// 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(),
router;
......@@ -1001,8 +1087,12 @@
get: function(path){
return routes.get(path);
},
navigate: function(path){
router_ensure().navigate(path);
navigate: function(mix, opts){
var path = mix;
if (mix != null && typeof mix === 'object') {
path = '?' + query_serialize(mix, '&');
}
router_ensure().navigate(path, opts);
return this;
},
current: function(){
......@@ -1019,18 +1109,11 @@
parse: Routes.parse,
$utils: {
/*
* Format URI path from CLI command:
* some action -foo bar === /some/action?foo=bar
*/
pathFromCLI: path_fromCLI,
query: {
serialize: query_serialize,
deserialize: query_deserialize
}
}
/*
* @deprecated - use `_` instead
*/
$utils: ApiUtils,
_ : ApiUtils,
};
......@@ -1039,6 +1122,9 @@
// source ../src/mask/attr/anchor-dynamic.js
(function() {
if (mask == null) {
return;
}
mask.registerAttrHandler('x-dynamic', function(node, value, model, ctx, tag){
tag.onclick = navigate;
......
......@@ -44,7 +44,7 @@ input[type="checkbox"] {
display: none;
}
#todoapp {
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
......@@ -52,25 +52,25 @@ input[type="checkbox"] {
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
#todoapp input::-webkit-input-placeholder {
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::-moz-placeholder {
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::input-placeholder {
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp h1 {
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
......@@ -83,7 +83,7 @@ input[type="checkbox"] {
text-rendering: optimizeLegibility;
}
#new-todo,
.new-todo,
.edit {
position: relative;
margin: 0;
......@@ -104,14 +104,14 @@ input[type="checkbox"] {
font-smoothing: antialiased;
}
#new-todo {
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
#main {
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
......@@ -121,7 +121,7 @@ label[for='toggle-all'] {
display: none;
}
#toggle-all {
.toggle-all {
position: absolute;
top: -55px;
left: -12px;
......@@ -131,50 +131,50 @@ label[for='toggle-all'] {
border: none; /* Mobile Safari */
}
#toggle-all:before {
.toggle-all:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
#toggle-all:checked:before {
.toggle-all:checked:before {
color: #737373;
}
#todo-list {
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
#todo-list li:last-child {
.todo-list li:last-child {
border-bottom: none;
}
#todo-list li.editing {
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
.todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
......@@ -188,15 +188,15 @@ label[for='toggle-all'] {
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>');
}
#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>');
}
#todo-list li label {
.todo-list li label {
white-space: pre;
word-break: break-word;
padding: 15px 60px 15px 15px;
......@@ -206,12 +206,12 @@ label[for='toggle-all'] {
transition: color 0.4s;
}
#todo-list li.completed label {
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
#todo-list li .destroy {
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
......@@ -226,27 +226,27 @@ label[for='toggle-all'] {
transition: color 0.2s ease-out;
}
#todo-list li .destroy:hover {
.todo-list li .destroy:hover {
color: #af5b5e;
}
#todo-list li .destroy:after {
.todo-list li .destroy:after {
content: '×';
}
#todo-list li:hover .destroy {
.todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
.todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
.footer {
color: #777;
padding: 10px 15px;
height: 20px;
......@@ -254,7 +254,7 @@ label[for='toggle-all'] {
border-top: 1px solid #e6e6e6;
}
#footer:before {
.footer:before {
content: '';
position: absolute;
right: 0;
......@@ -269,16 +269,16 @@ label[for='toggle-all'] {
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
.todo-count {
float: left;
text-align: left;
}
#todo-count strong {
.todo-count strong {
font-weight: 300;
}
#filters {
.filters {
margin: 0;
padding: 0;
list-style: none;
......@@ -287,11 +287,11 @@ label[for='toggle-all'] {
left: 0;
}
#filters li {
.filters li {
display: inline;
}
#filters li a {
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
......@@ -300,17 +300,17 @@ label[for='toggle-all'] {
border-radius: 3px;
}
#filters li a.selected,
#filters li a:hover {
.filters li a.selected,
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
#filters li a.selected {
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
#clear-completed,
html #clear-completed:active {
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
......@@ -319,11 +319,11 @@ html #clear-completed:active {
position: relative;
}
#clear-completed:hover {
.clear-completed:hover {
text-decoration: underline;
}
#info {
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
......@@ -331,17 +331,17 @@ html #clear-completed:active {
text-align: center;
}
#info p {
.info p {
line-height: 1;
}
#info a {
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
#info a:hover {
.info a:hover {
text-decoration: underline;
}
......@@ -350,16 +350,16 @@ html #clear-completed:active {
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
.toggle-all,
.todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
.todo-list li .toggle {
height: 40px;
}
#toggle-all {
.toggle-all {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
......@@ -368,11 +368,11 @@ html #clear-completed:active {
}
@media (max-width: 430px) {
#footer {
.footer {
height: 50px;
}
#filters {
.filters {
bottom: 10px;
}
}
......@@ -114,7 +114,12 @@
})({});
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 */
......@@ -228,7 +233,7 @@
xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) {
var count = parsedResponse.length
var count = parsedResponse.length;
if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline';
......
{
"private": true,
"dependencies": {
"atma-class": "^1.0.68",
"includejs": "^0.9.10",
"jquery": "^2.1.3",
"maskjs": "^0.10.1",
"ruta": "^0.1.12",
"todomvc-common": "^1.0.1",
"todomvc-app-css": "^1.0.0"
"atma-class": "^1.1.69",
"includejs": "^0.9.14",
"maskjs": "^0.52.0",
"ruta": "^0.1.16",
"todomvc-app-css": "^2.0.1",
"todomvc-common": "^1.0.2"
}
}
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