| /*global Backbone, jQuery, _, ENTER_KEY, ESC_KEY */ |
| var app = app || {}; |
| |
| (function ($) { |
| 'use strict'; |
| |
| // Todo Item View |
| // -------------- |
| |
| // The DOM element for a todo item... |
| app.TodoView = Backbone.View.extend({ |
| //... is a list tag. |
| tagName: 'li', |
| |
| // Cache the template function for a single item. |
| template: _.template($('#item-template').html()), |
| |
| // The DOM events specific to an item. |
| events: { |
| 'click .toggle': 'toggleCompleted', |
| 'dblclick label': 'edit', |
| 'click .destroy': 'clear', |
| 'keypress .edit': 'updateOnEnter', |
| 'keydown .edit': 'revertOnEscape', |
| 'blur .edit': 'close' |
| }, |
| |
| // The TodoView listens for changes to its model, re-rendering. Since |
| // there's a one-to-one correspondence between a **Todo** and a |
| // **TodoView** in this app, we set a direct reference on the model for |
| // convenience. |
| initialize: function () { |
| this.listenTo(this.model, 'change', this.render); |
| this.listenTo(this.model, 'destroy', this.remove); |
| this.listenTo(this.model, 'visible', this.toggleVisible); |
| }, |
| |
| // Re-render the titles of the todo item. |
| render: function () { |
| // Backbone LocalStorage is adding `id` attribute instantly after |
| // creating a model. This causes our TodoView to render twice. Once |
| // after creating a model and once on `id` change. We want to |
| // filter out the second redundant render, which is caused by this |
| // `id` change. It's known Backbone LocalStorage bug, therefore |
| // we've to create a workaround. |
| // https://github.com/tastejs/todomvc/issues/469 |
| if (this.model.changed.id !== undefined) { |
| return; |
| } |
| |
| this.$el.html(this.template(this.model.toJSON())); |
| this.$el.toggleClass('completed', this.model.get('completed')); |
| this.toggleVisible(); |
| this.$input = this.$('.edit'); |
| return this; |
| }, |
| |
| toggleVisible: function () { |
| this.$el.toggleClass('hidden', this.isHidden()); |
| }, |
| |
| isHidden: function () { |
| return this.model.get('completed') ? |
| app.TodoFilter === 'active' : |
| app.TodoFilter === 'completed'; |
| }, |
| |
| // Toggle the `"completed"` state of the model. |
| toggleCompleted: function () { |
| this.model.toggle(); |
| }, |
| |
| // Switch this view into `"editing"` mode, displaying the input field. |
| edit: function () { |
| this.$el.addClass('editing'); |
| this.$input.focus(); |
| }, |
| |
| // Close the `"editing"` mode, saving changes to the todo. |
| close: function () { |
| var value = this.$input.val(); |
| var trimmedValue = value.trim(); |
| |
| // We don't want to handle blur events from an item that is no |
| // longer being edited. Relying on the CSS class here has the |
| // benefit of us not having to maintain state in the DOM and the |
| // JavaScript logic. |
| if (!this.$el.hasClass('editing')) { |
| return; |
| } |
| |
| if (trimmedValue) { |
| this.model.save({ title: trimmedValue }); |
| } else { |
| this.clear(); |
| } |
| |
| this.$el.removeClass('editing'); |
| }, |
| |
| // If you hit `enter`, we're through editing the item. |
| updateOnEnter: function (e) { |
| if (e.which === ENTER_KEY) { |
| this.close(); |
| } |
| }, |
| |
| // If you're pressing `escape` we revert your change by simply leaving |
| // the `editing` state. |
| revertOnEscape: function (e) { |
| if (e.which === ESC_KEY) { |
| this.$el.removeClass('editing'); |
| // Also reset the hidden input back to the original value. |
| this.$input.val(this.model.get('title')); |
| } |
| }, |
| |
| // Remove the item, destroy the model from *localStorage* and delete its view. |
| clear: function () { |
| this.model.destroy(); |
| } |
| }); |
| })(jQuery); |