Commit 488ddc31 authored by Fred Wu's avatar Fred Wu

Replaced the old Spine implementation

parent 56a09f6f
html, body {
margin: 0;
padding: 0;
}
body {
font-family: "Helvetica Neue", helvetica, arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}
#views {
width: 520px;
margin: 0 auto 40px auto;
background: white;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-moz-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
-webkit-border-radius: 0 0 5px 5px;
border-radius: 0 0 5px 5px;
}
#tasks {
padding: 20px;
}
#tasks h1 {
font-size: 36px;
font-weight: bold;
text-align: center;
padding: 0 0 10px 0;
}
#tasks input[type="text"] {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
}
#tasks input::-webkit-input-placeholder {
font-style: italic;
}
#tasks .items {
margin: 10px 0;
list-style: none;
}
#tasks .item {
padding: 15px 20px 15px 0;
position: relative;
font-size: 24px;
border-bottom: 1px solid #cccccc;
}
#tasks .item.done span {
color: #777777;
text-decoration: line-through;
}
#tasks .item .destroy {
position: absolute;
right: 10px;
top: 16px;
display: none;
cursor: pointer;
width: 20px;
height: 20px;
background: url(../images/destroy.png) no-repeat center center;
}
#tasks .item:hover .destroy {
display: block;
}
#tasks .item .edit { display: none; }
#tasks .item.editing .edit { display: block; }
#tasks .item.editing .view { display: none; }
#tasks footer {
display: block;
margin: 20px -20px -20px -20px;
overflow: hidden;
color: #555555;
background: #f4fce8;
border-top: 1px solid #ededed;
padding: 0 20px;
line-height: 36px;
-moz-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
-webkit-border-radius: 0 0 5px 5px;
border-radius: 0 0 5px 5px;
}
#tasks .clear {
display: block;
float: right;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
color: #555555;
font-size: 11px;
margin-top: 8px;
margin-bottom:8px;
padding: 0 10px 1px;
-moz-border-radius: 12px;
-webkit-border-radius: 12px;
-o-border-radius: 12px;
border-radius: 12px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
cursor: pointer;
}
#tasks .clear:hover {
background: rgba(0, 0, 0, 0.15);
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
}
#tasks .clear:active {
position: relative;
top: 1px;
}
#tasks .count span {
font-weight: bold;
}
#credits {
width: 520px;
margin: 30px auto;
color: #999;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#credits a {
color: #888;
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>Spine.js</title>
<link rel="stylesheet" href="css/application.css" type="text/css" charset="utf-8">
<script src="lib/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/jquery.tmpl.min.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/spine.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/local.js" type="text/javascript" charset="utf-8"></script>
<script src="js/controllers/tasks.js" type="text/javascript" charset="utf-8"></script>
<script src="js/models/task.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="views">
<div id="tasks">
<h1>Todos</h1>
<form id="new-task">
<input name="name" type="text" placeholder="What needs to be done?">
</form>
<div class="items">
<script type="text/html" id="task-template">
<div class="item {{if done}}done{{/if}}">
<div class="view" title="Double click to edit...">
<input type="checkbox" {{if done}}checked="checked"{{/if}}>
<span>${name}</span> <a class="destroy"></a>
</div>
<form class="edit">
<input type="text" name="name" value="${name}">
</form>
</div>
</script>
</div>
<footer>
<a class="clear">Clear completed</a>
<div class="count"><span class="countVal"></span> left</div>
</footer>
</div>
</div>
<div id="credits">
Based on the official <a href="https://github.com/maccman/spine.todos">Spine.Todos</a>.
</div>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
jQuery(function($){
window.Tasks = Spine.Controller.create({
tag: "li",
proxied: ["render", "remove"],
events: {
"change input[type=checkbox]": "toggle",
"click .destroy": "destroy",
"dblclick .view": "edit",
"keypress input[type=text]": "blurOnEnter",
"blur input[type=text]": "close"
},
elements: {
"input[type=text]": "input",
".item": "wrapper"
},
init: function(){
this.item.bind("update", this.render);
this.item.bind("destroy", this.remove);
},
render: function(){
var elements = $("#taskTemplate").tmpl(this.item);
this.el.html(elements);
this.refreshElements();
return this;
},
toggle: function(){
this.item.done = !this.item.done;
this.item.save();
},
destroy: function(){
this.item.destroy();
},
edit: function(){
this.wrapper.addClass("editing");
this.input.focus();
},
blurOnEnter: function(e) {
if (e.keyCode == 13) e.target.blur();
},
close: function(){
this.wrapper.removeClass("editing");
this.item.updateAttributes({name: this.input.val()});
},
remove: function(){
this.el.remove();
}
});
window.TaskApp = Spine.Controller.create({
el: $("#tasks"),
proxied: ["addOne", "addAll", "renderCount"],
events: {
"submit form": "create",
"click .clear": "clear"
},
elements: {
".items": "items",
".countVal": "count",
".clear": "clear",
"form input": "input"
},
init: function(){
Task.bind("create", this.addOne);
Task.bind("refresh", this.addAll);
Task.bind("refresh change", this.renderCount);
Task.fetch();
},
addOne: function(task) {
var view = Tasks.init({item: task});
this.items.append(view.render().el);
},
addAll: function() {
Task.each(this.addOne);
},
create: function(){
Task.create({name: this.input.val()});
this.input.val("");
return false;
},
clear: function(){
Task.destroyDone();
},
renderCount: function(){
var active = Task.active().length;
this.count.text(active + ' left');
var inactive = Task.done().length;
this.clear[inactive ? "show" : "hide"]();
}
});
window.App = TaskApp.init();
});
\ No newline at end of file
// Create the Task model.
var Task = Spine.Model.setup("Task", ["name", "done"]);
// Persist model between page reloads.
Task.extend(Spine.Model.Local);
Task.extend({
// Return all active tasks.
active: function(){
return(this.select(function(item){ return !item.done; }));
},
// Return all done tasks.
done: function(){
return(this.select(function(item){ return !!item.done; }));
},
// Clear all done tasks.
destroyDone: function(){
jQuery(this.done()).each(function(i, rec){ rec.destroy(); });
}
});
\ No newline at end of file
......@@ -15,12 +15,12 @@ body {
width: 520px;
margin: 0 auto 40px auto;
background: white;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-moz-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
-webkit-border-radius: 0 0 5px 5px;
......@@ -47,7 +47,7 @@ body {
outline: none;
padding: 6px;
border: 1px solid #999999;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
......@@ -98,13 +98,13 @@ body {
display: block;
margin: 20px -20px -20px -20px;
overflow: hidden;
color: #555555;
background: #f4fce8;
border-top: 1px solid #ededed;
padding: 0 20px;
line-height: 36px;
-moz-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
-webkit-border-radius: 0 0 5px 5px;
......@@ -116,24 +116,24 @@ body {
float: right;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
color: #555555;
font-size: 11px;
margin-top: 8px;
margin-bottom:8px;
padding: 0 10px 1px;
-moz-border-radius: 12px;
-webkit-border-radius: 12px;
-o-border-radius: 12px;
border-radius: 12px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
cursor: pointer;
}
......@@ -152,4 +152,16 @@ body {
#tasks .count span {
font-weight: bold;
}
#credits {
width: 520px;
margin: 30px auto;
color: #999;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#credits a {
color: #888;
}
\ No newline at end of file
......@@ -4,45 +4,48 @@
<title>Spine.js</title>
<link rel="stylesheet" href="css/application.css" type="text/css" charset="utf-8">
<script src="lib/json2.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/jquery.tmpl.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/jquery.tmpl.min.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/spine.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/spine.model.local.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/local.js" type="text/javascript" charset="utf-8"></script>
<script src="app/models/task.js" type="text/javascript" charset="utf-8"></script>
<script src="app/application.js" type="text/javascript" charset="utf-8"></script>
<script type="text/x-jquery-tmpl" id="taskTemplate">
<div class="item {{if done}}done{{/if}}">
<div class="view" title="Double click to edit...">
<input type="checkbox" {{if done}}checked="checked"{{/if}}>
<span>${name}</span> <a class="destroy"></a>
</div>
<div class="edit">
<input type="text" value="${name}">
</div>
</div>
</script>
<script src="js/controllers/tasks.js" type="text/javascript" charset="utf-8"></script>
<script src="js/models/task.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="views">
<div id="tasks">
<h1>Todos</h1>
<form>
<input type="text" placeholder="What needs to be done?">
<form id="new-task">
<input name="name" type="text" placeholder="What needs to be done?">
</form>
<div class="items"></div>
<div class="items">
<script type="text/html" id="task-template">
<div class="item {{if done}}done{{/if}}">
<div class="view" title="Double click to edit...">
<input type="checkbox" {{if done}}checked="checked"{{/if}}>
<span>${name}</span> <a class="destroy"></a>
</div>
<form class="edit">
<input type="text" name="name" value="${name}">
</form>
</div>
</script>
</div>
<footer>
<a class="clear">Clear completed</a>
<div class="count"><span class="countVal"></span></div>
<div class="count"><span class="countVal"></span> left</div>
</footer>
</div>
</div>
<div id="credits">
Based on the official <a href="https://github.com/maccman/spine.todos">Spine.Todos</a>.
</div>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
(function(Spine, $){
var Model = Spine.Model;
var getUrl = function(object){
if (!(object && object.url)) return null;
return((typeof object.url == "function") ? object.url() : object.url);
};
var methodMap = {
"create": "POST",
"update": "PUT",
"destroy": "DELETE",
"read": "GET"
};
var urlError = function() {
throw new Error("A 'url' property or function must be specified");
};
var ajaxSync = function(method, record){
if (Model._noSync) return;
var params = {
type: methodMap[method],
contentType: "application/json",
dataType: "json",
processData: false
};
if (method == "create" && record.model)
params.url = getUrl(record.parent);
else
params.url = getUrl(record);
if (!params.url) throw("Invalid URL");
if (method == "create" || method == "update") {
var data = {};
if (Model.ajaxPrefix) {
var prefix = record.parent.name.toLowerCase();
data[prefix] = record;
} else {
data = record;
}
params.data = JSON.stringify(data);
}
if (method == "read")
params.success = function(data){
(record.refresh || record.load).call(record, data);
};
params.error = function(xhr, s, e){
record.trigger("ajaxError", xhr, s, e);
};
$.ajax(params);
};
Model.Ajax = {
extended: function(){
this.sync(ajaxSync);
this.fetch(this.proxy(function(){
ajaxSync("read", this);
}));
}
};
Model.extend({
ajaxPrefix: false,
url: function() {
return "/" + this.name.toLowerCase() + "s"
},
noSync: function(callback){
Model._noSync = true;
callback.apply(callback, arguments);
Model._noSync = false;
}
});
Model.include({
url: function(){
var base = getUrl(this.parent);
base += (base.charAt(base.length - 1) == "/" ? "" : "/");
base += encodeURIComponent(this.id);
return base;
}
});
})(Spine, Spine.$);
\ No newline at end of file
Spine.Model.Local = {
extended: function(){
this.sync(this.proxy(this.saveLocal));
this.fetch(this.proxy(this.loadLocal));
},
saveLocal: function(){
var result = JSON.stringify(this);
localStorage[this.name] = result;
},
loadLocal: function(){
var result = localStorage[this.name];
if ( !result ) return;
var result = JSON.parse(result);
this.refresh(result);
}
};
\ 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