Commit fb246fbf authored by Sindre Sorhus's avatar Sindre Sorhus

Merge pull request #450 from passy/ts-backbone-update

Ts backbone update
parents 9040e970 81906dc8
...@@ -106,10 +106,10 @@ https://github.com/documentcloud/backbone/blob/master/examples/todos/index.html ...@@ -106,10 +106,10 @@ https://github.com/documentcloud/backbone/blob/master/examples/todos/index.html
<% } %> <% } %>
</script> </script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <script src="../../../assets/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore-min.js"></script> <script src="../../../assets/lodash.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.9/backbone-min.js"></script> <script src="js/libs/backbone/backbone.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone-localstorage.js/1.0/backbone.localStorage-min.js"></script> <script src="js/libs/backbone/backbone.localStorage.js"></script>
<script src="js/app.js"></script> <script src="js/app.js"></script>
</body> </body>
</html> </html>
...@@ -11,20 +11,20 @@ var Todo = (function (_super) { ...@@ -11,20 +11,20 @@ var Todo = (function (_super) {
} }
Todo.prototype.defaults = function () { Todo.prototype.defaults = function () {
return { return {
content: "empty todo...", content: '',
done: false done: false
}; };
}; };
Todo.prototype.initialize = function () { Todo.prototype.initialize = function () {
if(!this.get("content")) { if(!this.get('content')) {
this.set({ this.set({
"content": this.defaults().content 'content': this.defaults().content
}); });
} }
}; };
Todo.prototype.toggle = function () { Todo.prototype.toggle = function () {
this.save({ this.save({
done: !this.get("done") done: !this.get('done')
}); });
}; };
Todo.prototype.clear = function () { Todo.prototype.clear = function () {
...@@ -38,7 +38,7 @@ var TodoList = (function (_super) { ...@@ -38,7 +38,7 @@ var TodoList = (function (_super) {
_super.apply(this, arguments); _super.apply(this, arguments);
this.model = Todo; this.model = Todo;
this.localStorage = new Store("todos-backbone"); this.localStorage = new Store('todos-typescript-backbone');
} }
TodoList.prototype.done = function () { TodoList.prototype.done = function () {
return this.filter(function (todo) { return this.filter(function (todo) {
...@@ -63,13 +63,13 @@ var Todos = new TodoList(); ...@@ -63,13 +63,13 @@ var Todos = new TodoList();
var TodoView = (function (_super) { var TodoView = (function (_super) {
__extends(TodoView, _super); __extends(TodoView, _super);
function TodoView(options) { function TodoView(options) {
this.tagName = "li"; this.tagName = 'li';
this.events = { this.events = {
"click .check": "toggleDone", 'click .check': 'toggleDone',
"dblclick label.todo-content": "edit", 'dblclick label.todo-content': 'edit',
"click button.destroy": "clear", 'click button.destroy': 'clear',
"keypress .todo-input": "updateOnEnter", 'keypress .todo-input': 'updateOnEnter',
"blur .todo-input": "close" 'blur .todo-input': 'close'
}; };
_super.call(this, options); _super.call(this, options);
this.template = _.template($('#item-template').html()); this.template = _.template($('#item-template').html());
...@@ -77,6 +77,7 @@ var TodoView = (function (_super) { ...@@ -77,6 +77,7 @@ var TodoView = (function (_super) {
this.model.bind('change', this.render); this.model.bind('change', this.render);
this.model.bind('destroy', this.remove); this.model.bind('destroy', this.remove);
} }
TodoView.ENTER_KEY = 13;
TodoView.prototype.render = function () { TodoView.prototype.render = function () {
this.$el.html(this.template(this.model.toJSON())); this.$el.html(this.template(this.model.toJSON()));
this.input = this.$('.todo-input'); this.input = this.$('.todo-input');
...@@ -86,17 +87,17 @@ var TodoView = (function (_super) { ...@@ -86,17 +87,17 @@ var TodoView = (function (_super) {
this.model.toggle(); this.model.toggle();
}; };
TodoView.prototype.edit = function () { TodoView.prototype.edit = function () {
this.$el.addClass("editing"); this.$el.addClass('editing');
this.input.focus(); this.input.focus();
}; };
TodoView.prototype.close = function () { TodoView.prototype.close = function () {
this.model.save({ this.model.save({
content: this.input.val() content: this.input.val()
}); });
this.$el.removeClass("editing"); this.$el.removeClass('editing');
}; };
TodoView.prototype.updateOnEnter = function (e) { TodoView.prototype.updateOnEnter = function (e) {
if(e.keyCode == 13) { if(e.keyCode == TodoView.ENTER_KEY) {
close(); close();
} }
}; };
...@@ -110,16 +111,16 @@ var AppView = (function (_super) { ...@@ -110,16 +111,16 @@ var AppView = (function (_super) {
function AppView() { function AppView() {
_super.call(this); _super.call(this);
this.events = { this.events = {
"keypress #new-todo": "createOnEnter", 'keypress #new-todo': 'createOnEnter',
"keyup #new-todo": "showTooltip", 'click .todo-clear button': 'clearCompleted',
"click .todo-clear button": "clearCompleted", 'click .mark-all-done': 'toggleAllComplete'
"click .mark-all-done": "toggleAllComplete"
}; };
this.tooltipTimeout = null; this.setElement($('#todoapp'), true);
this.setElement($("#todoapp"), true);
_.bindAll(this, 'addOne', 'addAll', 'render', 'toggleAllComplete'); _.bindAll(this, 'addOne', 'addAll', 'render', 'toggleAllComplete');
this.input = this.$("#new-todo"); this.input = this.$('#new-todo');
this.allCheckbox = this.$(".mark-all-done")[0]; this.allCheckbox = this.$('.mark-all-done')[0];
this.mainElement = this.$('#main')[0];
this.footerElement = this.$('#footer')[0];
this.statsTemplate = _.template($('#stats-template').html()); this.statsTemplate = _.template($('#stats-template').html());
Todos.bind('add', this.addOne); Todos.bind('add', this.addOne);
Todos.bind('reset', this.addAll); Todos.bind('reset', this.addAll);
...@@ -129,18 +130,25 @@ var AppView = (function (_super) { ...@@ -129,18 +130,25 @@ var AppView = (function (_super) {
AppView.prototype.render = function () { AppView.prototype.render = function () {
var done = Todos.done().length; var done = Todos.done().length;
var remaining = Todos.remaining().length; var remaining = Todos.remaining().length;
if(Todos.length) {
this.mainElement.style.display = 'block';
this.footerElement.style.display = 'block';
this.$('#todo-stats').html(this.statsTemplate({ this.$('#todo-stats').html(this.statsTemplate({
total: Todos.length, total: Todos.length,
done: done, done: done,
remaining: remaining remaining: remaining
})); }));
} else {
this.mainElement.style.display = 'none';
this.footerElement.style.display = 'none';
}
this.allCheckbox.checked = !remaining; this.allCheckbox.checked = !remaining;
}; };
AppView.prototype.addOne = function (todo) { AppView.prototype.addOne = function (todo) {
var view = new TodoView({ var view = new TodoView({
model: todo model: todo
}); });
this.$("#todo-list").append(view.render().el); this.$('#todo-list').append(view.render().el);
}; };
AppView.prototype.addAll = function () { AppView.prototype.addAll = function () {
Todos.each(this.addOne); Todos.each(this.addOne);
...@@ -165,20 +173,6 @@ var AppView = (function (_super) { ...@@ -165,20 +173,6 @@ var AppView = (function (_super) {
}); });
return false; return false;
}; };
AppView.prototype.showTooltip = function (e) {
var tooltip = $(".ui-tooltip-top");
var val = this.input.val();
tooltip.fadeOut();
if(this.tooltipTimeout) {
clearTimeout(this.tooltipTimeout);
}
if(val == '' || val == this.input.attr('placeholder')) {
return;
}
this.tooltipTimeout = _.delay(function () {
return tooltip.show().fadeIn();
}, 1000);
};
AppView.prototype.toggleAllComplete = function () { AppView.prototype.toggleAllComplete = function () {
var done = this.allCheckbox.checked; var done = this.allCheckbox.checked;
Todos.each(function (todo) { Todos.each(function (todo) {
......
...@@ -143,7 +143,7 @@ class TodoList extends Backbone.Collection { ...@@ -143,7 +143,7 @@ class TodoList extends Backbone.Collection {
// Filter down the list to only todo items that are still not finished. // Filter down the list to only todo items that are still not finished.
remaining() { remaining() {
return this.without(this.done()); return this.without.apply(this, this.done());
} }
// We keep the Todos in sequential order, despite being saved by unordered // We keep the Todos in sequential order, despite being saved by unordered
...@@ -178,6 +178,8 @@ class TodoView extends Backbone.View { ...@@ -178,6 +178,8 @@ class TodoView extends Backbone.View {
model: Todo; model: Todo;
input: any; input: any;
static ENTER_KEY:number = 13;
constructor (options? ) { constructor (options? ) {
//... is a list tag. //... is a list tag.
this.tagName = 'li'; this.tagName = 'li';
...@@ -200,7 +202,7 @@ class TodoView extends Backbone.View { ...@@ -200,7 +202,7 @@ class TodoView extends Backbone.View {
this.model.bind('change', this.render); this.model.bind('change', this.render);
this.model.bind('destroy', this.remove); this.model.bind('destroy', this.remove);
} }
w
// Re-render the contents of the todo item. // Re-render the contents of the todo item.
render() { render() {
this.$el.html(this.template(this.model.toJSON())); this.$el.html(this.template(this.model.toJSON()));
...@@ -227,7 +229,7 @@ w ...@@ -227,7 +229,7 @@ w
// If you hit `enter`, we're through editing the item. // If you hit `enter`, we're through editing the item.
updateOnEnter(e) { updateOnEnter(e) {
if (e.keyCode == 13) close(); if (e.keyCode == TodoView.ENTER_KEY) close();
} }
// Remove the item, destroy the model. // Remove the item, destroy the model.
...@@ -252,6 +254,8 @@ class AppView extends Backbone.View { ...@@ -252,6 +254,8 @@ class AppView extends Backbone.View {
input: any; input: any;
allCheckbox: HTMLInputElement; allCheckbox: HTMLInputElement;
mainElement: HTMLElement;
footerElement: HTMLElement;
statsTemplate: (params: any) => string; statsTemplate: (params: any) => string;
constructor () { constructor () {
...@@ -267,6 +271,8 @@ class AppView extends Backbone.View { ...@@ -267,6 +271,8 @@ class AppView extends Backbone.View {
this.input = this.$('#new-todo'); this.input = this.$('#new-todo');
this.allCheckbox = this.$('.mark-all-done')[0]; this.allCheckbox = this.$('.mark-all-done')[0];
this.mainElement = this.$('#main')[0];
this.footerElement = this.$('#footer')[0];
this.statsTemplate = _.template($('#stats-template').html()); this.statsTemplate = _.template($('#stats-template').html());
Todos.bind('add', this.addOne); Todos.bind('add', this.addOne);
...@@ -282,11 +288,19 @@ class AppView extends Backbone.View { ...@@ -282,11 +288,19 @@ class AppView extends Backbone.View {
var done = Todos.done().length; var done = Todos.done().length;
var remaining = Todos.remaining().length; var remaining = Todos.remaining().length;
if (Todos.length) {
this.mainElement.style.display = 'block';
this.footerElement.style.display = 'block';
this.$('#todo-stats').html(this.statsTemplate({ this.$('#todo-stats').html(this.statsTemplate({
total: Todos.length, total: Todos.length,
done: done, done: done,
remaining: remaining remaining: remaining
})); }));
} else {
this.mainElement.style.display = 'none';
this.footerElement.style.display = 'none';
}
this.allCheckbox.checked = !remaining; this.allCheckbox.checked = !remaining;
} }
......
/**
* Backbone localStorage Adapter
* Version 1.1.0
*
* https://github.com/jeromegn/Backbone.localStorage
*/
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(["underscore", "backbone"], function (_, Backbone) {
// Use global variables if the locals are undefined.
return factory(_ || root._, Backbone || root.Backbone);
});
} else {
// RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
factory(_, Backbone);
}
}(this, function (_, Backbone) {
// A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that.
// Hold reference to Underscore.js and Backbone.js in the closure in order
// to make things work even if they are removed from the global namespace
// Generate four random hex digits.
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
};
// Generate a pseudo-GUID by concatenating random hexadecimal.
function guid() {
return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
};
// Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table.
// window.Store is deprectated, use Backbone.LocalStorage instead
Backbone.LocalStorage = window.Store = function (name) {
this.name = name;
var store = this.localStorage().getItem(this.name);
this.records = (store && store.split(",")) || [];
};
_.extend(Backbone.LocalStorage.prototype, {
// Save the current state of the **Store** to *localStorage*.
save: function () {
this.localStorage().setItem(this.name, this.records.join(","));
},
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
// have an id of it's own.
create: function (model) {
if (!model.id) {
model.id = guid();
model.set(model.idAttribute, model.id);
}
this.localStorage().setItem(this.name + "-" + model.id, JSON.stringify(model));
this.records.push(model.id.toString());
this.save();
return this.find(model);
},
// Update a model by replacing its copy in `this.data`.
update: function (model) {
this.localStorage().setItem(this.name + "-" + model.id, JSON.stringify(model));
if (!_.include(this.records, model.id.toString()))
this.records.push(model.id.toString()); this.save();
return this.find(model);
},
// Retrieve a model from `this.data` by id.
find: function (model) {
return this.jsonData(this.localStorage().getItem(this.name + "-" + model.id));
},
// Return the array of all models currently in storage.
findAll: function () {
return _(this.records).chain()
.map(function (id) {
return this.jsonData(this.localStorage().getItem(this.name + "-" + id));
}, this)
.compact()
.value();
},
// Delete a model from `this.data`, returning it.
destroy: function (model) {
if (model.isNew())
return false
this.localStorage().removeItem(this.name + "-" + model.id);
this.records = _.reject(this.records, function (id) {
return id === model.id.toString();
});
this.save();
return model;
},
localStorage: function () {
return localStorage;
},
// fix for "illegal access" error on Android when JSON.parse is passed null
jsonData: function (data) {
return data && JSON.parse(data);
}
});
// localSync delegate to the model or collection's
// *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function (method, model, options) {
var store = model.localStorage || model.collection.localStorage;
var resp, errorMessage, syncDfd = $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
try {
switch (method) {
case "read":
resp = model.id != undefined ? store.find(model) : store.findAll();
break;
case "create":
resp = store.create(model);
break;
case "update":
resp = store.update(model);
break;
case "delete":
resp = store.destroy(model);
break;
}
} catch (error) {
if (error.code === DOMException.QUOTA_EXCEEDED_ERR && window.localStorage.length === 0)
errorMessage = "Private browsing is unsupported";
else
errorMessage = error.message;
}
if (resp) {
model.trigger("sync", model, resp, options);
if (options && options.success)
if (Backbone.VERSION === "0.9.10") {
options.success(model, resp, options);
} else {
options.success(resp);
}
if (syncDfd)
syncDfd.resolve(resp);
} else {
errorMessage = errorMessage ? errorMessage
: "Record Not Found";
model.trigger("error", model, errorMessage, options);
if (options && options.error)
if (Backbone.VERSION === "0.9.10") {
options.error(model, errorMessage, options);
} else {
options.error(errorMessage);
}
if (syncDfd)
syncDfd.reject(errorMessage);
}
// add compatibility with $.ajax
// always execute callback for success and error
if (options && options.complete) options.complete(resp);
return syncDfd && syncDfd.promise();
};
Backbone.ajaxSync = Backbone.sync;
Backbone.getSyncMethod = function (model) {
if (model.localStorage || (model.collection && model.collection.localStorage)) {
return Backbone.localSync;
}
return Backbone.ajaxSync;
};
// Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone.sync = function (method, model, options) {
return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
};
return Backbone.LocalStorage;
}));
\ 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