Commit fb3a233b authored by Ryan Niemeyer's avatar Ryan Niemeyer

Move app code to separate js file, add local storage via amplify, and actually add knockout 2.0.0.

parent 994ebff5
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
<head> <head>
<title>Knockout.js</title> <title>Knockout.js</title>
<link href="css/todos.css" media="all" rel="stylesheet" type="text/css" /> <link href="css/todos.css" media="all" rel="stylesheet" type="text/css" />
<script src="js/knockout-2.0.0.js" type="text/javascript"></script>
</head> </head>
<body> <body>
<div id="todoapp"> <div id="todoapp">
...@@ -66,92 +65,13 @@ ...@@ -66,92 +65,13 @@
<a href="http://twitter.com/addyosmani">Addy Osmani</a> <a href="http://twitter.com/addyosmani">Addy Osmani</a>
</div> </div>
<script type="text/javascript"> <!-- Knockout has no direct dependencies -->
(function() { <script src="js/knockout-2.0.0.js" type="text/javascript"></script>
//represent a single todo item <!-- needed to support JSON.stringify in older browsers (for local storage) -->
var Todo = function (text) { <script src="js/json2.js" type="text/javascript"></script>
this.content = ko.observable(text); <!-- used for local storage -->
this.done = ko.observable(false); <script src="js/amplify.store.min.js" type="text/javascript"></script>
this.editing = ko.observable(false); <!-- our app code -->
}; <script src="js/todos.js" type="text/javascript"></script>
//can place methods on prototype, as there can be many todos
ko.utils.extend(Todo.prototype, {
edit: function() { this.editing(true); },
stopEditing: function() { this.editing(false); }
});
//our main view model
var ViewModel = function() {
var self = this;
this.todos = ko.observableArray();
this.current = ko.observable();
//add a new todo, when enter key is pressed
this.add = function (data, event) {
var newTodo = new Todo(self.current());
self.todos.push(newTodo);
self.current("");
};
//remove a single todo
this.remove = function (todo) {
self.todos.remove(todo);
};
//remove all completed todos
this.removeCompleted = function () {
self.todos.remove(function(todo) {
return todo.done();
});
};
//count of all completed todos
this.completedCount = ko.computed(function () {
return ko.utils.arrayFilter(self.todos(), function(todo) {
return todo.done();
}).length;
});
//count of todos that are not complete
this.remainingCount = ko.computed(function () {
return self.todos().length - self.completedCount();
});
//helper function to keep expressions out of markup
this.getLabel = function(count) {
return ko.utils.unwrapObservable(count) === 1 ? "item" : "items";
};
};
//a custom binding to handle the enter key (could go in a separate library)
ko.bindingHandlers.enterKey = {
init: function(element, valueAccessor, allBindingsAccessor, data) {
var wrappedHandler, newValueAccessor;
//wrap the handler with a check for the enter key
wrappedHandler = function(data, event) {
if (event.keyCode === 13) {
valueAccessor().call(this, data, event);
}
};
//create a valueAccessor with the options that we would want to pass to the event binding
newValueAccessor = function() {
return { keyup: wrappedHandler };
};
//call the real event binding's init function
ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, data);
}
};
//bind a new instance of our view model to the page
ko.applyBindings(new ViewModel());
})();
</script>
</body> </body>
</html> </html>
\ No newline at end of file
/*!
* Amplify Store - Persistent Client-Side Storage 1.1.0
*
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
(function(a,b){function e(a,e){c.addType(a,function(f,g,h){var i,j,k,l,m=g,n=(new Date).getTime();if(!f){m={},l=[],k=0;try{f=e.length;while(f=e.key(k++))d.test(f)&&(j=JSON.parse(e.getItem(f)),j.expires&&j.expires<=n?l.push(f):m[f.replace(d,"")]=j.data);while(f=l.pop())e.removeItem(f)}catch(o){}return m}f="__amplify__"+f;if(g===b){i=e.getItem(f),j=i?JSON.parse(i):{expires:-1};if(j.expires&&j.expires<=n)e.removeItem(f);else return j.data}else if(g===null)e.removeItem(f);else{j=JSON.stringify({data:g,expires:h.expires?n+h.expires:null});try{e.setItem(f,j)}catch(o){c[a]();try{e.setItem(f,j)}catch(o){throw c.error()}}}return m})}var c=a.store=function(a,b,d,e){var e=c.type;d&&d.type&&d.type in c.types&&(e=d.type);return c.types[e](a,b,d||{})};c.types={},c.type=null,c.addType=function(a,b){c.type||(c.type=a),c.types[a]=b,c[a]=function(b,d,e){e=e||{},e.type=a;return c(b,d,e)}},c.error=function(){return"amplify.store quota exceeded"};var d=/^__amplify__/;for(var f in{localStorage:1,sessionStorage:1})try{window[f].getItem&&e(f,window[f])}catch(g){}if(window.globalStorage)try{e("globalStorage",window.globalStorage[window.location.hostname]),c.type==="sessionStorage"&&(c.type="globalStorage")}catch(g){}(function(){if(!c.types.localStorage){var a=document.createElement("div"),d="amplify";a.style.display="none",document.getElementsByTagName("head")[0].appendChild(a);try{a.addBehavior("#default#userdata"),a.load(d)}catch(e){a.parentNode.removeChild(a);return}c.addType("userData",function(e,f,g){a.load(d);var h,i,j,k,l,m=f,n=(new Date).getTime();if(!e){m={},l=[],k=0;while(h=a.XMLDocument.documentElement.attributes[k++])i=JSON.parse(h.value),i.expires&&i.expires<=n?l.push(h.name):m[h.name]=i.data;while(e=l.pop())a.removeAttribute(e);a.save(d);return m}e=e.replace(/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g,"-");if(f===b){h=a.getAttribute(e),i=h?JSON.parse(h):{expires:-1};if(i.expires&&i.expires<=n)a.removeAttribute(e);else return i.data}else f===null?a.removeAttribute(e):(j=a.getAttribute(e),i=JSON.stringify({data:f,expires:g.expires?n+g.expires:null}),a.setAttribute(e,i));try{a.save(d)}catch(o){j===null?a.removeAttribute(e):a.setAttribute(e,j),c.userData();try{a.setAttribute(e,i),a.save(d)}catch(o){j===null?a.removeAttribute(e):a.setAttribute(e,j);throw c.error()}}return m})}})(),function(){function e(a){return a===b?b:JSON.parse(JSON.stringify(a))}var a={},d={};c.addType("memory",function(c,f,g){if(!c)return e(a);if(f===b)return e(a[c]);d[c]&&(clearTimeout(d[c]),delete d[c]);if(f===null){delete a[c];return null}a[c]=f,g.expires&&(d[c]=setTimeout(function(){delete a[c],delete d[c]},g.expires));return f})}()})(this.amplify=this.amplify||{})
\ No newline at end of file
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
(function() {
//a custom binding to handle the enter key (could go in a separate library)
ko.bindingHandlers.enterKey = {
init: function(element, valueAccessor, allBindingsAccessor, data) {
var wrappedHandler, newValueAccessor;
//wrap the handler with a check for the enter key
wrappedHandler = function(data, event) {
if (event.keyCode === 13) {
valueAccessor().call(this, data, event);
}
};
//create a valueAccessor with the options that we would want to pass to the event binding
newValueAccessor = function() {
return { keyup: wrappedHandler };
};
//call the real event binding's init function
ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, data);
}
};
//represent a single todo item
var Todo = function (content, done) {
this.content = ko.observable(content);
this.done = ko.observable(done);
this.editing = ko.observable(false);
};
//can place methods on prototype, as there can be many todos
ko.utils.extend(Todo.prototype, {
edit: function() { this.editing(true); },
stopEditing: function() { this.editing(false); }
});
//our main view model
var ViewModel = function(todos) {
var self = this;
//map array of passed in todos to an observableArray of Todo objects
this.todos = ko.observableArray(ko.utils.arrayMap(todos, function(todo) {
return new Todo(todo.content, todo.done);
}));
//store the new todo value being entered
this.current = ko.observable();
//add a new todo, when enter key is pressed
this.add = function (data, event) {
var newTodo = new Todo(self.current());
self.todos.push(newTodo);
self.current("");
};
//remove a single todo
this.remove = function (todo) {
self.todos.remove(todo);
};
//remove all completed todos
this.removeCompleted = function () {
self.todos.remove(function(todo) {
return todo.done();
});
};
//count of all completed todos
this.completedCount = ko.computed(function () {
return ko.utils.arrayFilter(self.todos(), function(todo) {
return todo.done();
}).length;
});
//count of todos that are not complete
this.remainingCount = ko.computed(function () {
return self.todos().length - self.completedCount();
});
//helper function to keep expressions out of markup
this.getLabel = function(count) {
return ko.utils.unwrapObservable(count) === 1 ? "item" : "items";
};
//computed observable that fires whenever anything changes in our todos
this.isDirty = ko.computed(function() {
//get a clean copy of the todos, which also creates a dependency on the observableArray and all observables in each item
var todos = ko.toJS(self.todos);
//store to local storage
amplify.store("todos-knockout", todos);
}).extend({ throttle: 1000 }); //save at most once per second
};
//check local storage for todos
var todos = amplify.store("todos-knockout");
//bind a new instance of our view model to the page
ko.applyBindings(new ViewModel(todos || []));
})();
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