Commit 0839ee5f authored by Sindre Sorhus's avatar Sindre Sorhus

YUI app - convert to tabs

parent 8f87c1bb
YUI.add('todo-app', function (Y) { YUI.add('todo-app', function (Y) {
"use strict"; "use strict";
// Dependencies from MVC namespace. // Dependencies from MVC namespace.
var TodoList = Y.TodoMVC.TodoList, var TodoList = Y.TodoMVC.TodoList,
TodoView = Y.TodoMVC.TodoView, TodoView = Y.TodoMVC.TodoView,
TodoApp; TodoApp;
// -- Main Application -------------- // -- Main Application --------------
TodoApp = Y.Base.create('todoApp', Y.App, [], { TodoApp = Y.Base.create('todoApp', Y.App, [], {
// Set container to bind to the existing '#todoapp' element // Set container to bind to the existing '#todoapp' element
containerTemplate: '#todoapp', containerTemplate: '#todoapp',
// Compile statistics template with Handlebars. // Compile statistics template with Handlebars.
template: Y.Handlebars.compile(Y.one('#stats-template').getHTML()), template: Y.Handlebars.compile(Y.one('#stats-template').getHTML()),
// DOM events for creating new Todos and clearing out old ones. // DOM events for creating new Todos and clearing out old ones.
events: { events: {
'#new-todo': { '#new-todo': {
keypress: 'enterCreate' keypress: 'enterCreate'
}, },
'#clear-completed': { '#clear-completed': {
click: 'clearCompleted' click: 'clearCompleted'
}, },
'#toggle-all': { '#toggle-all': {
click: 'completeAll' click: 'completeAll'
} }
}, },
// Initialize our TodoList, and bind any events that occur // Initialize our TodoList, and bind any events that occur
// when new Todos are added, changed, or removed within it. // when new Todos are added, changed, or removed within it.
// Also, fetch any Todos that are found within localStorage. // Also, fetch any Todos that are found within localStorage.
initializer: function () { initializer: function () {
this.set('todoList', new TodoList()); this.set('todoList', new TodoList());
var list = this.get('todoList'); var list = this.get('todoList');
Y.Handlebars.registerHelper('pluralize', function (context, word) { Y.Handlebars.registerHelper('pluralize', function (context, word) {
return (context === 1) ? word : word + 's'; return (context === 1) ? word : word + 's';
}); });
list.after(['add', 'remove', 'reset', 'todo:completedChange'], list.after(['add', 'remove', 'reset', 'todo:completedChange'],
this.render, this); this.render, this);
list.load(); list.load();
// Keep our filters on refresh by immediately dispatching route. // Keep our filters on refresh by immediately dispatching route.
this.once('ready', function (e) { this.once('ready', function (e) {
if (this.hasRoute(this.getPath())) { if (this.hasRoute(this.getPath())) {
this.dispatch(); this.dispatch();
} }
}); });
}, },
// Render our application with the statistics from our TodoList, // Render our application with the statistics from our TodoList,
// and various other stylistic elements. // and various other stylistic elements.
render: function () { render: function () {
var todoList = this.get('todoList'), var todoList = this.get('todoList'),
completed = todoList.completed().size(), completed = todoList.completed().size(),
remaining = todoList.remaining().size(), remaining = todoList.remaining().size(),
container = this.get('container'), container = this.get('container'),
main = this.get('main'), main = this.get('main'),
footer = this.get('footer'); footer = this.get('footer');
// If we have Todos in our TodoList, show them with statistics. // If we have Todos in our TodoList, show them with statistics.
if (todoList.size()) { if (todoList.size()) {
main.show(); main.show();
footer.show(); footer.show();
footer.setHTML(this.template({ footer.setHTML(this.template({
completed: completed, completed: completed,
remaining: remaining remaining: remaining
})); }));
// Highlights for filters at the bottom of our Todo application. // Highlights for filters at the bottom of our Todo application.
footer.one('#filters li a').removeClass('selected'); footer.one('#filters li a').removeClass('selected');
footer.all('#filters li a') footer.all('#filters li a')
.filter('[href="#/' + (this.get('filter') || '') + '"]') .filter('[href="#/' + (this.get('filter') || '') + '"]')
.addClass('selected'); .addClass('selected');
} else { } else {
main.hide(); main.hide();
footer.hide(); footer.hide();
} }
// Set the checkbox only if all Todos have been completed. // Set the checkbox only if all Todos have been completed.
this.get('allCheckbox').set('checked', !remaining); this.get('allCheckbox').set('checked', !remaining);
this.addViews(); this.addViews();
}, },
// Add Todo views to the DOM simultaneously, triggered when // Add Todo views to the DOM simultaneously, triggered when
// the application initially loads, or we switch filters. // the application initially loads, or we switch filters.
addViews: function () { addViews: function () {
var fragment = Y.one(Y.config.doc.createDocumentFragment()), var fragment = Y.one(Y.config.doc.createDocumentFragment()),
todoList = this.get('todoList'), todoList = this.get('todoList'),
models; models;
// An Array of models is passed through when the 'reset' // An Array of models is passed through when the 'reset'
// event is triggered through syncing through load(). // event is triggered through syncing through load().
switch (this.get('filter')) { switch (this.get('filter')) {
case 'active': case 'active':
models = todoList.remaining(); models = todoList.remaining();
break; break;
case 'completed': case 'completed':
models = todoList.completed(); models = todoList.completed();
break; break;
default: default:
models = todoList; models = todoList;
break; break;
} }
// Iterate through the (filtered) ModelList. // Iterate through the (filtered) ModelList.
models.each(function (model) { models.each(function (model) {
var view = new TodoView({model: model}); var view = new TodoView({model: model});
fragment.append(view.render().get('container')); fragment.append(view.render().get('container'));
}); });
this.get('container').one('#todo-list').setContent(fragment); this.get('container').one('#todo-list').setContent(fragment);
}, },
// Create and save a new Todo from the inputted value when the // Create and save a new Todo from the inputted value when the
// Enter key is pressed down. // Enter key is pressed down.
enterCreate: function (e) { enterCreate: function (e) {
var ENTER_KEY = 13, var ENTER_KEY = 13,
todoList = this.get('todoList'), todoList = this.get('todoList'),
inputNode = this.get('inputNode'), inputNode = this.get('inputNode'),
value = Y.Escape.html(Y.Lang.trim(inputNode.get('value'))); value = Y.Escape.html(Y.Lang.trim(inputNode.get('value')));
if (e.keyCode !== ENTER_KEY || !value) { if (e.keyCode !== ENTER_KEY || !value) {
return; return;
} }
todoList.create({ todoList.create({
title: value title: value
}); });
inputNode.set('value', ''); inputNode.set('value', '');
}, },
// Clear all completed Todos from the TodoList. This removes the models // Clear all completed Todos from the TodoList. This removes the models
// from the list, as well as deletes them from localStorage. // from the list, as well as deletes them from localStorage.
clearCompleted: function (e) { clearCompleted: function (e) {
var todoList = this.get('todoList'), var todoList = this.get('todoList'),
completed = todoList.completed(); completed = todoList.completed();
todoList.remove(completed); todoList.remove(completed);
completed.each(function (todo) { completed.each(function (todo) {
todo.clear(); todo.clear();
}); });
}, },
// Complete all non-complete Todos, or reset them all if they are // Complete all non-complete Todos, or reset them all if they are
// all already complete. // all already complete.
completeAll: function () { completeAll: function () {
var todoList = this.get('todoList'), var todoList = this.get('todoList'),
allCheckbox = this.get('allCheckbox'), allCheckbox = this.get('allCheckbox'),
completed = allCheckbox.get('checked'); completed = allCheckbox.get('checked');
Y.Array.each(todoList.toArray(), function (todo) { Y.Array.each(todoList.toArray(), function (todo) {
todo.save({completed: completed}); todo.save({completed: completed});
}); });
}, },
// Set the filter for our application from the route that is passed // Set the filter for our application from the route that is passed
// in (see below). // in (see below).
handleFilter: function (req) { handleFilter: function (req) {
this.set('filter', req.params.filter); this.set('filter', req.params.filter);
this.get('todoList').load(); this.get('todoList').load();
} }
}, { }, {
ATTRS: { ATTRS: {
// Significant DOM elements that relate to our application that // Significant DOM elements that relate to our application that
// we would like to keep as attributes. // we would like to keep as attributes.
container: { container: {
valueFn: function () { valueFn: function () {
return Y.one('#todoapp'); return Y.one('#todoapp');
} }
}, },
inputNode: { inputNode: {
valueFn: function () { valueFn: function () {
return Y.one('#new-todo'); return Y.one('#new-todo');
} }
}, },
allCheckbox: { allCheckbox: {
valueFn: function () { valueFn: function () {
return Y.one('#toggle-all'); return Y.one('#toggle-all');
} }
}, },
main: { main: {
valueFn: function () { valueFn: function () {
return Y.one('#main'); return Y.one('#main');
} }
}, },
footer: { footer: {
valueFn: function () { valueFn: function () {
return Y.one('#footer'); return Y.one('#footer');
} }
}, },
// This can be set to fall back on server-side routing when // This can be set to fall back on server-side routing when
// HTML5 pushState is not available. For this application, // HTML5 pushState is not available. For this application,
// we are only using hash-based URLs though. // we are only using hash-based URLs though.
serverRouting: { serverRouting: {
value: false value: false
}, },
// Our initial filter for the application. // Our initial filter for the application.
filter: { filter: {
value: null value: null
}, },
// Routing for the application, to determine the filter. // Routing for the application, to determine the filter.
// The callback takes a request object, Express-style. // The callback takes a request object, Express-style.
routes: { routes: {
value: [ value: [
{path: '/:filter', callback: 'handleFilter'} {path: '/:filter', callback: 'handleFilter'}
] ]
} }
} }
}); });
// Namespace this application under our custom Y.MVC namespace. // Namespace this application under our custom Y.MVC namespace.
Y.namespace('TodoMVC').TodoApp = TodoApp; Y.namespace('TodoMVC').TodoApp = TodoApp;
}, '@VERSION@', { }, '@VERSION@', {
requires: [ requires: [
'app', 'app',
'todo-list', 'todo-list',
'todo-view', 'todo-view',
'node', 'node',
'event-focus' 'event-focus'
] ]
}); });
YUI.add('todo', function (Y) { YUI.add('todo', function (Y) {
"use strict"; "use strict";
// -- Todo Model ------------- // -- Todo Model -------------
var Todo = Y.Base.create('todo', Y.Model, [Y.ModelSync.Local], { var Todo = Y.Base.create('todo', Y.Model, [Y.ModelSync.Local], {
// Set up the root localStorage key we save our Model data in. // Set up the root localStorage key we save our Model data in.
root: 'todos-yui', root: 'todos-yui',
// Toggle the completed state of the Todo. // Toggle the completed state of the Todo.
toggle: function () { toggle: function () {
this.save({completed: !this.get('completed')}); this.save({completed: !this.get('completed')});
}, },
// Destroy this Todo and remove it from localStorage. // Destroy this Todo and remove it from localStorage.
clear: function () { clear: function () {
this.destroy({remove: true}); this.destroy({remove: true});
} }
}, { }, {
// Default attributes. // Default attributes.
ATTRS: { ATTRS: {
title: { title: {
value: 'empty todo ...' value: 'empty todo ...'
}, },
completed: { completed: {
value: false value: false
} }
} }
}); });
// Set this Model under our custom Y.MVC namespace. // Set this Model under our custom Y.MVC namespace.
Y.namespace('TodoMVC').Todo = Todo; Y.namespace('TodoMVC').Todo = Todo;
}, '@VERSION@', { }, '@VERSION@', {
requires: [ requires: [
'gallery-model-sync-local', 'gallery-model-sync-local',
'model' 'model'
] ]
}); });
YUI.add('todo-list', function (Y) { YUI.add('todo-list', function (Y) {
"use strict"; "use strict";
// Dependencies from Y.MVC. // Dependencies from Y.MVC.
var Todo = Y.TodoMVC.Todo, var Todo = Y.TodoMVC.Todo,
TodoList; TodoList;
// -- TodoList Model list ----- // -- TodoList Model list -----
TodoList = Y.Base.create('todoList', Y.ModelList, [Y.ModelSync.Local], { TodoList = Y.Base.create('todoList', Y.ModelList, [Y.ModelSync.Local], {
// The related Model for our Model List. // The related Model for our Model List.
model: Todo, model: Todo,
// The root used for our localStorage key. // The root used for our localStorage key.
root: 'todos-yui', root: 'todos-yui',
// Return a ModelList of our completed Models. // Return a ModelList of our completed Models.
completed: function () { completed: function () {
return this.filter({ asList: true }, function (todo) { return this.filter({ asList: true }, function (todo) {
return todo.get('completed'); return todo.get('completed');
}); });
}, },
// Return an ModelList of our un-completed Models. // Return an ModelList of our un-completed Models.
remaining: function () { remaining: function () {
return this.filter({ asList: true }, function (todo) { return this.filter({ asList: true }, function (todo) {
return !todo.get('completed'); return !todo.get('completed');
}); });
} }
}); });
// Set this Model List under our custom Y.MVC namespace. // Set this Model List under our custom Y.MVC namespace.
Y.namespace('TodoMVC').TodoList = TodoList; Y.namespace('TodoMVC').TodoList = TodoList;
}, '@VERSION@', { }, '@VERSION@', {
requires: [ requires: [
'gallery-model-sync-local', 'gallery-model-sync-local',
'model-list', 'model-list',
'todo' 'todo'
] ]
}); });
YUI.add('todo-view', function (Y) { YUI.add('todo-view', function (Y) {
"use strict"; "use strict";
// -- Todo View ------------------- // -- Todo View -------------------
var TodoView = Y.Base.create('todoView', Y.View, [], { var TodoView = Y.Base.create('todoView', Y.View, [], {
// The container element that the View is rendered under. // The container element that the View is rendered under.
containerTemplate: '<li>', containerTemplate: '<li>',
// Compile our template using Handlebars. // Compile our template using Handlebars.
template: Y.Handlebars.compile(Y.one('#item-template').getHTML()), template: Y.Handlebars.compile(Y.one('#item-template').getHTML()),
// Bind DOM events for handling changes to a specific Todo, // Bind DOM events for handling changes to a specific Todo,
// for completion and editing. // for completion and editing.
events: { events: {
'.toggle': { '.toggle': {
click: 'toggleComplete' click: 'toggleComplete'
}, },
'label': { 'label': {
dblclick: 'edit' dblclick: 'edit'
}, },
'.edit': { '.edit': {
blur: 'close', blur: 'close',
keypress: 'enterUpdate' keypress: 'enterUpdate'
}, },
'.destroy': { '.destroy': {
click: 'clear' click: 'clear'
} }
}, },
// Initialize this view by setting event handlers when the Model // Initialize this view by setting event handlers when the Model
// is updated or destroyed. // is updated or destroyed.
initializer: function () { initializer: function () {
var model = this.get('model'); var model = this.get('model');
model.after('change', this.render, this); model.after('change', this.render, this);
}, },
// Render this view in our <li> container, and fill it with the // Render this view in our <li> container, and fill it with the
// data in our Model. // data in our Model.
render: function () { render: function () {
var container = this.get('container'), var container = this.get('container'),
model = this.get('model'); model = this.get('model');
container.setHTML(this.template(model.toJSON())); container.setHTML(this.template(model.toJSON()));
container.toggleClass('completed', model.get('completed')); container.toggleClass('completed', model.get('completed'));
this.set('inputNode', container.one('.edit')); this.set('inputNode', container.one('.edit'));
return this; return this;
}, },
// Toggle the linked Todo's completion status. // Toggle the linked Todo's completion status.
toggleComplete: function () { toggleComplete: function () {
this.get('model').toggle(); this.get('model').toggle();
}, },
// Turn on editing mode for the Todo by exposing the input field. // Turn on editing mode for the Todo by exposing the input field.
edit: function () { edit: function () {
this.get('container').addClass('editing'); this.get('container').addClass('editing');
this.get('inputNode').focus(); this.get('inputNode').focus();
}, },
// Get the value from our input field while hiding it, and // Get the value from our input field while hiding it, and
// save it to our Todo when focus is lost from the field. // save it to our Todo when focus is lost from the field.
close: function (e) { close: function (e) {
var value = this.get('inputNode').get('value'), var value = this.get('inputNode').get('value'),
editedValue = Y.Escape.html(Y.Lang.trim(value)); editedValue = Y.Escape.html(Y.Lang.trim(value));
this.get('container').removeClass('editing'); this.get('container').removeClass('editing');
if (editedValue) { if (editedValue) {
this.get('model').save({title: editedValue}); this.get('model').save({title: editedValue});
} else { } else {
this.clear(); this.clear();
} }
}, },
// Also allow updating the Todo's text through the enter key. // Also allow updating the Todo's text through the enter key.
enterUpdate: function (e) { enterUpdate: function (e) {
var ENTER_KEY = 13; var ENTER_KEY = 13;
if (e.keyCode === ENTER_KEY) { if (e.keyCode === ENTER_KEY) {
this.close(); this.close();
} }
}, },
// Destroy the model when the delete button is clicked. // Destroy the model when the delete button is clicked.
clear: function (e) { clear: function (e) {
this.get('model').clear(); this.get('model').clear();
} }
}); });
// Set this View under our custom Y.TodoMVC namespace. // Set this View under our custom Y.TodoMVC namespace.
Y.namespace('TodoMVC').TodoView = TodoView; Y.namespace('TodoMVC').TodoView = TodoView;
}, '@VERSION@', { }, '@VERSION@', {
requires: [ requires: [
'view', 'view',
'handlebars' 'handlebars'
] ]
}); });
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