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 Copyright (c) 2012, Peter Michaux
All rights reserved. All rights reserved.
Licensed under the Simplified BSD License. Licensed under the Simplified BSD License.
https://github.com/petermichaux/aristocrat/blob/master/LICENSE https://github.com/petermichaux/aristocrat/blob/master/LICENSE
*/ */
var aristocrat = aristocrat || {}; var aristocrat = {};
(function() { (function() {
......
ul.completed li.incompleted,
ul.incompleted li.completed {
display: none;
}
This diff is collapsed.
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <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> </head>
<body> <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/maria/maria.js"></script>
<script src="../lib/aristocrat/aristocrat.js"></script> <script src="../lib/aristocrat/aristocrat.js"></script>
<script src="../../../../assets/director.min.js"></script>
<script src="js/namespace.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/TodoModel.js"></script>
<script src="js/models/TodosModel.js"></script> <script src="js/models/TodosModel.js"></script>
<script src="js/templates/TodosInputTemplate.js"></script> <script src="js/templates/TodosTemplate.js"></script>
<script src="js/views/TodosInputView.js"></script> <script src="js/views/TodosView.js"></script>
<script src="js/templates/TodosListTemplate.js"></script> <script src="js/controllers/TodosController.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/templates/TodoTemplate.js"></script>
<script src="js/views/TodoView.js"></script> <script src="js/views/TodoView.js"></script>
<script src="js/controllers/TodoController.js"></script> <script src="js/controllers/TodoController.js"></script>
......
maria.addEventListener(window, 'load', function() { /*jshint strict: false */
var loading = document.getElementById('loading'); /*global maria, Router, checkit */
loading.parentNode.removeChild(loading);
maria.on(window, 'load', function() {
var model; var model;
if ((typeof localStorage === 'object') && (typeof JSON === 'object')) { if ((typeof localStorage === 'object') && (typeof JSON === 'object')) {
var store = localStorage.getItem('todos-maria'); var store = localStorage.getItem('todos-maria');
model = store ? checkit.TodosModel.fromJSON(JSON.parse(store)) : model = store ? checkit.TodosModel.fromJSON(JSON.parse(store)) :
new checkit.TodosModel(); new checkit.TodosModel();
evento.addEventListener(model, 'change', function() { maria.on(model, 'change', function() {
localStorage.setItem('todos-maria', JSON.stringify(model.toJSON())); localStorage.setItem('todos-maria', JSON.stringify(model.toJSON()));
}); });
} }
...@@ -15,6 +15,20 @@ maria.addEventListener(window, 'load', function() { ...@@ -15,6 +15,20 @@ maria.addEventListener(window, 'load', function() {
model = new checkit.TodosModel(); model = new checkit.TodosModel();
} }
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); var view = new checkit.TodosAppView(model);
document.body.appendChild(view.build()); document.body.appendChild(view.build());
}); });
/*jshint strict: false */
/*global maria, checkit */
maria.Controller.subclass(checkit, 'TodoController', { maria.Controller.subclass(checkit, 'TodoController', {
properties: { properties: {
onMouseoverRoot: function() {
this.getView().showHoverState();
},
onMouseoutRoot: function() {
this.getView().hideHoverState();
},
onClickCheck: function() {
this.getModel().toggleDone();
},
onClickDestroy: function() { onClickDestroy: function() {
this.getModel().destroy(); this.getModel().destroy();
}, },
onDblclickDisplay: function() { onClickToggle: function() {
this.getView().showEdit(); this.getModel().toggleCompleted();
}, },
onKeyupInput: function() { onDblclickLabel: function() {
var view = this.getView(); this.getView().showEdit();
if (/\S/.test(view.getInputValue())) {
view.showToolTip();
} else {
view.hideToolTip();
}
}, },
onKeypressInput: function(evt) { onKeyupEdit: function(evt) {
if (evt.keyCode === 13) { if (checkit.isEnterKeyCode(evt.keyCode)) {
this.onBlurInput(); this.onBlurEdit();
} }
}, },
onBlurInput: function() { onBlurEdit: function() {
var model = this.getModel();
var view = this.getView(); var view = this.getView();
var value = view.getInputValue(); var value = view.getInputValue();
view.hideToolTip();
view.showDisplay(); view.showDisplay();
if (!/^\s*$/.test(value)) { if (checkit.isBlank(value)) {
this.getModel().setContent(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', { maria.Model.subclass(checkit, 'TodoModel', {
properties: { properties: {
_content: '', _title: '',
_isDone: false, _completed: false,
getContent: function() { getTitle: function() {
return this._content; return this._title;
}, },
setContent: function(content) { setTitle: function(title) {
content = ('' + content).replace(/^\s+|\s+$/g, ''); title = ('' + title).trim();
if (this._content !== content) { if (this._title !== title) {
this._content = content; this._title = title;
this.dispatchEvent({type: 'change'}); this.dispatchEvent({type: 'change'});
} }
}, },
isDone: function() { isCompleted: function() {
return this._isDone; return this._completed;
}, },
setDone: function(isDone) { setCompleted: function(completed) {
isDone = !!isDone; completed = !!completed;
if (this._isDone !== isDone) { if (this._completed !== completed) {
this._isDone = isDone; this._completed = completed;
this.dispatchEvent({type: 'change'}); this.dispatchEvent({type: 'change'});
} }
}, },
toggleDone: function() { toggleCompleted: function() {
this.setDone(!this.isDone()); this.setCompleted(!this.isCompleted());
}, },
toJSON: function() { toJSON: function() {
return { return {
content: this._content, title: this._title,
is_done: this._isDone completed: this._completed
}; };
} }
} }
...@@ -36,7 +39,7 @@ maria.Model.subclass(checkit, 'TodoModel', { ...@@ -36,7 +39,7 @@ maria.Model.subclass(checkit, 'TodoModel', {
checkit.TodoModel.fromJSON = function(todoJSON) { checkit.TodoModel.fromJSON = function(todoJSON) {
var model = new checkit.TodoModel(); var model = new checkit.TodoModel();
model._content = todoJSON.content; model._title = todoJSON.title;
model._isDone = todoJSON.is_done; model._completed = todoJSON.completed;
return model; return model;
}; };
/*jshint strict: false */
/*global maria, checkit */
maria.SetModel.subclass(checkit, 'TodosModel', { maria.SetModel.subclass(checkit, 'TodosModel', {
properties: { properties: {
isEmpty: function() { _mode: 'all',
return this.length === 0; 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+'".');
}
}, },
getDone: function() { getCompleted: function() {
return this.filter(function(todo) { return this.filter(function(todo) {
return todo.isDone(); return todo.isCompleted();
}); });
}, },
getUndone: function() { getIncompleted: function() {
return this.filter(function(todo) { return this.filter(function(todo) {
return !todo.isDone(); return !todo.isCompleted();
}); });
}, },
isAllDone: function() { isAllCompleted: function() {
return this.length > 0 && return (this.length > 0) && (this.getCompleted().length === this.length);
(this.getDone().length === this.length);
}, },
markAllDone: function() { markAllCompleted: function() {
this.forEach(function(todo) { this.forEach(function(todo) {
todo.setDone(true); todo.setCompleted(true);
}); });
}, },
markAllUndone: function() { markAllIncompleted: function() {
this.forEach(function(todo) { this.forEach(function(todo) {
todo.setDone(false); todo.setCompleted(false);
}); });
}, },
deleteDone: function() { deleteCompleted: function() {
this['delete'].apply(this, this.getDone()); this['delete'].apply(this, this.getCompleted());
}, },
toJSON: function() { toJSON: function() {
return this.map(function(todo) { return this.map(function(todo) {
......
var checkit = checkit || {}; /*jshint unused:false */
var checkit = {};
/*global checkit */
// In a full development environment this template would be expressed // In a full development environment this template would be expressed
// in a file containing only HTML and be compiled to the following as part // in a file containing only HTML and be compiled to the following as part
// of the server/build functionality. // of the server/build functionality.
...@@ -7,14 +9,11 @@ ...@@ -7,14 +9,11 @@
// included here. // included here.
// //
checkit.TodoTemplate = checkit.TodoTemplate =
'<li class="todo">' + '<li>' +
'<div class="display">' + '<div class="view">' +
'<input class="check" type="checkbox">' + '<input class="toggle" type="checkbox">' +
'<span class="todo-content"></span>' + '<label></label>' +
'<span class="todo-destroy">delete</span>' + '<button class="destroy"></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>' + '</div>' +
'<input class="edit">' +
'</li>'; '</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', { maria.ElementView.subclass(checkit, 'TodoView', {
uiActions: { uiActions: {
'mouseover .todo' : 'onMouseoverRoot' , 'click .destroy': 'onClickDestroy' ,
'mouseout .todo' : 'onMouseoutRoot' , 'click .toggle' : 'onClickToggle' ,
'click .check' : 'onClickCheck' , 'dblclick label' : 'onDblclickLabel',
'click .todo-destroy': 'onClickDestroy' , 'keyup .edit' : 'onKeyupEdit' ,
'dblclick .todo-content': 'onDblclickDisplay', 'blur .edit' : 'onBlurEdit'
'keyup .todo-input' : 'onKeyupInput' ,
'keypress .todo-input' : 'onKeypressInput' ,
'blur .todo-input' : 'onBlurInput'
}, },
properties: { properties: {
buildData: function() { buildData: function() {
var model = this.getModel(); var model = this.getModel();
var content = model.getContent();
this.find('.todo-content').innerHTML = var item = this.find('li');
content.replace('&', '&amp;').replace('<', '&lt;'); aristocrat.removeClass(item, '(in|)completed');
this.find('.check').checked = model.isDone(); aristocrat.addClass(item, (model.isCompleted() ? 'completed' : 'incompleted'));
aristocrat[model.isDone() ? 'addClass' : 'removeClass'](this.find('.todo'), 'done');
this.find('label').innerHTML = checkit.escapeHTML(model.getTitle());
this.find('.toggle').checked = model.isCompleted();
}, },
update: function() { update: function() {
this.buildData(); this.buildData();
}, },
showEdit: function() { showEdit: function() {
var input = this.find('.todo-input'); var input = this.find('.edit');
input.value = this.getModel().getContent(); input.value = this.getModel().getTitle();
aristocrat.addClass(this.find('.todo'), 'editing'); aristocrat.addClass(this.find('li'), 'editing');
input.focus(); input.focus();
}, },
showDisplay: function() { showDisplay: function() {
aristocrat.removeClass(this.find('.todo'), 'editing'); aristocrat.removeClass(this.find('li'), 'editing');
}, },
getInputValue: function() { getInputValue: function() {
return this.find('.todo-input').value; return this.find('.edit').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';
} }
} }
}); });
// 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