Commit 00435de1 authored by Addy Osmani's avatar Addy Osmani

Merge pull request #352 from petermichaux/gh-pages

Rewrite Maria example to use latest TodoMVC requirements
parents 0d6e84dd 6f5dad67
/*
Aristocrat version 1
Aristocrat version 2
Copyright (c) 2012, Peter Michaux
All rights reserved.
Licensed under the Simplified BSD License.
https://github.com/petermichaux/aristocrat/blob/master/LICENSE
*/
var aristocrat = aristocrat || {};
var aristocrat = {};
(function() {
......@@ -78,7 +78,7 @@ aristocrat.removeClass(document.body, 'king');
var re = getRegExp(className);
while (re.test(el.className)) { // in case multiple occurrences
el.className = el.className.replace(re, ' ');
}
}
};
/**
......
ul.completed li.incompleted,
ul.incompleted li.completed {
display: none;
}
This diff is collapsed.
<!DOCTYPE html>
<html>
<head>
<title>To-do App</title>
<meta charset="utf-8">
<title>Maria • TodoMVC</title>
<link href="css/todos.css" rel="stylesheet" type="text/css">
<link href="../../../../assets/base.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet">
</head>
<body>
<noscript>
<p>This application uses JavaScript. Please enable JavaScript in your browser and refresh the page.</p>
</noscript>
<script>
document.write('<p id="loading">loading...</p>');
</script>
<script src="../lib/maria/maria.js"></script>
<script src="../lib/aristocrat/aristocrat.js"></script>
<script src="../../../../assets/director.min.js"></script>
<script src="../lib/maria/maria.js"></script>
<script src="../lib/aristocrat/aristocrat.js"></script>
<script src="js/namespace.js"></script>
<script src="js/models/TodoModel.js"></script>
<script src="js/models/TodosModel.js"></script>
<script src="js/templates/TodosInputTemplate.js"></script>
<script src="js/views/TodosInputView.js"></script>
<script src="js/templates/TodosListTemplate.js"></script>
<script src="js/views/TodosListView.js"></script>
<script src="js/templates/TodosToolbarTemplate.js"></script>
<script src="js/views/TodosToolbarView.js"></script>
<script src="js/controllers/TodosToolbarController.js"></script>
<script src="js/templates/TodosStatsTemplate.js"></script>
<script src="js/views/TodosStatsView.js"></script>
<script src="js/templates/TodosAppTemplate.js"></script>
<script src="js/views/TodosAppView.js"></script>
<script src="js/controllers/TodosInputController.js"></script>
<script src="js/templates/TodoTemplate.js"></script>
<script src="js/views/TodoView.js"></script>
<script src="js/controllers/TodoController.js"></script>
<script src="js/bootstrap.js"></script>
<script src="js/namespace.js"></script>
<script src="js/util.js"></script>
<script src="js/models/TodoModel.js"></script>
<script src="js/models/TodosModel.js"></script>
<script src="js/templates/TodosTemplate.js"></script>
<script src="js/views/TodosView.js"></script>
<script src="js/controllers/TodosController.js"></script>
<script src="js/templates/TodoTemplate.js"></script>
<script src="js/views/TodoView.js"></script>
<script src="js/controllers/TodoController.js"></script>
<script src="js/bootstrap.js"></script>
</body>
</html>
maria.addEventListener(window, 'load', function() {
var loading = document.getElementById('loading');
loading.parentNode.removeChild(loading);
/*jshint strict: false */
/*global maria, Router, checkit */
var model;
if ((typeof localStorage === 'object') && (typeof JSON === 'object')) {
var store = localStorage.getItem('todos-maria');
model = store ? checkit.TodosModel.fromJSON(JSON.parse(store)) :
new checkit.TodosModel();
evento.addEventListener(model, 'change', function() {
localStorage.setItem('todos-maria', JSON.stringify(model.toJSON()));
});
}
else {
model = new checkit.TodosModel();
}
maria.on(window, 'load', function() {
var model;
if ((typeof localStorage === 'object') && (typeof JSON === 'object')) {
var store = localStorage.getItem('todos-maria');
model = store ? checkit.TodosModel.fromJSON(JSON.parse(store)) :
new checkit.TodosModel();
maria.on(model, 'change', function() {
localStorage.setItem('todos-maria', JSON.stringify(model.toJSON()));
});
}
else {
model = new checkit.TodosModel();
}
var view = new checkit.TodosAppView(model);
document.body.appendChild(view.build());
var routes = {
'/': function() {
model.setMode('all');
},
'/active': function() {
model.setMode('incompleted');
},
'/completed': function() {
model.setMode('completed');
}
};
var router = Router(routes);
router.init();
var view = new checkit.TodosAppView(model);
document.body.appendChild(view.build());
});
/*jshint strict: false */
/*global maria, checkit */
maria.Controller.subclass(checkit, 'TodoController', {
properties: {
onMouseoverRoot: function() {
this.getView().showHoverState();
},
onMouseoutRoot: function() {
this.getView().hideHoverState();
},
onClickCheck: function() {
this.getModel().toggleDone();
},
onClickDestroy: function() {
this.getModel().destroy();
},
onDblclickDisplay: function() {
this.getView().showEdit();
},
onKeyupInput: function() {
var view = this.getView();
if (/\S/.test(view.getInputValue())) {
view.showToolTip();
} else {
view.hideToolTip();
}
},
onKeypressInput: function(evt) {
if (evt.keyCode === 13) {
this.onBlurInput();
}
},
onBlurInput: function() {
var view = this.getView();
var value = view.getInputValue();
view.hideToolTip();
view.showDisplay();
if (!/^\s*$/.test(value)) {
this.getModel().setContent(value);
}
}
}
properties: {
onClickDestroy: function() {
this.getModel().destroy();
},
onClickToggle: function() {
this.getModel().toggleCompleted();
},
onDblclickLabel: function() {
this.getView().showEdit();
},
onKeyupEdit: function(evt) {
if (checkit.isEnterKeyCode(evt.keyCode)) {
this.onBlurEdit();
}
},
onBlurEdit: function() {
var model = this.getModel();
var view = this.getView();
var value = view.getInputValue();
view.showDisplay();
if (checkit.isBlank(value)) {
model.destroy();
}
else {
model.setTitle(value);
}
}
}
});
/*jshint strict: false */
/*global maria, checkit */
maria.Controller.subclass(checkit, 'TodosAppController', {
properties: {
onKeyupNewTodo: function(evt) {
if (checkit.isEnterKeyCode(evt.keyCode)) {
var view = this.getView();
var value = view.getInputValue();
if (!checkit.isBlank(value)) {
var todo = new checkit.TodoModel();
todo.setTitle(value);
this.getModel().add(todo);
view.clearInput();
}
}
},
onClickToggleAll: function() {
var model = this.getModel();
if (model.isAllCompleted()) {
model.markAllIncompleted();
}
else {
model.markAllCompleted();
}
},
onClickClearCompleted: function() {
this.getModel().deleteCompleted();
}
}
});
maria.Controller.subclass(checkit, 'TodosInputController', {
properties: {
onFocusInput: function() {
this.onKeyupInput();
},
onBlurInput: function() {
this.getView().hideToolTip();
},
onKeyupInput: function() {
var view = this.getView();
if (/\S/.test(view.getInputValue())) {
view.showToolTip();
} else {
view.hideToolTip();
}
},
onKeypressInput: function(evt) {
if (evt.keyCode != 13) {
return;
}
var view = this.getView();
var value = view.getInputValue();
if (/^\s*$/.test(value)) { // don't create an empty Todo
return;
}
var todo = new checkit.TodoModel();
todo.setContent(value);
this.getModel().add(todo);
view.clearInput();
}
}
});
maria.Controller.subclass(checkit, 'TodosToolbarController', {
properties: {
onClickAllCheckbox: function() {
var model = this.getModel();
if (model.isAllDone()) {
model.markAllUndone();
} else {
model.markAllDone();
}
},
onClickMarkAllDone: function() {
this.getModel().markAllDone();
},
onClickMarkAllUndone: function() {
this.getModel().markAllUndone();
},
onClickDeleteDone: function() {
this.getModel().deleteDone();
}
}
});
/*jshint strict: false */
/*global maria, checkit */
maria.Model.subclass(checkit, 'TodoModel', {
properties: {
_content: '',
_isDone: false,
getContent: function() {
return this._content;
},
setContent: function(content) {
content = ('' + content).replace(/^\s+|\s+$/g, '');
if (this._content !== content) {
this._content = content;
this.dispatchEvent({type: 'change'});
}
},
isDone: function() {
return this._isDone;
},
setDone: function(isDone) {
isDone = !!isDone;
if (this._isDone !== isDone) {
this._isDone = isDone;
this.dispatchEvent({type: 'change'});
}
},
toggleDone: function() {
this.setDone(!this.isDone());
},
toJSON: function() {
return {
content: this._content,
is_done: this._isDone
};
}
}
properties: {
_title: '',
_completed: false,
getTitle: function() {
return this._title;
},
setTitle: function(title) {
title = ('' + title).trim();
if (this._title !== title) {
this._title = title;
this.dispatchEvent({type: 'change'});
}
},
isCompleted: function() {
return this._completed;
},
setCompleted: function(completed) {
completed = !!completed;
if (this._completed !== completed) {
this._completed = completed;
this.dispatchEvent({type: 'change'});
}
},
toggleCompleted: function() {
this.setCompleted(!this.isCompleted());
},
toJSON: function() {
return {
title: this._title,
completed: this._completed
};
}
}
});
checkit.TodoModel.fromJSON = function(todoJSON) {
var model = new checkit.TodoModel();
model._content = todoJSON.content;
model._isDone = todoJSON.is_done;
return model;
var model = new checkit.TodoModel();
model._title = todoJSON.title;
model._completed = todoJSON.completed;
return model;
};
/*jshint strict: false */
/*global maria, checkit */
maria.SetModel.subclass(checkit, 'TodosModel', {
properties: {
isEmpty: function() {
return this.length === 0;
},
getDone: function() {
return this.filter(function(todo) {
return todo.isDone();
});
},
getUndone: function() {
return this.filter(function(todo) {
return !todo.isDone();
});
},
isAllDone: function() {
return this.length > 0 &&
(this.getDone().length === this.length);
},
markAllDone: function() {
this.forEach(function(todo) {
todo.setDone(true);
});
},
markAllUndone: function() {
this.forEach(function(todo) {
todo.setDone(false);
});
},
deleteDone: function() {
this['delete'].apply(this, this.getDone());
},
toJSON: function() {
return this.map(function(todo) {
return todo.toJSON();
});
}
}
properties: {
_mode: 'all',
getPossibleModes: function() {
return ['all', 'incompleted', 'completed'];
},
getMode: function() {
return this._mode;
},
setMode: function(mode) {
if (this.getPossibleModes().some(function(m) {return m === mode;})) {
if (this._mode !== mode) {
this._mode = mode;
this.dispatchEvent({type: 'change'});
}
}
else {
throw new Error('checkit.TodosModel.prototype.setMode: unsupported mode "'+mode+'".');
}
},
getCompleted: function() {
return this.filter(function(todo) {
return todo.isCompleted();
});
},
getIncompleted: function() {
return this.filter(function(todo) {
return !todo.isCompleted();
});
},
isAllCompleted: function() {
return (this.length > 0) && (this.getCompleted().length === this.length);
},
markAllCompleted: function() {
this.forEach(function(todo) {
todo.setCompleted(true);
});
},
markAllIncompleted: function() {
this.forEach(function(todo) {
todo.setCompleted(false);
});
},
deleteCompleted: function() {
this['delete'].apply(this, this.getCompleted());
},
toJSON: function() {
return this.map(function(todo) {
return todo.toJSON();
});
}
}
});
checkit.TodosModel.fromJSON = function(todosJSON) {
var model = new checkit.TodosModel();
for (var i = 0, ilen = todosJSON.length; i < ilen; i++) {
model.add(checkit.TodoModel.fromJSON(todosJSON[i]));
}
return model;
var model = new checkit.TodosModel();
for (var i = 0, ilen = todosJSON.length; i < ilen; i++) {
model.add(checkit.TodoModel.fromJSON(todosJSON[i]));
}
return model;
};
var checkit = checkit || {};
/*jshint unused:false */
var checkit = {};
/*global checkit */
// In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part
// of the server/build functionality.
......@@ -7,14 +9,11 @@
// included here.
//
checkit.TodoTemplate =
'<li class="todo">' +
'<div class="display">' +
'<input class="check" type="checkbox">' +
'<span class="todo-content"></span>' +
'<span class="todo-destroy">delete</span>' +
'</div>' +
'<div class="edit">' +
'<input class="todo-input" type="text">' +
'<span class="ui-tooltip-top" style="display:none;">Press enter to update this task.</span>' +
'</div>' +
'</li>';
'<li>' +
'<div class="view">' +
'<input class="toggle" type="checkbox">' +
'<label></label>' +
'<button class="destroy"></span>' +
'</div>' +
'<input class="edit">' +
'</li>';
// In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part
// of the server/build functionality.
//
// Due to the limitations of a simple example that does not require
// any special server environment to try, the manually compiled version is
// included here.
//
checkit.TodosAppTemplate =
'<div class="todos-app">' +
'<h1>Todos</h1>' +
'<div class="content"></div>' +
'<p class="copyright">Another fine widget from AlphaBeta.</p>' +
'</div>';
// In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part
// of the server/build functionality.
//
// Due to the limitations of a simple example that does not require
// any special server environment to try, the manually compiled version is
// included here.
//
checkit.TodosInputTemplate =
'<div class="create-todo">' +
'<input class="new-todo" placeholder="What needs to be done?" type="text">' +
'<span class="ui-tooltip-top" style="display:none;">Press enter to add this task.</span>' +
'</div>';
// In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part
// of the server/build functionality.
//
// Due to the limitations of a simple example that does not require
// any special server environment to try, the manually compiled version is
// included here.
//
checkit.TodosListTemplate =
'<ul class="todos-list"></ul>';
// In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part
// of the server/build functionality.
//
// Due to the limitations of a simple example that does not require
// any special server environment to try, the manually compiled version is
// included here.
//
checkit.TodosStatsTemplate =
'<div class="todos-stats"><strong class="todos-count"></strong> todos in list</div>';
/*global checkit */
// In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part
// of the server/build functionality.
//
// Due to the limitations of a simple example that does not require
// any special server environment to try, the manually compiled version is
// included here.
//
checkit.TodosAppTemplate =
'<section id="todoapp">' +
'<header id="header">' +
'<h1>todos</h1>' +
'<input id="new-todo" placeholder="What needs to be done?" autofocus>' +
'</header>' +
'<section id="main">' +
'<input id="toggle-all" type="checkbox">' +
'<label for="toggle-all">Mark all as completed</label>' +
'<ul id="todo-list"></ul>' +
'</section>' +
'<footer id="footer">' +
'<span id="todo-count"></span>' +
'<ul id="filters">' +
'<li>' +
'<a class="all-filter" href="#/">All</a>' +
'</li>' +
'<li>' +
'<a class="incompleted-filter" href="#/active">Active</a>' +
'</li>' +
'<li>' +
'<a class="completed-filter" href="#/completed">Completed</a>' +
'</li>' +
'</ul>' +
'<button id="clear-completed"></button>' +
'</footer>' +
'</section>' +
'<footer id="info">' +
'<p>Double-click to edit a todo</p>' +
'<p>Inspired by the official <a href="https://github.com/maccman/spine.todos">Spine.Todos</a></p>' +
'<p>Created by <a href="http://github.com/petermichaux">Peter Michaux</a></p>' +
'</footer>';
// In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part
// of the server/build functionality.
//
// Due to the limitations of a simple example that does not require
// any special server environment to try, the manually compiled version is
// included here.
//
checkit.TodosToolbarTemplate =
'<ul class="todos-toolbar">' +
'<li><input type="checkbox" class="allCheckbox"></li> ' +
'<li class="markallDone">mark all as complete</li> ' +
'<li class="markallUndone">mark all as incomplete</li> ' +
'<li class="deleteComplete">delete complete</li>' +
'</ul>';
/*jshint strict: false */
/*global checkit */
checkit.isBlank = function(str) {
return (/^\s*$/).test(str);
};
checkit.escapeHTML = function(str) {
return str.replace('&', '&amp;').replace('<', '&lt;');
};
checkit.isEnterKeyCode = function(keyCode) {
return keyCode === 13;
};
/*jshint strict: false */
/*global maria, aristocrat, checkit */
maria.ElementView.subclass(checkit, 'TodoView', {
uiActions: {
'mouseover .todo' : 'onMouseoverRoot' ,
'mouseout .todo' : 'onMouseoutRoot' ,
'click .check' : 'onClickCheck' ,
'click .todo-destroy': 'onClickDestroy' ,
'dblclick .todo-content': 'onDblclickDisplay',
'keyup .todo-input' : 'onKeyupInput' ,
'keypress .todo-input' : 'onKeypressInput' ,
'blur .todo-input' : 'onBlurInput'
},
properties: {
buildData: function() {
var model = this.getModel();
var content = model.getContent();
this.find('.todo-content').innerHTML =
content.replace('&', '&amp;').replace('<', '&lt;');
this.find('.check').checked = model.isDone();
aristocrat[model.isDone() ? 'addClass' : 'removeClass'](this.find('.todo'), 'done');
},
update: function() {
this.buildData();
},
showEdit: function() {
var input = this.find('.todo-input');
input.value = this.getModel().getContent();
aristocrat.addClass(this.find('.todo'), 'editing');
input.focus();
},
showDisplay: function() {
aristocrat.removeClass(this.find('.todo'), 'editing');
},
getInputValue: function() {
return this.find('.todo-input').value;
},
showHoverState: function() {
aristocrat.addClass(this.find('.todo'), 'todo-hover');
},
hideHoverState: function() {
aristocrat.removeClass(this.find('.todo'), 'todo-hover');
},
showToolTip: function() {
this.find('.ui-tooltip-top').style.display = 'block';
},
hideToolTip: function() {
this.find('.ui-tooltip-top').style.display = 'none';
}
}
uiActions: {
'click .destroy': 'onClickDestroy' ,
'click .toggle' : 'onClickToggle' ,
'dblclick label' : 'onDblclickLabel',
'keyup .edit' : 'onKeyupEdit' ,
'blur .edit' : 'onBlurEdit'
},
properties: {
buildData: function() {
var model = this.getModel();
var item = this.find('li');
aristocrat.removeClass(item, '(in|)completed');
aristocrat.addClass(item, (model.isCompleted() ? 'completed' : 'incompleted'));
this.find('label').innerHTML = checkit.escapeHTML(model.getTitle());
this.find('.toggle').checked = model.isCompleted();
},
update: function() {
this.buildData();
},
showEdit: function() {
var input = this.find('.edit');
input.value = this.getModel().getTitle();
aristocrat.addClass(this.find('li'), 'editing');
input.focus();
},
showDisplay: function() {
aristocrat.removeClass(this.find('li'), 'editing');
},
getInputValue: function() {
return this.find('.edit').value;
}
}
});
// All the subviews of a TodosAppView will always have
// the same model as the TodosAppView has.
//
maria.ElementView.subclass(checkit, 'TodosAppView', {
properties: {
getContainerEl: function() {
return this.find('.content'); // child views will be appended to this element
},
initialize: function() {
this.appendChild(new checkit.TodosInputView());
this.appendChild(new checkit.TodosToolbarView());
this.appendChild(new checkit.TodosListView());
this.appendChild(new checkit.TodosStatsView());
},
insertBefore: function(newChild, oldChild) {
newChild.setModel(this.getModel());
maria.ElementView.prototype.insertBefore.call(this, newChild, oldChild);
},
setModel: function(model) {
if (model !== this.getModel()) {
maria.ElementView.prototype.setModel.call(this, model);
var childViews = this.childNodes;
for (var i = 0, ilen = childViews.length; i < ilen; i++) {
childViews[i].setModel(model);
}
}
}
}
});
maria.ElementView.subclass(checkit, 'TodosInputView', {
uiActions: {
'focus .new-todo': 'onFocusInput' ,
'blur .new-todo': 'onBlurInput' ,
'keyup .new-todo': 'onKeyupInput' ,
'keypress .new-todo': 'onKeypressInput'
},
properties: {
getInputValue: function() {
return this.find('.new-todo').value;
},
clearInput: function() {
this.find('.new-todo').value = '';
},
showToolTip: function() {
this.find('.ui-tooltip-top').style.display = 'block';
},
hideToolTip: function() {
this.find('.ui-tooltip-top').style.display = 'none';
}
}
});
maria.SetView.subclass(checkit, 'TodosListView', {
properties: {
createChildView: function(todoModel) {
return new checkit.TodoView(todoModel);
}
}
});
maria.ElementView.subclass(checkit, 'TodosStatsView', {
properties: {
buildData: function() {
this.find('.todos-count').innerHTML = this.getModel().length;
},
update: function() {
this.buildData();
}
}
});
maria.ElementView.subclass(checkit, 'TodosToolbarView', {
uiActions: {
'click .allCheckbox' : 'onClickAllCheckbox' ,
'click .markallDone' : 'onClickMarkAllDone' ,
'click .markallUndone' : 'onClickMarkAllUndone',
'click .deleteComplete': 'onClickDeleteDone'
},
properties: {
buildData: function() {
var model = this.getModel();
var checkbox = this.find('.allCheckbox');
checkbox.checked = model.isAllDone();
checkbox.disabled = model.isEmpty();
},
update: function() {
this.buildData();
}
}
});
/*jshint strict: false */
/*global maria, aristocrat, checkit */
maria.SetView.subclass(checkit, 'TodosAppView', {
uiActions: {
'keyup #new-todo' : 'onKeyupNewTodo' ,
'click #toggle-all' : 'onClickToggleAll' ,
'click #clear-completed': 'onClickClearCompleted'
},
properties: {
buildData: function() {
var model = this.getModel();
var length = model.length;
this.find('#main').style.display = (length > 0) ? '' : 'none';
this.find('#footer').style.display = (length > 0) ? '' : 'none';
var checkbox = this.find('#toggle-all');
checkbox.checked = model.isAllCompleted();
checkbox.disabled = model.isEmpty();
var todoList = this.find('#todo-list');
model.getPossibleModes().forEach(function(mode) {
aristocrat.removeClass(todoList, mode);
});
aristocrat.addClass(todoList, model.getMode());
var incompletedLength = model.getIncompleted().length;
this.find('#todo-count').innerHTML =
'<strong>' + incompletedLength + '</strong> ' +
((incompletedLength === 1) ? 'item' : 'items') +
' left';
var selected = this.find('.selected');
if (selected) {
aristocrat.removeClass(selected, 'selected');
}
aristocrat.addClass(this.find('.' + model.getMode() + '-filter'), 'selected');
var completedLength = model.getCompleted().length;
var clearButton = this.find('#clear-completed');
clearButton.style.display = (completedLength > 0) ? '' : 'none';
clearButton.innerHTML = 'Clear completed (' + completedLength + ')';
},
update: function(evt) {
maria.SetView.prototype.update.call(this, evt);
this.buildData();
},
getContainerEl: function() {
// child views will be appended to this element
return this.find('#todo-list');
},
createChildView: function(todoModel) {
return new checkit.TodoView(todoModel);
},
getInputValue: function() {
return this.find('#new-todo').value;
},
clearInput: function() {
this.find('#new-todo').value = '';
}
}
});
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