Commit aa503579 authored by Sindre Sorhus's avatar Sindre Sorhus

Backbone.Marionette cleanup

- Convert to tabs
- Trim trailing whitespace
parent 9b81a25c
# Backbone.Marionette TodoMVC app
Backbone.Marionette is a composite application library for Backbone.js that aims to simplify the construction of large scale JavaScript applications. It is a collection of common design and implementation patterns found in the applications that Derick Bailey has been building with Backbone, and includes various pieces inspired by composite application architectures, such as Microsoft's "Prism" framework. Backbone.Marionette is a composite application library for Backbone.js that aims to simplify the construction of large scale JavaScript applications. It is a collection of common design and implementation patterns found in the applications that Derick Bailey has been building with Backbone, and includes various pieces inspired by composite application architectures, such as Microsoft's "Prism" framework.
This implementation of the application uses Marionette's module system. Variations using RequireJS and a more classic approach to JavaScript modules are also [available](https://github.com/marionettejs/backbone.marionette/wiki/Projects-and-websites-using-marionette). This implementation of the application uses Marionette's module system. Variations using RequireJS and a more classic approach to JavaScript modules are also [available](https://github.com/marionettejs/backbone.marionette/wiki/Projects-and-websites-using-marionette).
\ No newline at end of file
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="utf-8">
<meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Marionette • TodoMVC</title>
<title>Marionette • TodoMVC</title> <link rel="stylesheet" href="../../../assets/base.css">
<link rel="stylesheet" href="../../../assets/base.css"> <link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" href="css/app.css"> <!--[if IE]>
<!--[if IE]> <script src="../../../assets/ie.js"></script>
<script src="../../../assets/ie.js"></script> <![endif]-->
<![endif]--> <script type="text/html" id="template-footer">
<span id="todo-count"><strong></strong> items left</span>
<script type="text/html" id="template-footer"> <ul id="filters">
<span id="todo-count"><strong></strong> items left</span> <li>
<ul id="filters"> <a href="#">All</a>
<li> </li>
<a href="#">All</a> <li>
</li> <a href="#active">Active</a>
<li> </li>
<a href="#active">Active</a> <li>
</li> <a href="#completed">Completed</a>
<li> </li>
<a href="#completed">Completed</a> </ul>
</li> <button id="clear-completed">Clear completed</button>
</ul> </script>
<button id="clear-completed">Clear completed</button> <script type="text/html" id="template-header">
</script> <h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus>
<script type="text/html" id="template-header"> </script>
<h1>todos</h1> <script type="text/html" id="template-todoItemView">
<input id="new-todo" placeholder="What needs to be done?" autofocus> <div class="view">
</script> <input class="toggle" type="checkbox" <% if (completed) { %>checked<% } %>>
<label><%= title %></label>
<script type="text/html" id="template-todoItemView"> <button class="destroy"></button>
<div class="view"> </div>
<input class="toggle" type="checkbox" <% if (completed) { %>checked<% } %>> <input class="edit" value="<%= title %>">
<label><%= title %></label> </script>
<button class="destroy"></button> <script type="text/html" id="template-todoListCompositeView">
</div> <input id="toggle-all" type="checkbox">
<input class="edit" value="<%= title %>"> <label for="toggle-all">Mark all as complete</label>
</script> <ul id="todo-list"></ul>
</script>
<script type="text/html" id="template-todoListCompositeView"> </head>
<input id="toggle-all" type="checkbox"> <body>
<label for="toggle-all">Mark all as complete</label> <section id="todoapp">
<ul id="todo-list"></ul> <header id="header"></header>
</script> <section id="main"></section>
<footer id="footer"></footer>
</head> </section>
<footer id="info">
<body> <p>Double-click to edit a todo</p>
<section id="todoapp"> <p>
<header id="header"></header> Created by <a href="http://github.com/jsoverson">Jarrod Overson</a>
<section id="main"></section> and <a href="http://github.com/derickbailey">Derick Bailey</a>
<footer id="footer"></footer> using <a href="http://marionettejs.com">Backbone.Marionette</a>
</section> </p>
<p>Further variations on the Backbone.Marionette app are also <a href="https://github.com/marionettejs/backbone.marionette/wiki/Projects-and-websites-using-marionette">available</a>.</p>
<footer id="info"> </footer>
<p>Double-click to edit a todo</p> <!-- vendor libraries -->
<p> <script src="../../../assets/base.js"></script>
Created by <a href="http://github.com/jsoverson">Jarrod Overson</a> <script src="../../../assets/jquery.min.js"></script>
and <a href="http://github.com/derickbailey">Derick Bailey</a>, <script src="../../../assets/lodash.min.js"></script>
using <a href="http://marionettejs.com">Backbone.Marionette</a> <script src="js/lib/backbone.js"></script>
</p> <script src="js/lib/backbone-localStorage.js"></script>
<p>Further variations on the Backbone.Marionette app are also <a href="https://github.com/marionettejs/backbone.marionette/wiki/Projects-and-websites-using-marionette">available</a>.</p> <script src="js/lib/backbone.marionette.js"></script>
</footer> <!-- application -->
<script src="js/TodoMVC.js"></script>
<!-- vendor libraries --> <script src="js/TodoMVC.Todos.js"></script>
<script src="../../../assets/base.js"></script> <script src="js/TodoMVC.Layout.js"></script>
<script src="../../../assets/jquery.min.js"></script> <script src="js/TodoMVC.TodoList.Views.js"></script>
<script src="../../../assets/lodash.min.js"></script> <script src="js/TodoMVC.TodoList.js"></script>
<script src="js/lib/backbone.js"></script> <script>
<script src="js/lib/backbone-localStorage.js"></script> $(function(){
<script src="js/lib/backbone.marionette.js"></script> // Start the TodoMVC app (defined in js/TodoMVC.js)
TodoMVC.start();
<!-- application --> });
<script src="js/TodoMVC.js"></script> </script>
<script src="js/TodoMVC.Todos.js"></script> </body>
<script src="js/TodoMVC.Layout.js"></script>
<script src="js/TodoMVC.TodoList.Views.js"></script>
<script src="js/TodoMVC.TodoList.js"></script>
<script>
$(function(){
// Start the TodoMVC app (defined in js/TodoMVC.js)
TodoMVC.start();
});
</script>
</body>
</html> </html>
TodoMVC.module("Layout", function(Layout, App, Backbone, Marionette, $, _){ TodoMVC.module("Layout", function(Layout, App, Backbone, Marionette, $, _){
// Layout Header View // Layout Header View
// ------------------ // ------------------
Layout.Header = Backbone.Marionette.ItemView.extend({ Layout.Header = Backbone.Marionette.ItemView.extend({
template : "#template-header", template : "#template-header",
// UI bindings create cached attributes that // UI bindings create cached attributes that
// point to jQuery selected objects // point to jQuery selected objects
ui : { ui : {
input : '#new-todo' input : '#new-todo'
}, },
events : { events : {
'keypress #new-todo': 'onInputKeypress' 'keypress #new-todo': 'onInputKeypress'
}, },
onInputKeypress : function(evt) { onInputKeypress : function(evt) {
var ENTER_KEY = 13; var ENTER_KEY = 13;
var todoText = this.ui.input.val().trim(); var todoText = this.ui.input.val().trim();
if ( evt.which === ENTER_KEY && todoText ) { if ( evt.which === ENTER_KEY && todoText ) {
this.collection.create({ this.collection.create({
title : todoText title : todoText
}); });
this.ui.input.val(''); this.ui.input.val('');
} }
} }
}); });
// Layout Footer View // Layout Footer View
// ------------------ // ------------------
Layout.Footer = Backbone.Marionette.Layout.extend({ Layout.Footer = Backbone.Marionette.Layout.extend({
template : "#template-footer", template : "#template-footer",
// UI bindings create cached attributes that // UI bindings create cached attributes that
// point to jQuery selected objects // point to jQuery selected objects
ui : { ui : {
count : '#todo-count strong', count : '#todo-count strong',
filters : '#filters a' filters : '#filters a'
}, },
events : { events : {
'click #clear-completed' : 'onClearClick' 'click #clear-completed' : 'onClearClick'
}, },
initialize : function() { initialize : function() {
this.bindTo(App.vent, 'todoList:filter', this.updateFilterSelection, this); this.bindTo(App.vent, 'todoList:filter', this.updateFilterSelection, this);
this.bindTo(this.collection, 'all', this.updateCount, this); this.bindTo(this.collection, 'all', this.updateCount, this);
}, },
onRender : function() { onRender : function() {
this.updateCount(); this.updateCount();
}, },
updateCount : function() { updateCount : function() {
var count = this.collection.getActive().length; var count = this.collection.getActive().length;
this.ui.count.html(count); this.ui.count.html(count);
if (count === 0) { if (count === 0) {
this.$el.parent().hide(); this.$el.parent().hide();
} else { } else {
this.$el.parent().show(); this.$el.parent().show();
} }
}, },
updateFilterSelection : function(filter) { updateFilterSelection : function(filter) {
this.ui.filters this.ui.filters
.removeClass('selected') .removeClass('selected')
.filter('[href="#' + filter + '"]') .filter('[href="#' + filter + '"]')
.addClass('selected'); .addClass('selected');
}, },
onClearClick : function() { onClearClick : function() {
var completed = this.collection.getCompleted(); var completed = this.collection.getCompleted();
completed.forEach(function destroy(todo) { completed.forEach(function destroy(todo) {
todo.destroy(); todo.destroy();
}); });
} }
}); });
}); });
TodoMVC.module("TodoList.Views", function(Views, App, Backbone, Marionette, $, _){ TodoMVC.module("TodoList.Views", function(Views, App, Backbone, Marionette, $, _){
// Todo List Item View // Todo List Item View
// ------------------- // -------------------
// //
// Display an individual todo item, and respond to changes // Display an individual todo item, and respond to changes
// that are made to the item, including marking completed. // that are made to the item, including marking completed.
Views.ItemView = Marionette.ItemView.extend({ Views.ItemView = Marionette.ItemView.extend({
tagName : 'li', tagName : 'li',
template : "#template-todoItemView", template : "#template-todoItemView",
ui : { ui : {
edit : '.edit' edit : '.edit'
}, },
events : { events : {
'click .destroy' : 'destroy', 'click .destroy' : 'destroy',
'dblclick label' : 'onEditClick', 'dblclick label' : 'onEditClick',
'keypress .edit' : 'onEditKeypress', 'keypress .edit' : 'onEditKeypress',
'click .toggle' : 'toggle' 'click .toggle' : 'toggle'
}, },
initialize : function() { initialize : function() {
this.bindTo(this.model, 'change', this.render, this); this.bindTo(this.model, 'change', this.render, this);
}, },
onRender : function() { onRender : function() {
this.$el.removeClass('active completed'); this.$el.removeClass('active completed');
if (this.model.get('completed')) this.$el.addClass('completed'); if (this.model.get('completed')) this.$el.addClass('completed');
else this.$el.addClass('active'); else this.$el.addClass('active');
}, },
destroy : function() { destroy : function() {
this.model.destroy(); this.model.destroy();
}, },
toggle : function() { toggle : function() {
this.model.toggle().save(); this.model.toggle().save();
}, },
onEditClick : function() { onEditClick : function() {
this.$el.addClass('editing'); this.$el.addClass('editing');
this.ui.edit.focus(); this.ui.edit.focus();
}, },
onEditKeypress : function(evt) { onEditKeypress : function(evt) {
var ENTER_KEY = 13; var ENTER_KEY = 13;
var todoText = this.ui.edit.val().trim(); var todoText = this.ui.edit.val().trim();
if ( evt.which === ENTER_KEY && todoText ) { if ( evt.which === ENTER_KEY && todoText ) {
this.model.set('title', todoText).save(); this.model.set('title', todoText).save();
this.$el.removeClass('editing'); this.$el.removeClass('editing');
} }
} }
}); });
// Item List View // Item List View
// -------------- // --------------
// //
// Controls the rendering of the list of items, including the // Controls the rendering of the list of items, including the
// filtering of activs vs completed items for display. // filtering of activs vs completed items for display.
Views.ListView = Backbone.Marionette.CompositeView.extend({ Views.ListView = Backbone.Marionette.CompositeView.extend({
template : "#template-todoListCompositeView", template : "#template-todoListCompositeView",
itemView : Views.ItemView, itemView : Views.ItemView,
itemViewContainer : '#todo-list', itemViewContainer : '#todo-list',
ui : { ui : {
toggle : '#toggle-all' toggle : '#toggle-all'
}, },
events : { events : {
'click #toggle-all' : 'onToggleAllClick' 'click #toggle-all' : 'onToggleAllClick'
}, },
initialize : function() { initialize : function() {
this.bindTo(this.collection, 'all', this.update, this); this.bindTo(this.collection, 'all', this.update, this);
}, },
onRender : function() { onRender : function() {
this.update(); this.update();
}, },
update : function() { update : function() {
function reduceCompleted(left, right) { return left && right.get('completed'); } function reduceCompleted(left, right) { return left && right.get('completed'); }
var allCompleted = this.collection.reduce(reduceCompleted,true); var allCompleted = this.collection.reduce(reduceCompleted,true);
this.ui.toggle.prop('checked', allCompleted); this.ui.toggle.prop('checked', allCompleted);
if (this.collection.length === 0) { if (this.collection.length === 0) {
this.$el.parent().hide(); this.$el.parent().hide();
} else { } else {
this.$el.parent().show(); this.$el.parent().show();
} }
}, },
onToggleAllClick : function(evt) { onToggleAllClick : function(evt) {
var isChecked = evt.currentTarget.checked; var isChecked = evt.currentTarget.checked;
this.collection.each(function(todo){ this.collection.each(function(todo){
todo.save({'completed': isChecked}); todo.save({'completed': isChecked});
}); });
} }
}); });
// Application Event Handlers // Application Event Handlers
// -------------------------- // --------------------------
// //
// Handler for filtering the list of items by showing and // Handler for filtering the list of items by showing and
// hiding through the use of various CSS classes // hiding through the use of various CSS classes
App.vent.on('todoList:filter',function(filter) { App.vent.on('todoList:filter',function(filter) {
filter = filter || 'all'; filter = filter || 'all';
$('#todoapp').attr('class', 'filter-' + filter); $('#todoapp').attr('class', 'filter-' + filter);
}); });
}); });
TodoMVC.module("TodoList", function(TodoList, App, Backbone, Marionette, $, _){ TodoMVC.module("TodoList", function(TodoList, App, Backbone, Marionette, $, _){
// TodoList Router // TodoList Router
// --------------- // ---------------
// //
// Handle routes to show the active vs complete todo items // Handle routes to show the active vs complete todo items
TodoList.Router = Marionette.AppRouter.extend({ TodoList.Router = Marionette.AppRouter.extend({
appRoutes : { appRoutes : {
"*filter": "filterItems" "*filter": "filterItems"
} }
}); });
// TodoList Controller (Mediator) // TodoList Controller (Mediator)
// ------------------------------ // ------------------------------
// //
// Control the workflow and logic that exists at the application // Control the workflow and logic that exists at the application
// level, above the implementation detail of views and models // level, above the implementation detail of views and models
TodoList.Controller = function(){ TodoList.Controller = function(){
this.todoList = new App.Todos.TodoList(); this.todoList = new App.Todos.TodoList();
}; };
_.extend(TodoList.Controller.prototype, { _.extend(TodoList.Controller.prototype, {
// Start the app by showing the appropriate views // Start the app by showing the appropriate views
// and fetching the list of todo items, if there are any // and fetching the list of todo items, if there are any
start: function(){ start: function(){
this.showHeader(this.todoList); this.showHeader(this.todoList);
this.showFooter(this.todoList); this.showFooter(this.todoList);
this.showTodoList(this.todoList); this.showTodoList(this.todoList);
this.todoList.fetch(); this.todoList.fetch();
}, },
showHeader: function(todoList){ showHeader: function(todoList){
var header = new App.Layout.Header({ var header = new App.Layout.Header({
collection: todoList collection: todoList
}); });
App.header.show(header); App.header.show(header);
}, },
showFooter: function(todoList){ showFooter: function(todoList){
var footer = new App.Layout.Footer({ var footer = new App.Layout.Footer({
collection: todoList collection: todoList
}); });
App.footer.show(footer); App.footer.show(footer);
}, },
showTodoList: function(todoList){ showTodoList: function(todoList){
App.main.show(new TodoList.Views.ListView({ App.main.show(new TodoList.Views.ListView({
collection : todoList collection : todoList
})); }));
}, },
// Set the filter to show complete or all items // Set the filter to show complete or all items
filterItems: function(filter){ filterItems: function(filter){
App.vent.trigger("todoList:filter", filter.trim() || ""); App.vent.trigger("todoList:filter", filter.trim() || "");
} }
}); });
// TodoList Initializer // TodoList Initializer
// -------------------- // --------------------
// //
// Get the TodoList up and running by initializing the mediator // Get the TodoList up and running by initializing the mediator
// when the the application is started, pulling in all of the // when the the application is started, pulling in all of the
// existing Todo items and displaying them. // existing Todo items and displaying them.
TodoList.addInitializer(function(){ TodoList.addInitializer(function(){
var controller = new TodoList.Controller(); var controller = new TodoList.Controller();
new TodoList.Router({ new TodoList.Router({
controller: controller controller: controller
}); });
controller.start(); controller.start();
}); });
}); });
TodoMVC.module("Todos", function(Todos, App, Backbone, Marionette, $, _){ TodoMVC.module("Todos", function(Todos, App, Backbone, Marionette, $, _){
// Todo Model // Todo Model
// ---------- // ----------
Todos.Todo = Backbone.Model.extend({ Todos.Todo = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage('todos-backbone'), localStorage: new Backbone.LocalStorage('todos-backbone'),
defaults: { defaults: {
title : '', title : '',
completed : false, completed : false,
created : 0 created : 0
}, },
initialize : function() { initialize : function() {
if (this.isNew()) this.set('created', Date.now()); if (this.isNew()) this.set('created', Date.now());
}, },
toggle : function() { toggle : function() {
return this.set('completed', !this.isCompleted()); return this.set('completed', !this.isCompleted());
}, },
isCompleted: function() { isCompleted: function() {
return this.get('completed'); return this.get('completed');
} }
}); });
// Todo Collection // Todo Collection
// --------------- // ---------------
Todos.TodoList = Backbone.Collection.extend({ Todos.TodoList = Backbone.Collection.extend({
model: Todos.Todo, model: Todos.Todo,
localStorage: new Backbone.LocalStorage('todos-backbone'), localStorage: new Backbone.LocalStorage('todos-backbone'),
getCompleted: function() { getCompleted: function() {
return this.filter(this._isCompleted); return this.filter(this._isCompleted);
}, },
getActive: function() { getActive: function() {
return this.reject(this._isCompleted); return this.reject(this._isCompleted);
}, },
comparator: function( todo ) { comparator: function( todo ) {
return todo.get('created'); return todo.get('created');
}, },
_isCompleted: function(todo){ _isCompleted: function(todo){
return todo.isCompleted(); return todo.isCompleted();
} }
}); });
}); });
var TodoMVC = new Backbone.Marionette.Application(); var TodoMVC = new Backbone.Marionette.Application();
TodoMVC.addRegions({ TodoMVC.addRegions({
header : '#header', header : '#header',
main : '#main', main : '#main',
footer : '#footer' footer : '#footer'
}); });
TodoMVC.on("initialize:after", function(){ TodoMVC.on("initialize:after", function(){
Backbone.history.start(); Backbone.history.start();
}); });
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