Commit 84c47397 authored by Stephen Sawchuk's avatar Stephen Sawchuk

(lavaca) code style.

parent 098c0150
...@@ -14,7 +14,6 @@ button { ...@@ -14,7 +14,6 @@ button {
font-family: inherit; font-family: inherit;
color: inherit; color: inherit;
-webkit-appearance: none; -webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none; -ms-appearance: none;
-o-appearance: none; -o-appearance: none;
appearance: none; appearance: none;
...@@ -34,6 +33,11 @@ body { ...@@ -34,6 +33,11 @@ body {
font-smoothing: antialiased; font-smoothing: antialiased;
} }
button,
input[type="checkbox"] {
outline: none;
}
#todoapp { #todoapp {
background: #fff; background: #fff;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
...@@ -61,7 +65,7 @@ body { ...@@ -61,7 +65,7 @@ body {
font-style: italic; font-style: italic;
} }
#todoapp input:-moz-placeholder { #todoapp input::-moz-placeholder {
font-style: italic; font-style: italic;
color: #a9a9a9; color: #a9a9a9;
} }
...@@ -100,9 +104,6 @@ body { ...@@ -100,9 +104,6 @@ body {
background: #8d7d77; background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px; border-top-left-radius: 1px;
...@@ -123,7 +124,6 @@ body { ...@@ -123,7 +124,6 @@ body {
padding: 6px; padding: 6px;
border: 1px solid #999; border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-ms-box-sizing: border-box; -ms-box-sizing: border-box;
-o-box-sizing: border-box; -o-box-sizing: border-box;
...@@ -159,7 +159,8 @@ label[for='toggle-all'] { ...@@ -159,7 +159,8 @@ label[for='toggle-all'] {
left: -4px; left: -4px;
width: 40px; width: 40px;
text-align: center; text-align: center;
border: none; /* Mobile Safari */ /* Mobile Safari */
border: none;
} }
#toggle-all:before { #toggle-all:before {
...@@ -214,9 +215,9 @@ label[for='toggle-all'] { ...@@ -214,9 +215,9 @@ label[for='toggle-all'] {
top: 0; top: 0;
bottom: 0; bottom: 0;
margin: auto 0; margin: auto 0;
border: none; /* Mobile Safari */ /* Mobile Safari */
border: none;
-webkit-appearance: none; -webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none; -ms-appearance: none;
-o-appearance: none; -o-appearance: none;
appearance: none; appearance: none;
...@@ -224,7 +225,8 @@ label[for='toggle-all'] { ...@@ -224,7 +225,8 @@ label[for='toggle-all'] {
#todo-list li .toggle:after { #todo-list li .toggle:after {
content: '✔'; content: '✔';
line-height: 43px; /* 40 + a couple of pixels visual adjustment */ /* 40 + a couple of pixels visual adjustment */
line-height: 43px;
font-size: 20px; font-size: 20px;
color: #d9d9d9; color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf; text-shadow: 0 -1px 0 #bfbfbf;
...@@ -238,15 +240,13 @@ label[for='toggle-all'] { ...@@ -238,15 +240,13 @@ label[for='toggle-all'] {
} }
#todo-list li label { #todo-list li label {
white-space: pre;
word-break: break-word; word-break: break-word;
padding: 15px; padding: 15px 60px 15px 15px;
margin-left: 45px; margin-left: 45px;
display: block; display: block;
line-height: 1.2; line-height: 1.2;
-webkit-transition: color 0.4s; -webkit-transition: color 0.4s;
-moz-transition: color 0.4s;
-ms-transition: color 0.4s;
-o-transition: color 0.4s;
transition: color 0.4s; transition: color 0.4s;
} }
...@@ -267,9 +267,6 @@ label[for='toggle-all'] { ...@@ -267,9 +267,6 @@ label[for='toggle-all'] {
font-size: 22px; font-size: 22px;
color: #a88a8a; color: #a88a8a;
-webkit-transition: all 0.2s; -webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
-ms-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s; transition: all 0.2s;
} }
...@@ -277,9 +274,7 @@ label[for='toggle-all'] { ...@@ -277,9 +274,7 @@ label[for='toggle-all'] {
text-shadow: 0 0 1px #000, text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8); 0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3); -webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
-ms-transform: scale(1.3); -ms-transform: scale(1.3);
-o-transform: scale(1.3);
transform: scale(1.3); transform: scale(1.3);
} }
...@@ -387,6 +382,7 @@ label[for='toggle-all'] { ...@@ -387,6 +382,7 @@ label[for='toggle-all'] {
Hack to remove background from Mobile Safari. Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera Can't use it globally since it destroys checkboxes in Firefox and Opera
*/ */
@media screen and (-webkit-min-device-pixel-ratio:0) { @media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all, #toggle-all,
#todo-list li .toggle { #todo-list li .toggle {
...@@ -403,14 +399,15 @@ label[for='toggle-all'] { ...@@ -403,14 +399,15 @@ label[for='toggle-all'] {
width: 65px; width: 65px;
height: 41px; height: 41px;
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg); transform: rotate(90deg);
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
} }
} }
.hidden{ .hidden {
display:none; display: none;
} }
hr { hr {
...@@ -528,7 +525,7 @@ hr { ...@@ -528,7 +525,7 @@ hr {
border-top-color: rgba(0, 0, 0, .04); border-top-color: rgba(0, 0, 0, .04);
} }
/**body*/.learn-bar > .learn { .learn-bar > .learn {
position: absolute; position: absolute;
width: 272px; width: 272px;
top: 8px; top: 8px;
...@@ -536,19 +533,23 @@ hr { ...@@ -536,19 +533,23 @@ hr {
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
background-color: rgba(255, 255, 255, .6); background-color: rgba(255, 255, 255, .6);
-webkit-transition-property: left;
transition-property: left; transition-property: left;
-webkit-transition-duration: 500ms;
transition-duration: 500ms; transition-duration: 500ms;
} }
@media (min-width: 899px) { @media (min-width: 899px) {
/**body*/.learn-bar { .learn-bar {
width: auto; width: auto;
margin: 0 0 0 300px; margin: 0 0 0 300px;
} }
/**body*/.learn-bar > .learn {
.learn-bar > .learn {
left: 8px; left: 8px;
} }
/**body*/.learn-bar #todoapp {
.learn-bar #todoapp {
width: 550px; width: 550px;
margin: 130px auto 40px auto; margin: 130px auto 40px auto;
} }
......
...@@ -136,6 +136,10 @@ ...@@ -136,6 +136,10 @@
} }
function getFile(file, callback) { function getFile(file, callback) {
if (!location.host) {
return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.');
}
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true); xhr.open('GET', findRoot() + file, true);
......
/*global define */
define(function (require) { define(function (require) {
'use strict'; 'use strict';
...@@ -7,12 +9,13 @@ define(function (require) { ...@@ -7,12 +9,13 @@ define(function (require) {
var $ = require('$'); var $ = require('$');
require('lavaca/ui/DustTemplate'); require('lavaca/ui/DustTemplate');
// Uncomment this section to use hash-based browser history instead of HTML5 history. // Uncomment this section to use hash-based browser history instead of HTML5
// You should use hash-based history if there's no server-side component supporting your app's routes. // history. You should use hash-based history if there's no server-side
// component supporting your app's routes.
History.overrideStandardsMode(); History.overrideStandardsMode();
// Override Lavaca's default view-root selector to match // Override Lavaca's default view-root selector to match the TodoMVC template
// the TodoMVC template file better // file better
Application.prototype.viewRootSelector = '#todoapp'; Application.prototype.viewRootSelector = '#todoapp';
/** /**
...@@ -27,10 +30,10 @@ define(function (require) { ...@@ -27,10 +30,10 @@ define(function (require) {
'/active': [TodosController, 'home', {filter: 'active'}], '/active': [TodosController, 'home', {filter: 'active'}],
'/completed': [TodosController, 'home', {filter: 'completed'}] '/completed': [TodosController, 'home', {filter: 'completed'}]
}); });
// Patch learn sidebar links so they get ignored by the router // Patch learn sidebar links so they get ignored by the router
$(document.body).find('aside.learn a').addClass('ignore'); $(document.body).find('aside.learn a').addClass('ignore');
}); });
return app; return app;
}); });
define(function(require) { /*global define */
define(function (require) {
'use strict'; 'use strict';
// Constants // Constants
...@@ -7,8 +9,8 @@ define(function(require) { ...@@ -7,8 +9,8 @@ define(function(require) {
var Collection = require('lavaca/mvc/Collection'); var Collection = require('lavaca/mvc/Collection');
/** /**
* A collection of ToDo items that saves * A collection of ToDo items that saves and restores its data from
* and restores its data from localStorage * localStorage
* @class app.models.TodosCollection * @class app.models.TodosCollection
* @super Lavaca.mvc.Collection * @super Lavaca.mvc.Collection
*/ */
...@@ -26,8 +28,7 @@ define(function(require) { ...@@ -26,8 +28,7 @@ define(function(require) {
// Restore any data from localStorage // Restore any data from localStorage
restore.call(this); restore.call(this);
// Listen for changes to the models and then // Listen for changes to the models and then save them in localStorage
// save them in localStorage
this.on('addItem', store); this.on('addItem', store);
this.on('moveItem', store); this.on('moveItem', store);
this.on('removeItem', store); this.on('removeItem', store);
...@@ -44,56 +45,59 @@ define(function(require) { ...@@ -44,56 +45,59 @@ define(function(require) {
/* ---- Computed Properties ---- */ /* ---- Computed Properties ---- */
// Returns a boolean indicating whether // Returns a boolean indicating whether all items are complete
// all items are complete
function allComplete() { function allComplete() {
var allAreComplete = true; var allAreComplete = true;
this.each(function (index, model) { this.each(function (index, model) {
if (!model.get('completed')) { if (!model.get('completed')) {
allAreComplete = false; allAreComplete = false;
return false; // break out of `each` loop return false; // break out of `each` loop
} }
}); });
return allAreComplete; return allAreComplete;
} }
// Returns a count of incomplete items // Returns a count of incomplete items
function itemsLeft() { function itemsLeft() {
return this.filter({completed: false}).length; return this.filter({ completed: false }).length;
} }
// Returns a count of complete items // Returns a count of complete items
function itemsCompleted() { function itemsCompleted() {
return this.filter({completed: true}).length; return this.filter({ completed: true }).length;
} }
/* ---- Handle Persistence ---- */ /* ---- Handle Persistence ---- */
// Called every time the models change. // Called every time the models change. Set a timeout that will write the data
// Set a timeout that will write the data // to localStorage. Clear the timeout with every call to make sure that data
// to localStorage. Clear the timeout with // is only written once even if multiple changes are made in the same run loop
// every call to make sure that data is only
// written once even if multiple changes
// are made in the same run loop
function store() { function store() {
var items;
clearTimeout(this.storeTimer); clearTimeout(this.storeTimer);
this.storeTimer = setTimeout(function () { this.storeTimer = setTimeout(function () {
// Safari will throw an exception // Safari will throw an exception when trying to write to localStorage in
// when trying to write to localStorage in
// private browsing mode // private browsing mode
try { try {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this.toObject().items)); items = JSON.stringify(this.toObject().items);
localStorage.setItem(LOCAL_STORAGE_KEY, items);
} catch (e) {} } catch (e) {}
}.bind(this)); }.bind(this));
} }
// Pull the data from localStorage and // Pull the data from localStorage and add it to the collection
// add it to the collection
function restore() { function restore() {
var data; var data;
var i; var i;
try { try {
data = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY)); data = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY));
if (data) { if (data) {
for (i = 0; i < data.length; i++) { for (i = 0; i < data.length; i++) {
this.add(data[i]); this.add(data[i]);
......
define(function(require) { /*global define */
define(function (require) {
'use strict'; 'use strict';
var Controller = require('lavaca/mvc/Controller'); var Controller = require('lavaca/mvc/Controller');
...@@ -12,12 +14,12 @@ define(function(require) { ...@@ -12,12 +14,12 @@ define(function(require) {
*/ */
var TodosController = Controller.extend({ var TodosController = Controller.extend({
home: function (params) { home: function (params) {
// Set the `filter` parameter on the collection based // Set the `filter` parameter on the collection based on the values
// on the values defined with the routes in app.js // defined with the routes in app.js
todosCollection.set('filter', params.filter); todosCollection.set('filter', params.filter);
// Create an instance of TodosView with `collection` as its model // Create an instance of TodosView with `collection` as its model and then
// and then set a history state which will update the URL // set a history state which will update the URL
return this return this
.view(null, TodosView, todosCollection) .view(null, TodosView, todosCollection)
.then(this.history({}, document.title, params.url)); .then(this.history({}, document.title, params.url));
...@@ -25,5 +27,4 @@ define(function(require) { ...@@ -25,5 +27,4 @@ define(function(require) {
}); });
return TodosController; return TodosController;
}); });
/*global define */
define(function (require) { define(function (require) {
'use strict'; 'use strict';
var View = require('lavaca/mvc/View'); var View = require('lavaca/mvc/View');
var Promise = require('lavaca/util/Promise'); var Promise = require('lavaca/util/Promise');
var $ = require('$'); var $ = require('$');
var _UNDEFINED; var _UNDEFINED;
/** /**
...@@ -15,8 +16,10 @@ define(function (require) { ...@@ -15,8 +16,10 @@ define(function (require) {
var CollectionView = View.extend(function CollectionView() { var CollectionView = View.extend(function CollectionView() {
// Call the super class' constructor // Call the super class' constructor
View.apply(this, arguments); View.apply(this, arguments);
this.el.empty(); this.el.empty();
this.collectionViews = []; this.collectionViews = [];
this.mapEvent({ this.mapEvent({
model: { model: {
'addItem': this.onItemEvent.bind(this), 'addItem': this.onItemEvent.bind(this),
...@@ -32,23 +35,25 @@ define(function (require) { ...@@ -32,23 +35,25 @@ define(function (require) {
* @type String * @type String
*/ */
className: 'collection-view', className: 'collection-view',
/** /**
* A function that should return a jQuery element * A function that should return a jQuery element that will be used as the
* that will be used as the `el` for a particular * `el` for a particular item in the collection. The function is passed two
* item in the collection. The function is passed * parameters, the model and the index.
* two parameters, the model and the index.
* @property itemEl * @property itemEl
* @type jQuery * @type jQuery
*/ */
itemEl: function () { itemEl: function () {
return $('<div/>'); // default to <li>? return $('<div/>'); // default to <li>?
}, },
/** /**
* The view type used for each item view * The view type used for each item view
* @property TView * @property TView
* @type lavaca.mvc.View * @type lavaca.mvc.View
*/ */
TView: View, TView: View,
/** /**
* Initializes and renders all item views to be shown * Initializes and renders all item views to be shown
* @method render * @method render
...@@ -57,13 +62,17 @@ define(function (require) { ...@@ -57,13 +62,17 @@ define(function (require) {
var models = this.model.filter(this.modelFilter.bind(this)); var models = this.model.filter(this.modelFilter.bind(this));
var fragment = document.createDocumentFragment(); var fragment = document.createDocumentFragment();
var view; var view;
models.forEach(function (item) { models.forEach(function (item) {
view = this.addItemView(item); view = this.addItemView(item);
fragment.appendChild(view.el[0]); fragment.appendChild(view.el[0]);
}.bind(this)); }.bind(this));
this.trigger('rendersuccess', {html: fragment}); this.trigger('rendersuccess', {html: fragment});
return new Promise().resolve(); return new Promise().resolve();
}, },
/** /**
* Creates a new view and inserts it into the DOM at the * Creates a new view and inserts it into the DOM at the
* provided index * provided index
...@@ -79,10 +88,12 @@ define(function (require) { ...@@ -79,10 +88,12 @@ define(function (require) {
if (insertIndex < 0 || insertIndex > count) { if (insertIndex < 0 || insertIndex > count) {
throw 'Invalid item view insertion index'; throw 'Invalid item view insertion index';
} }
view = new this.TView(this.itemEl(model, index), model, this); view = new this.TView(this.itemEl(model, index), model, this);
this.childViews.set(view.id, view); this.childViews.set(view.id, view);
this.collectionViews.splice(insertIndex, 0, view); this.collectionViews.splice(insertIndex, 0, view);
this.applyChildViewEvent(view); this.applyChildViewEvent(view);
if (insertIndex === 0) { if (insertIndex === 0) {
this.el.prepend(view.el[0]); this.el.prepend(view.el[0]);
} else if (insertIndex === count) { } else if (insertIndex === count) {
...@@ -93,8 +104,10 @@ define(function (require) { ...@@ -93,8 +104,10 @@ define(function (require) {
.eq(insertIndex - 1) .eq(insertIndex - 1)
.after(view.el[0]); .after(view.el[0]);
} }
return view; return view;
}, },
/** /**
* adds listeners for a specific child view * adds listeners for a specific child view
* @method applyChildViewEvent * @method applyChildViewEvent
...@@ -109,6 +122,7 @@ define(function (require) { ...@@ -109,6 +122,7 @@ define(function (require) {
} }
} }
}, },
/** /**
* Remove and disposes a view * Remove and disposes a view
* @method removeItemView * @method removeItemView
...@@ -120,6 +134,7 @@ define(function (require) { ...@@ -120,6 +134,7 @@ define(function (require) {
view.el.remove(); view.el.remove();
view.dispose(); view.dispose();
}, },
/** /**
* Returns the index of a view * Returns the index of a view
* @method getViewIndexByModel * @method getViewIndexByModel
...@@ -136,6 +151,7 @@ define(function (require) { ...@@ -136,6 +151,7 @@ define(function (require) {
}); });
return collectionViewIndex; return collectionViewIndex;
}, },
/** /**
* The filter to run against the collection * The filter to run against the collection
* @method modelFilter * @method modelFilter
...@@ -145,8 +161,10 @@ define(function (require) { ...@@ -145,8 +161,10 @@ define(function (require) {
modelFilter: function () { modelFilter: function () {
return true; return true;
}, },
/** /**
* Event handler for all collection events that produces all add, remove, and move actions * Event handler for all collection events that produces all add, remove,
* and move actions
* @method modelFilter * @method modelFilter
* @param {Obejct} [e] the event * @param {Obejct} [e] the event
*/ */
...@@ -154,25 +172,31 @@ define(function (require) { ...@@ -154,25 +172,31 @@ define(function (require) {
var models = this.model.filter(this.modelFilter.bind(this)); var models = this.model.filter(this.modelFilter.bind(this));
var i = -1; var i = -1;
var model, view, viewIndex, oldIndex, modelIndex, temp; var model, view, viewIndex, oldIndex, modelIndex, temp;
// Add new views // Add new views
while (model = models[++i]) { while (model = models[++i]) {
viewIndex = this.getViewIndexByModel(model); viewIndex = this.getViewIndexByModel(model);
if (viewIndex === -1) { if (viewIndex === -1) {
this.addItemView(model, i); this.addItemView(model, i);
} }
} }
// Remove Old Views // Remove Old Views
i = -1; i = -1;
while (view = this.collectionViews[++i]) { while (view = this.collectionViews[++i]) {
modelIndex = models.indexOf(view.model); modelIndex = models.indexOf(view.model);
if (modelIndex === -1) { if (modelIndex === -1) {
this.removeItemView(i); this.removeItemView(i);
} }
} }
// Move any existing views // Move any existing views
i = -1; i = -1;
while (model = models[++i]) { while (model = models[++i]) {
oldIndex = this.getViewIndexByModel(model); oldIndex = this.getViewIndexByModel(model);
if (oldIndex !== i) { if (oldIndex !== i) {
this.swapViews(this.collectionViews[i], this.collectionViews[oldIndex]); this.swapViews(this.collectionViews[i], this.collectionViews[oldIndex]);
temp = this.collectionViews[oldIndex]; temp = this.collectionViews[oldIndex];
...@@ -181,6 +205,7 @@ define(function (require) { ...@@ -181,6 +205,7 @@ define(function (require) {
} }
} }
}, },
/** /**
* Swaps two views in the DOM * Swaps two views in the DOM
* @method swapViews * @method swapViews
...@@ -192,12 +217,11 @@ define(function (require) { ...@@ -192,12 +217,11 @@ define(function (require) {
var b = viewB.el[0]; var b = viewB.el[0];
var aParent = a.parentNode; var aParent = a.parentNode;
var aSibling = a.nextSibling === b ? a : a.nextSibling; var aSibling = a.nextSibling === b ? a : a.nextSibling;
b.parentNode.insertBefore(a, b); b.parentNode.insertBefore(a, b);
aParent.insertBefore(b, aSibling); aParent.insertBefore(b, aSibling);
} }
}); });
return CollectionView; return CollectionView;
}); });
/*global define */
define(function (require) { define(function (require) {
'use strict'; 'use strict';
...@@ -16,6 +18,7 @@ define(function (require) { ...@@ -16,6 +18,7 @@ define(function (require) {
var TodosView = View.extend(function TodosView() { var TodosView = View.extend(function TodosView() {
// Call the super class' constructor // Call the super class' constructor
View.apply(this, arguments); View.apply(this, arguments);
this.mapEvent({ this.mapEvent({
'input.toggle': { 'input.toggle': {
change: toggleComplete.bind(this) change: toggleComplete.bind(this)
...@@ -34,6 +37,7 @@ define(function (require) { ...@@ -34,6 +37,7 @@ define(function (require) {
'change': this.onModelChange.bind(this) 'change': this.onModelChange.bind(this)
} }
}); });
this.render(); this.render();
}, { }, {
/** /**
...@@ -42,12 +46,14 @@ define(function (require) { ...@@ -42,12 +46,14 @@ define(function (require) {
* @type String * @type String
*/ */
template: 'templates/todo-item', template: 'templates/todo-item',
/** /**
* A class name added to the view container * A class name added to the view container
* @property className * @property className
* @type String * @type String
*/ */
className: 'todo-item', className: 'todo-item',
/** /**
* Executes when the template renders successfully * Executes when the template renders successfully
* @method onRenderSuccess * @method onRenderSuccess
...@@ -56,6 +62,7 @@ define(function (require) { ...@@ -56,6 +62,7 @@ define(function (require) {
View.prototype.onRenderSuccess.apply(this, arguments); View.prototype.onRenderSuccess.apply(this, arguments);
this.checkIfCompleted(); this.checkIfCompleted();
}, },
/** /**
* Redraws template with model * Redraws template with model
* @method redraw * @method redraw
...@@ -64,9 +71,10 @@ define(function (require) { ...@@ -64,9 +71,10 @@ define(function (require) {
View.prototype.redraw.apply(this, arguments) View.prototype.redraw.apply(this, arguments)
.then(this.checkIfCompleted.bind(this)); .then(this.checkIfCompleted.bind(this));
}, },
/** /**
* Adds/Removes a completed class to view element, * Adds/Removes a completed class to view element, this wouldnt be needed if
* this wouldnt be needed if we restructured some DOM or CSS * we restructured some DOM or CSS
* @method checkIfCompleted * @method checkIfCompleted
*/ */
checkIfCompleted: function () { checkIfCompleted: function () {
...@@ -77,6 +85,7 @@ define(function (require) { ...@@ -77,6 +85,7 @@ define(function (require) {
this.el.removeClass('completed'); this.el.removeClass('completed');
} }
}, },
/** /**
* Redraws template when model changes * Redraws template when model changes
* @method onModelChange * @method onModelChange
...@@ -86,7 +95,6 @@ define(function (require) { ...@@ -86,7 +95,6 @@ define(function (require) {
this.redraw(); this.redraw();
} }
} }
}); });
// Set the completion state of a single model // Set the completion state of a single model
...@@ -97,12 +105,14 @@ define(function (require) { ...@@ -97,12 +105,14 @@ define(function (require) {
// Start editing a Todo // Start editing a Todo
function startEditing() { function startEditing() {
this.el.addClass('editing'); this.el.addClass('editing');
this.$input.focus(); this.$input.focus();
this.$input.val(this.model.get('title')); // resetting value fixes text selection on focus
// resetting value fixes text selection on focus
this.$input.val(this.model.get('title'));
} }
// Commit the edit when the ENTER key // Commit the edit when the ENTER key is pressed
// is pressed
function editTodo(e) { function editTodo(e) {
if (e.which === ENTER_KEY) { if (e.which === ENTER_KEY) {
endEditing.call(this); endEditing.call(this);
...@@ -114,7 +124,9 @@ define(function (require) { ...@@ -114,7 +124,9 @@ define(function (require) {
function endEditing() { function endEditing() {
var trimmedValue = this.$input.val().trim(); var trimmedValue = this.$input.val().trim();
this.el.removeClass('editing'); this.el.removeClass('editing');
if (trimmedValue) { if (trimmedValue) {
this.model.set('title', trimmedValue); this.model.set('title', trimmedValue);
} else { } else {
...@@ -128,5 +140,4 @@ define(function (require) { ...@@ -128,5 +140,4 @@ define(function (require) {
} }
return TodosView; return TodosView;
}); });
/*global define */
define(function (require) { define(function (require) {
'use strict'; 'use strict';
...@@ -13,6 +15,7 @@ define(function (require) { ...@@ -13,6 +15,7 @@ define(function (require) {
var TodosCollectionView = CollectionView.extend(function TodosCollectionView() { var TodosCollectionView = CollectionView.extend(function TodosCollectionView() {
CollectionView.apply(this, arguments); CollectionView.apply(this, arguments);
this.mapChildViewEvent('removeView', this.onRemoveView.bind(this), this.TView); this.mapChildViewEvent('removeView', this.onRemoveView.bind(this), this.TView);
this.render(); this.render();
}, { }, {
/** /**
...@@ -21,29 +24,32 @@ define(function (require) { ...@@ -21,29 +24,32 @@ define(function (require) {
* @type String * @type String
*/ */
className: 'todos-collection-view', className: 'todos-collection-view',
/** /**
* A function that should return a jQuery element * A function that should return a jQuery element that will be used as the
* that will be used as the `el` for a particular * `el` for a particular item in the collection. The function is passed two
* item in the collection. The function is passed * parameters, the model and the index.
* two parameters, the model and the index.
* @property itemEl * @property itemEl
* @type jQuery * @type jQuery
*/ */
itemEl: function () { itemEl: function () {
return $('<li/>'); return $('<li/>');
}, },
/** /**
* The view type used for each item view * The view type used for each item view
* @property itemEl * @property itemEl
* @type lavaca.mvc.View * @type lavaca.mvc.View
*/ */
TView: TodoItemView, TView: TodoItemView,
/** /**
* Executes when the template renders successfully * Executes when the template renders successfully
* @method onRenderSuccess * @method onRenderSuccess
* *
* @param {Event} e The render event. This object should have a string property named "html" * @param {Event} e The render event. This object should have a string
* that contains the template's rendered HTML output. * property named "html" that contains the template's
* rendered HTML output.
*/ */
onRenderSuccess: function (e) { onRenderSuccess: function (e) {
this.el.html(e.html); this.el.html(e.html);
...@@ -54,6 +60,7 @@ define(function (require) { ...@@ -54,6 +60,7 @@ define(function (require) {
this.el.attr('data-view-id', this.id); this.el.attr('data-view-id', this.id);
this.hasRendered = true; this.hasRendered = true;
}, },
/** /**
* The filter to run against the collection * The filter to run against the collection
* @method modelFilter * @method modelFilter
...@@ -68,6 +75,7 @@ define(function (require) { ...@@ -68,6 +75,7 @@ define(function (require) {
return true; return true;
} }
}, },
/** /**
* Removes a view when removeView event it triggered * Removes a view when removeView event it triggered
* @method swapViews * @method swapViews
...@@ -77,9 +85,7 @@ define(function (require) { ...@@ -77,9 +85,7 @@ define(function (require) {
onRemoveView: function (e) { onRemoveView: function (e) {
this.model.remove(e.model); this.model.remove(e.model);
} }
}); });
return TodosCollectionView; return TodosCollectionView;
}); });
/*global define */
define(function (require) { define(function (require) {
'use strict'; 'use strict';
...@@ -16,10 +18,11 @@ define(function (require) { ...@@ -16,10 +18,11 @@ define(function (require) {
var TodosView = PageView.extend(function TodosView() { var TodosView = PageView.extend(function TodosView() {
// Call the super class' constructor // Call the super class' constructor
PageView.apply(this, arguments); PageView.apply(this, arguments);
// Map collection view to #todo-list // Map collection view to #todo-list
this.mapChildView('#todo-list', TodosCollectionView, this.model); this.mapChildView('#todo-list', TodosCollectionView, this.model);
// Map DOM and model events to event handler
// functions declared below // Map DOM and model events to event handler functions declared below
this.mapEvent({ this.mapEvent({
'#new-todo': { '#new-todo': {
keypress: addTodo.bind(this) keypress: addTodo.bind(this)
...@@ -46,27 +49,27 @@ define(function (require) { ...@@ -46,27 +49,27 @@ define(function (require) {
* The name of the template used by the view * The name of the template used by the view
*/ */
template: 'templates/todos', template: 'templates/todos',
/** /**
* @field {String} className * @field {String} className
* @default 'example' * @default 'example'
* A class name added to the view container * A class name added to the view container
*/ */
className: 'todos' className: 'todos'
}); });
/* ---- Event Handlers ---- */ /* ---- Event Handlers ---- */
// Whenever the model changes, set a timeout // Whenever the model changes, set a timeout that will re-render the view's
// that will re-render the view's template // template and update the DOM. Clear the timeout with every call to make sure
// and update the DOM. Clear the timeout with // that the redraw only happens once even if multiple changes are made in the
// every call to make sure that the redraw // same run loop
// only happens once even if multiple changes
// are made in the same run loop
function modelChange() { function modelChange() {
clearTimeout(this.redrawTimeout); clearTimeout(this.redrawTimeout);
this.redrawTimeout = setTimeout(function () { this.redrawTimeout = setTimeout(function () {
var count = this.model.count(); var count = this.model.count();
if (count === 0) { if (count === 0) {
this.countIsZero = true; this.countIsZero = true;
this.redraw(); this.redraw();
...@@ -84,16 +87,20 @@ define(function (require) { ...@@ -84,16 +87,20 @@ define(function (require) {
function addTodo(e) { function addTodo(e) {
var input = e.currentTarget; var input = e.currentTarget;
var val; var val;
if (e.which === ENTER_KEY) { if (e.which === ENTER_KEY) {
val = input.value.trim(); val = input.value.trim();
if (val) { if (val) {
this.model.add({ this.model.add({
id: Date.now(), id: Date.now(),
title: val, title: val,
completed: false completed: false
}); });
input.value = ''; input.value = '';
} }
e.preventDefault(); e.preventDefault();
} }
} }
...@@ -111,5 +118,4 @@ define(function (require) { ...@@ -111,5 +118,4 @@ define(function (require) {
} }
return TodosView; return TodosView;
}); });
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