Commit 083ba9fe authored by Kevin Malakoff's avatar Kevin Malakoff Committed by Sam Saccone

Update Knockback to 1.0.0 and simplified.

parent 24f9d0df
node_modules/knockback/*
!node_modules/knockback/knockback.js
!node_modules/knockback/node_modules
node_modules/knockback/node_modules/backbone/*
!node_modules/knockback/node_modules/backbone/backbone.js
node_modules/backbone/*
!node_modules/backbone/backbone.js
node_modules/knockback/node_modules/underscore/*
!node_modules/knockback/node_modules/underscore/underscore.js
node_modules/knockback/node_modules/knockout/*
!node_modules/knockback/node_modules/knockout/build
node_modules/knockback/node_modules/knockout/build/*
!node_modules/knockback/node_modules/knockout/build/output
node_modules/knockback/node_modules/knockout/build/output/*
!node_modules/knockback/node_modules/knockout/build/output/knockout-latest.debug.js
node_modules/backbone.localStorage/*
!node_modules/backbone.localStorage/backbone.localStorage.js
node_modules/jquery/*
!node_modules/jquery/dist
node_modules/jquery/dist/*
!node_modules/jquery/dist/jquery.js
node_modules/knockback/*
!node_modules/knockback/knockback.js
node_modules/knockout/*
!node_modules/knockout/build
node_modules/knockout/build/*
!node_modules/knockout/build/output
node_modules/knockout/build/output/*
!node_modules/knockout/build/output/knockout-latest.debug.js
node_modules/todomvc-app-css/*
!node_modules/todomvc-app-css/index.css
node_modules/todomvc-common/*
!node_modules/todomvc-common/base.css
!node_modules/todomvc-common/base.js
node_modules/underscore/*
!node_modules/underscore/underscore.js
......@@ -7,28 +7,28 @@
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
</head>
<body>
<section id="todoapp" kb-inject="AppViewModel">
<header id="header">
<section class="todoapp" kb-inject="AppViewModel">
<header class="header">
<h1>todos</h1>
<input id="new-todo" type="text" data-bind="value: title, valueUpdate: 'afterkeydown', event: {keyup: onAddTodo}" placeholder="What needs to be done?" autofocus>
<input class="new-todo" type="text" data-bind="value: title, valueUpdate: 'afterkeydown', event: {keyup: onAddTodo}" placeholder="What needs to be done?" autofocus>
</header>
<section id="main" data-bind="block: tasks_exist">
<input id="toggle-all" type="checkbox" data-bind="checked: all_completed">
<section class="main" data-bind="visible: tasks_exist">
<input class="toggle-all" type="checkbox" data-bind="checked: all_completed">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-bind="foreach: todos">
<ul class="todo-list" data-bind="foreach: todos">
<li data-bind="css: {completed: completed, editing: editing}">
<div class="view" data-bind="event: {dblclick: onCheckEditBegin}">
<input class="toggle" type="checkbox" data-bind="checked: completed" checked>
<label data-bind="text: title"></label>
<button class="destroy" data-bind="click: onDestroyTodo"></button>
<button class="destroy" data-bind="click: onDestroy"></button>
</div>
<input class="edit" type="text" data-bind="value: title, selectAndFocus: editing, event: {blur: onCheckEditEnd, keyup: onCheckEditEnd}">
<input class="edit" type="text" data-bind="value: edit_title, event: {blur: onCheckEditEnd, keyup: onCheckEditEnd}">
</li>
</ul>
</section>
<footer id="footer" data-bind="block: tasks_exist">
<span id="todo-count" data-bind="html: loc.remaining_message"></span>
<ul id="filters">
<footer class="footer" data-bind="visible: tasks_exist">
<span class="todo-count" data-bind="html: loc.remaining_message"></span>
<ul class="filters">
<li>
<a href="#/" data-bind="css: {selected: list_filter_mode()==''}">All</a>
</li>
......@@ -39,10 +39,10 @@
<a href="#/completed" data-bind="css: {selected: list_filter_mode()=='completed'}">Completed</a>
</li>
</ul>
<button id="clear-completed" data-bind="text: loc.clear_message, block: loc.clear_message, click: onDestroyCompleted"></button>
<button class="clear-completed" data-bind="text: loc.clear_message, visible: loc.clear_message, click: onClearCompleted"></button>
</footer>
</section>
<footer id="info">
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="https://github.com/kmalakoff">Kevin Malakoff</a>. <br/>
Please try out the <a href="http://kmalakoff.github.com/knockback-todos-app/">enhanced version</a> <br/>
......@@ -54,15 +54,13 @@
<!-- App Dependencies -->
<script src="node_modules/todomvc-common/base.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script src="node_modules/knockback/node_modules/underscore/underscore.js"></script>
<script src="node_modules/knockback/node_modules/backbone/backbone.js"></script>
<script src="node_modules/knockback/node_modules/knockout/build/output/knockout-latest.debug.js"></script>
<script src="node_modules/underscore/underscore.js"></script>
<script src="node_modules/backbone/backbone.js"></script>
<script src="node_modules/knockout/build/output/knockout-latest.debug.js"></script>
<script src="node_modules/knockback/knockback.js"></script>
<script src="node_modules/backbone.localStorage/backbone.localStorage.js"></script>
<script src="node_modules/backbone.localstorage/backbone.localStorage.js"></script>
<!-- App and Components -->
<script src="js/lib/knockout-extended-bindings.js"></script>
<script src="js/models/todo.js"></script>
<script src="js/models/todo_collection.js"></script>
<script src="js/viewmodels/todo.js"></script>
<script src="js/viewmodels/app.js"></script>
......
// Generated by CoffeeScript 1.8.0
(function() {
ko.bindingHandlers.dblclick = {
init: function(element, value_accessor) {
return $(element).dblclick(ko.utils.unwrapObservable(value_accessor()));
}
};
ko.bindingHandlers.block = {
update: function(element, value_accessor) {
return element.style.display = ko.utils.unwrapObservable(value_accessor()) ? 'block' : 'none';
}
};
ko.bindingHandlers.selectAndFocus = {
init: function(element, value_accessor, all_bindings_accessor) {
ko.bindingHandlers.hasfocus.init(element, value_accessor, all_bindings_accessor);
return ko.utils.registerEventHandler(element, 'focus', function() {
return element.select();
});
},
update: function(element, value_accessor) {
ko.utils.unwrapObservable(value_accessor());
return _.defer(function() {
return ko.bindingHandlers.hasfocus.update(element, value_accessor);
});
}
};
}).call(this);
// Generated by CoffeeScript 1.8.0
(function() {
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
window.Todo = (function(_super) {
__extends(Todo, _super);
function Todo() {
return Todo.__super__.constructor.apply(this, arguments);
}
Todo.prototype.completed = function(completed) {
if (arguments.length === 0) {
return !!this.get('completed');
}
return this.save({
completed: completed ? new Date() : null
});
};
return Todo;
})(Backbone.Model);
}).call(this);
// Generated by CoffeeScript 1.8.0
// Generated by CoffeeScript 1.10.0
(function() {
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
window.TodoCollection = (function(_super) {
__extends(TodoCollection, _super);
window.TodoCollection = (function(superClass) {
extend(TodoCollection, superClass);
function TodoCollection() {
return TodoCollection.__super__.constructor.apply(this, arguments);
......@@ -12,35 +12,6 @@
TodoCollection.prototype.localStorage = new Store('todos-knockback');
TodoCollection.prototype.model = Todo;
TodoCollection.prototype.completedCount = function() {
return this.models.reduce((function(prev, cur) {
return prev + (cur.completed() ? 1 : 0);
}), 0);
};
TodoCollection.prototype.remainingCount = function() {
return this.models.length - this.completedCount();
};
TodoCollection.prototype.completeAll = function(completed) {
return this.each(function(todo) {
return todo.completed(completed);
});
};
TodoCollection.prototype.destroyCompleted = function() {
var completed_tasks, model, _i, _len;
completed_tasks = this.filter(function(todo) {
return todo.completed();
});
for (_i = 0, _len = completed_tasks.length; _i < _len; _i++) {
model = completed_tasks[_i];
model.destroy();
}
};
return TodoCollection;
})(Backbone.Collection);
......
// Generated by CoffeeScript 1.8.0
// Generated by CoffeeScript 1.10.0
(function() {
var ENTER_KEY;
var ENTER_KEY,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
ENTER_KEY = 13;
window.AppViewModel = function() {
var filter_fn, router;
this.collections = {
todos: new TodoCollection()
};
this.collections.todos.fetch();
this.list_filter_mode = ko.observable('');
filter_fn = ko.computed((function(_this) {
return function() {
switch (_this.list_filter_mode()) {
case 'active':
return function(model) {
return !model.completed();
};
case 'completed':
return function(model) {
return model.completed();
};
default:
return function() {
return true;
};
}
};
})(this));
this.todos = kb.collectionObservable(this.collections.todos, {
view_model: TodoViewModel,
filters: filter_fn
});
this.todos_changed = kb.triggeredObservable(this.collections.todos, 'change add remove');
this.tasks_exist = ko.computed((function(_this) {
return function() {
_this.todos_changed();
return !!_this.collections.todos.length;
};
})(this));
this.title = ko.observable('');
this.onAddTodo = (function(_this) {
return function(view_model, event) {
if (!$.trim(_this.title()) || (event.keyCode !== ENTER_KEY)) {
return true;
}
_this.collections.todos.create({
title: $.trim(_this.title())
});
return _this.title('');
};
})(this);
this.remaining_count = ko.computed((function(_this) {
return function() {
_this.todos_changed();
return _this.collections.todos.remainingCount();
};
})(this));
this.completed_count = ko.computed((function(_this) {
return function() {
_this.todos_changed();
return _this.collections.todos.completedCount();
};
})(this));
this.all_completed = ko.computed({
read: (function(_this) {
window.AppViewModel = (function() {
function AppViewModel() {
this.onClearCompleted = bind(this.onClearCompleted, this);
this.onAddTodo = bind(this.onAddTodo, this);
var filter_fn, fn, i, len, ref, route, router;
this.list_filter_mode = ko.observable('');
filter_fn = ko.computed((function(_this) {
return function() {
return !_this.remaining_count();
switch (_this.list_filter_mode()) {
case 'active':
return (function(model) {
return !model.get('completed');
});
case 'completed':
return (function(model) {
return model.get('completed');
});
default:
return (function() {
return true;
});
}
};
})(this),
write: (function(_this) {
return function(completed) {
return _this.collections.todos.completeAll(completed);
})(this));
this.todos = kb.collectionObservable(new TodoCollection(), TodoViewModel, {
filters: filter_fn
});
this.todos.collection().fetch();
this._todos_changed = kb.triggeredObservable(this.todos.collection(), 'change add remove');
this.tasks_exist = ko.computed((function(_this) {
return function() {
_this._todos_changed();
return !!_this.todos.collection().models.length;
};
})(this)
});
this.onDestroyCompleted = (function(_this) {
return function() {
return _this.collections.todos.destroyCompleted();
};
})(this);
this.loc = {
remaining_message: ko.computed((function(_this) {
})(this));
this.title = ko.observable('');
this.completed_count = ko.computed((function(_this) {
return function() {
return "<strong>" + (_this.remaining_count()) + "</strong> " + (_this.remaining_count() === 1 ? 'item' : 'items') + " left";
_this._todos_changed();
return _this.todos.collection().filter(function(model) {
return model.get('completed');
}).length;
};
})(this)),
clear_message: ko.computed((function(_this) {
})(this));
this.remaining_count = ko.computed((function(_this) {
return function() {
var count;
if ((count = _this.completed_count())) {
return "Clear completed";
} else {
return '';
}
_this._todos_changed();
return _this.todos.collection().length - _this.completed_count();
};
})(this))
};
router = new Backbone.Router;
router.route('', null, (function(_this) {
return function() {
return _this.list_filter_mode('');
};
})(this));
router.route('active', null, (function(_this) {
return function() {
return _this.list_filter_mode('active');
};
})(this));
router.route('completed', null, (function(_this) {
return function() {
return _this.list_filter_mode('completed');
})(this));
this.all_completed = ko.computed({
read: (function(_this) {
return function() {
return !_this.remaining_count();
};
})(this),
write: (function(_this) {
return function(completed) {
return _this.todos.collection().each(function(model) {
return model.save({
completed: completed
});
});
};
})(this)
});
this.loc = {
remaining_message: ko.computed((function(_this) {
return function() {
return "<strong>" + (_this.remaining_count()) + "</strong> " + (_this.remaining_count() === 1 ? 'item' : 'items') + " left";
};
})(this)),
clear_message: ko.computed((function(_this) {
return function() {
if (_this.completed_count()) {
return 'Clear completed';
} else {
return '';
}
};
})(this))
};
})(this));
Backbone.history.start();
};
router = new Backbone.Router;
ref = ['', 'active', 'completed'];
fn = (function(_this) {
return function(route) {
return router.route(route, null, function() {
return _this.list_filter_mode(route);
});
};
})(this);
for (i = 0, len = ref.length; i < len; i++) {
route = ref[i];
fn(route);
}
Backbone.history.start();
}
AppViewModel.prototype.onAddTodo = function(vm, event) {
if (!$.trim(this.title()) || (event.keyCode !== ENTER_KEY)) {
return;
}
this.todos.collection().create({
title: $.trim(this.title())
});
return this.title('');
};
AppViewModel.prototype.onClearCompleted = function() {
var i, len, model, ref, results;
ref = this.todos.collection().filter(function(model) {
return model.get('completed');
});
results = [];
for (i = 0, len = ref.length; i < len; i++) {
model = ref[i];
results.push(model.destroy());
}
return results;
};
return AppViewModel;
})();
}).call(this);
// Generated by CoffeeScript 1.8.0
// Generated by CoffeeScript 1.10.0
(function() {
window.TodoViewModel = function(model) {
var ENTER_KEY, ESCAPE_KEY;
ENTER_KEY = 13;
ESCAPE_KEY = 27;
this.editing = ko.observable(false);
this.completed = kb.observable(model, {
key: 'completed',
read: (function() {
return model.completed();
}),
write: (function(completed) {
return model.completed(completed);
})
}, this);
this.title = kb.observable(model, {
key: 'title',
write: ((function(_this) {
return function(title) {
if ($.trim(title)) {
model.save({
title: $.trim(title)
});
} else {
_.defer(function() {
return model.destroy();
});
}
return _this.editing(false);
var ENTER_KEY, ESCAPE_KEY,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
ENTER_KEY = 13;
ESCAPE_KEY = 27;
window.TodoViewModel = (function(superClass) {
extend(TodoViewModel, superClass);
function TodoViewModel(model, options) {
this.onCheckEditEnd = bind(this.onCheckEditEnd, this);
this.onCheckEditBegin = bind(this.onCheckEditBegin, this);
this.onDestroy = bind(this.onDestroy, this);
TodoViewModel.__super__.constructor.call(this, model, {
requires: ['title', 'completed']
}, options);
this.completed.subscribe((function(_this) {
return function(completed) {
return _this.model().save({
completed: completed
});
};
})(this))
}, this);
this.onDestroyTodo = (function(_this) {
return function() {
return model.destroy();
};
})(this);
this.onCheckEditBegin = (function(_this) {
return function() {
if (!_this.editing()) {
_this.editing(true);
return $('.todo-input').focus();
}
};
})(this);
this.onCheckEditEnd = (function(_this) {
return function(view_model, event) {
if (event.keyCode === ESCAPE_KEY) {
_this.editing(false);
}
if ((event.keyCode === ENTER_KEY) || (event.type === 'blur')) {
$('.todo-input').blur();
return _this.editing(false);
})(this));
this.edit_title = ko.observable();
this.editing = ko.observable(false);
}
TodoViewModel.prototype.onDestroy = function() {
return this.model().destroy();
};
TodoViewModel.prototype.onCheckEditBegin = function() {
if (this.editing()) {
return;
}
this.edit_title(this.title());
this.editing(true);
return $('.todo-input').focus();
};
TodoViewModel.prototype.onCheckEditEnd = function(vm, event) {
var title;
if (!this.editing()) {
return;
}
if (event.keyCode === ESCAPE_KEY) {
this.editing(false);
}
if ((event.keyCode === ENTER_KEY) || (event.type === 'blur')) {
$('.todo-input').blur();
title = this.edit_title();
if ($.trim(title)) {
this.model().set({
title: title
}).save({
title: $.trim(title)
});
} else {
_.defer((function(_this) {
return function() {
return _this.model().destroy();
};
})(this));
}
};
})(this);
};
return this.editing(false);
}
};
return TodoViewModel;
})(kb.ViewModel);
}).call(this);
/*!
* jQuery JavaScript Library v2.1.3
* jQuery JavaScript Library v2.1.4
* http://jquery.com/
*
* Includes Sizzle.js
......@@ -9,7 +9,7 @@
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-12-18T15:11Z
* Date: 2015-04-28T16:01Z
*/
(function( global, factory ) {
......@@ -67,7 +67,7 @@ var
// Use the correct document accordingly with window argument (sandbox)
document = window.document,
version = "2.1.3",
version = "2.1.4",
// Define a local copy of jQuery
jQuery = function( selector, context ) {
......@@ -531,7 +531,12 @@ jQuery.each("Boolean Number String Function Array Date RegExp Object Error".spli
});
function isArraylike( obj ) {
var length = obj.length,
// Support: iOS 8.2 (not reproducible in simulator)
// `in` check used to prevent JIT error (gh-2145)
// hasOwn isn't used here due to false negatives
// regarding Nodelist length in IE
var length = "length" in obj && obj.length,
type = jQuery.type( obj );
if ( type === "function" || jQuery.isWindow( obj ) ) {
......
......@@ -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": {
"backbone.localStorage": "^1.1.3",
"jquery": "^2.0.0",
"knockback": "^0.20.5",
"todomvc-app-css": "^1.0.0",
"todomvc-common": "^1.0.1"
"backbone": "^1.2.3",
"backbone.localstorage": "^1.1.16",
"jquery": "^2.1.4",
"knockback": "^1.0.0",
"knockout": "^3.3.0",
"todomvc-app-css": "^2.0.1",
"todomvc-common": "^1.0.2",
"underscore": "^1.8.3"
}
}
# Add custom handlers to Knockout.js - adapted from Knockout.js Todos app: https://github.com/ashish01/knockoutjs-todos
ko.bindingHandlers.dblclick =
init: (element, value_accessor) -> $(element).dblclick(ko.utils.unwrapObservable(value_accessor()))
ko.bindingHandlers.block =
update: (element, value_accessor) -> element.style.display = if ko.utils.unwrapObservable(value_accessor()) then 'block' else 'none'
ko.bindingHandlers.selectAndFocus =
init: (element, value_accessor, all_bindings_accessor) ->
ko.bindingHandlers.hasfocus.init(element, value_accessor, all_bindings_accessor)
ko.utils.registerEventHandler(element, 'focus', -> element.select())
update: (element, value_accessor) ->
ko.utils.unwrapObservable(value_accessor()) # create dependency
_.defer -> ko.bindingHandlers.hasfocus.update(element, value_accessor)
\ No newline at end of file
class window.Todo extends Backbone.Model
completed: (completed) ->
return !!@get('completed') if arguments.length is 0 # getter
@save({completed: if completed then new Date() else null}) # setter
\ No newline at end of file
class window.TodoCollection extends Backbone.Collection
localStorage: new Store('todos-knockback') # Save all of the todos under the "todos-knockback" namespace.
model: Todo
completedCount: -> @models.reduce(((prev,cur)-> return prev + if cur.completed() then 1 else 0), 0)
remainingCount: -> @models.length - @completedCount()
completeAll: (completed) -> @each((todo) -> todo.completed(completed))
destroyCompleted: ->
completed_tasks = @filter((todo) -> return todo.completed())
model.destroy() for model in completed_tasks
return
\ No newline at end of file
ENTER_KEY = 13
window.AppViewModel = ->
#############################
# Shared
#############################
# collections
@collections =
todos: new TodoCollection()
@collections.todos.fetch()
# shared observables
@list_filter_mode = ko.observable('')
filter_fn = ko.computed =>
switch @list_filter_mode()
when 'active' then return (model) -> return not model.completed()
when 'completed' then return (model) -> return model.completed()
else return -> return true
@todos = kb.collectionObservable(@collections.todos, {view_model: TodoViewModel, filters: filter_fn})
@todos_changed = kb.triggeredObservable(@collections.todos, 'change add remove')
@tasks_exist = ko.computed(=> @todos_changed(); return !!@collections.todos.length)
#############################
# Header Section
#############################
@title = ko.observable('')
@onAddTodo = (view_model, event) =>
return true if not $.trim(@title()) or (event.keyCode != ENTER_KEY)
# Create task and reset UI
@collections.todos.create({title: $.trim(@title())})
class window.AppViewModel
constructor: ->
@list_filter_mode = ko.observable('')
filter_fn = ko.computed(=>
switch @list_filter_mode()
when 'active' then return ((model) -> return !model.get('completed'))
when 'completed' then return ((model) -> return model.get('completed'))
else return (-> return true)
)
@todos = kb.collectionObservable(new TodoCollection(), TodoViewModel, {filters: filter_fn})
@todos.collection().fetch()
# Note: collectionObservables only track additions and removals so model attribute changes need to be manually triggered
@_todos_changed = kb.triggeredObservable(@todos.collection(), 'change add remove')
@tasks_exist = ko.computed(=> @_todos_changed(); return !!@todos.collection().models.length)
@title = ko.observable('')
@completed_count = ko.computed(=> @_todos_changed(); return @todos.collection().filter((model) -> return model.get('completed')).length)
@remaining_count = ko.computed(=> @_todos_changed(); return @todos.collection().length - @completed_count())
@all_completed = ko.computed({
read: => return not @remaining_count()
write: (completed) => @todos.collection().each((model) -> model.save({completed}))
})
@loc =
remaining_message: ko.computed(=> return "<strong>#{@remaining_count()}</strong> #{if @remaining_count() == 1 then 'item' else 'items'} left")
clear_message: ko.computed(=> return if @completed_count() then 'Clear completed' else '')
router = new Backbone.Router
for route in ['', 'active', 'completed'] then do (route) =>
router.route(route, null, => @list_filter_mode(route))
Backbone.history.start()
onAddTodo: (vm, event) =>
return if not $.trim(@title()) or (event.keyCode isnt ENTER_KEY)
@todos.collection().create({title: $.trim(@title())})
@title('')
#############################
# Main Section
#############################
@remaining_count = ko.computed => @todos_changed(); return @collections.todos.remainingCount()
@completed_count = ko.computed => @todos_changed(); return @collections.todos.completedCount()
@all_completed = ko.computed
read: => return not @remaining_count()
write: (completed) => @collections.todos.completeAll(completed)
#############################
# Footer Section
#############################
@onDestroyCompleted = => @collections.todos.destroyCompleted()
#############################
# Localization
#############################
@loc =
remaining_message: ko.computed(=> return "<strong>#{@remaining_count()}</strong> #{if @remaining_count() == 1 then 'item' else 'items'} left")
clear_message: ko.computed(=> return if (count = @completed_count()) then "Clear completed (#{count})" else '')
#############################
# Routing
#############################
router = new Backbone.Router
router.route('', null, => @list_filter_mode(''))
router.route('active', null, => @list_filter_mode('active'))
router.route('completed', null, => @list_filter_mode('completed'))
Backbone.history.start()
return
\ No newline at end of file
onClearCompleted: => model.destroy() for model in @todos.collection().filter((model) -> return model.get('completed'))
window.TodoViewModel = (model) ->
ENTER_KEY = 13
ESCAPE_KEY = 27
ENTER_KEY = 13
ESCAPE_KEY = 27
@editing = ko.observable(false)
@completed = kb.observable(model, {key: 'completed', read: (-> return model.completed()), write: ((completed) -> model.completed(completed)) }, @)
class window.TodoViewModel extends kb.ViewModel
constructor: (model, options) ->
super(model, {requires: ['title', 'completed']}, options)
@title = kb.observable(model, {
key: 'title'
write: ((title) =>
if $.trim(title) then model.save(title: $.trim(title)) else _.defer(->model.destroy())
@editing(false)
)
}, @)
@completed.subscribe((completed) => @model().save({completed}))
@edit_title = ko.observable()
@editing = ko.observable(false)
onDestroy: => @model().destroy()
onCheckEditBegin: =>
return if @editing()
@onDestroyTodo = => model.destroy()
@edit_title(@title())
@editing(true)
$('.todo-input').focus()
@onCheckEditBegin = =>
if not @editing()
@editing(true)
$('.todo-input').focus()
onCheckEditEnd: (vm, event) =>
return unless @editing()
@onCheckEditEnd = (view_model, event) =>
if (event.keyCode == ESCAPE_KEY)
if (event.keyCode is ESCAPE_KEY)
@editing(false)
if (event.keyCode == ENTER_KEY) or (event.type == 'blur')
if (event.keyCode is ENTER_KEY) or (event.type is 'blur')
$('.todo-input').blur()
title = @edit_title()
if $.trim(title) then @model().set({title}).save({title: $.trim(title)}) else _.defer(=>@model().destroy())
@editing(false)
return
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment