|
<html doctype="html"> |
|
<head> |
|
<script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script> |
|
<script type="text/javascript"> |
|
var jjj = { |
|
Storage: (function() { |
|
var storage = window.localStorage, |
|
store = function(namespace, obj, callback) { |
|
retrieveAll(namespace, function(list) { |
|
obj = $.extend(obj, { |
|
id: obj.id || (new Date).getTime() |
|
}); |
|
list[obj.id] = obj; |
|
storage.setItem(namespace, JSON.stringify(list)); |
|
if (typeof callback === "function") { |
|
callback(obj); |
|
} |
|
}); |
|
}, |
|
retrieve = function(namespace, id, callback) { |
|
retrieveAll(namespace, function(objects) { |
|
callback(objects[id]) |
|
}); |
|
}, |
|
retrieveAll = function(namespace, callback) { |
|
var json = JSON.parse(storage.getItem(namespace) || "{}"), |
|
all = []; |
|
for (key in json) { if (json.hasOwnProperty(key)) { |
|
all.push(json[key]); |
|
}} |
|
callback(all); |
|
}, |
|
remove = function(namespace, id, callback) { |
|
retrieveAll(namespace, function(list) { |
|
delete list[id]; |
|
storage.setItem(namespace, JSON.stringify(list)); |
|
callback(); |
|
}); |
|
}, |
|
sync = function(method, model, options) { |
|
switch(method) { |
|
case "read": |
|
if(model.id) { |
|
retrieve(model.getClass().url, model.id, options.success); |
|
} else { |
|
retrieveAll(model.url, options.success); |
|
} |
|
break; |
|
case "create": |
|
store(model.getClass().url, model.toJSON(), options.success); |
|
break; |
|
case "update": |
|
store(model.getClass().url, model.toJSON(), options.success); |
|
break; |
|
case "delete": |
|
remove(model.getClass().url, model.id, options.success); |
|
break; |
|
} |
|
}; |
|
return { |
|
sync: sync |
|
} |
|
})(), |
|
Model: function(options) { |
|
var defaults = { |
|
url: "", |
|
instanceMembers: {}, |
|
classMembers: {} |
|
}, |
|
preprocessedSuccess = function(success, preprocessor) { |
|
return function(resp, status, xhr) { |
|
if (success) { |
|
success(preprocessor(resp), status, xhr) |
|
}; |
|
} |
|
}, |
|
inflateAll = function(attrs) { |
|
for (var i in attrs) { if (attrs.hasOwnProperty(i)) { |
|
attrs[i] = inflate(attrs[i]); |
|
}} |
|
return attrs; |
|
}, |
|
inflate = function(attr) { |
|
return $.extend(attr, instanceMembers, options.instanceMembers); |
|
}, |
|
instanceMembers = { |
|
toJSON: function() { |
|
return this; |
|
}, |
|
isNew: function() { |
|
return typeof this.id === null; |
|
}, |
|
url: function() { |
|
var base = this.getClass().url || ""; |
|
if (this.isNew()) return base; |
|
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); |
|
}, |
|
getClass: function() { |
|
return model; |
|
}, |
|
save: function(opts) { |
|
opts = opts || {}; |
|
jjj.Storage.sync(this.isNew() ? "create" : "update", this, $.extend(opts, { |
|
success: preprocessedSuccess(opts.success, inflate) |
|
})); |
|
}, |
|
destroy: function(opts) { |
|
opts = opts || {}; |
|
jjj.Storage.sync("delete", this, opts); |
|
} |
|
}, |
|
classMembers = { |
|
inflate: inflate, |
|
inflateAll: inflateAll, |
|
url: options.url || "", |
|
toJSON: function() { |
|
return {}; |
|
}, |
|
create: function(params, opts) { |
|
opts = opts || {}; |
|
jjj.Storage.sync("create", inflate(params), $.extend(opts, { |
|
success: preprocessedSuccess(opts.success, inflate) |
|
})); |
|
}, |
|
all: function(opts) { |
|
opts = opts || {}; |
|
jjj.Storage.sync("read", this, $.extend(opts, { |
|
success: preprocessedSuccess(opts.success, inflateAll) |
|
})); |
|
}, |
|
find: function(id, opts) { |
|
opts = opts || {}; |
|
jjj.Storage.sync("read", inflate({ id: id }), $.extend(opts, { |
|
success: preprocessedSuccess(opts.success, inflate) |
|
})); |
|
} |
|
}, |
|
model = function(obj) { |
|
return inflate(obj); |
|
}; |
|
options = $.extend(defaults, options); |
|
$.extend(model, classMembers, options.classMembers); |
|
return model; |
|
} |
|
}, |
|
app = { |
|
init: function() { |
|
app.Note = jjj.Model({ |
|
url: "notes", |
|
instanceMembers: { |
|
render: function() { |
|
this.el = $("<li>").html($("<span>").text(this.text)); |
|
var self = this, |
|
deleteBtn = $("<a class='delete' href='#'>delete</a>").click(function(){ |
|
self.destroy({ |
|
success: function(){ |
|
self.el.remove(); |
|
} |
|
}); |
|
return false; |
|
}); |
|
return this.el.append(deleteBtn); |
|
} |
|
} |
|
}); |
|
var notes = app.Note.all({ |
|
success: function(notes) { |
|
for (var n in notes) { if (notes.hasOwnProperty(n)) { |
|
$("#notes").append(notes[n].render()); |
|
}} |
|
$("#note_input").keyup(function(evt) { |
|
var $this = $(this); |
|
if(evt.which === 13) { |
|
app.Note.create({ |
|
text: $this.val() |
|
}, { |
|
success: function(note) { |
|
$("#notes").append(note.render()); |
|
} |
|
}); |
|
$this.val(""); |
|
} |
|
}); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
$(document).ready(app.init); |
|
</script> |
|
<style type="text/css"> |
|
body { |
|
font-family: Helvetica, sans-serif; |
|
} |
|
#wrap { |
|
width: 300px; |
|
margin: 0 auto; |
|
border: 1px solid #ddd; |
|
border-radius: 4px; |
|
background: #eee; |
|
padding: 20px; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3), inset 0 -1px 5px rgba(0, 0, 0, 0.1); |
|
} |
|
#content header { |
|
margin-bottom: 20px; |
|
} |
|
#content header input { |
|
font-size: 16px; |
|
width: 100%; |
|
border: 1px solid #ddd; |
|
border-radius: 3px; |
|
background: #fafafa; |
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5); |
|
padding: 4px; |
|
} |
|
#content section ul { |
|
font-size: 16px; |
|
border: 1px solid #ddd; |
|
border-radius: 3px; |
|
list-style: none; |
|
padding: 0; |
|
margin: 0; |
|
} |
|
#content section ul li { |
|
padding: 10px; |
|
border-bottom: 1px solid #ddd; |
|
margin: 0; |
|
display: block; |
|
} |
|
#content section ul li:last-child { |
|
border: none; |
|
} |
|
#content section ul li .delete { |
|
display: inline-block; |
|
float: right; |
|
color: #09f; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="wrap"> |
|
<div id="content"> |
|
<header> |
|
<input type="text" id="note_input" placeholder="Your note here..." /> |
|
</header> |
|
<section> |
|
<ul id="notes"></ul> |
|
</section> |
|
</div> |
|
</div> |
|
</body> |
|
</html> |