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

(lavaca) code style.

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