-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #282 from beastridge/gh-pages
Thorax
- Loading branch information
Showing
57 changed files
with
18,121 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
Thorax TodoMVC | ||
============== | ||
This is a modified version of the Backbone TodoMVC app that uses [Thorax](http://thoraxjs.org). | ||
|
||
The Backbone implementation has code to manage the list items which are present on the page. Thorax provides collection bindings with the `collection` helper, which eleminate the need for most of this code: | ||
|
||
{{#collection todosCollection filter="filterTodoItem" | ||
item-view="todo-item" tag="ul" id="todo-list"}} | ||
|
||
`todosCollection` was specified in js/views/app.js:initialize, all instance variables of the view are automatically made available to the associated template. The `item-view` attribute is optional for collections, but specified here since we want to initialize an `todo-item` view for each item. This class is defined in js/views/todo-item.js and is referenced here by it's `name` attribute which is defined in that file. | ||
|
||
The `filter` attribute specifies a function to be called for each model in the collection and hide or show that item depending on wether the function returns true or false. It is called when the collection is first rendered, then as models are added or a model fires a change event. If a `filter` event is triggered on the collection (which it is in routers/router.js:setFilter) it will force the collection to re-filter each model in the colleciton. | ||
|
||
In this implementation the `stats` view has it's own view class and is re-rendered instead of the `app` view being re-rendered. Thorax provides the ability to embed views by name or reference with the `view` helper: | ||
|
||
{{view "stats"}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | ||
<title>Thorax • TodoMVC</title> | ||
<link rel="stylesheet" href="../../../assets/base.css"> | ||
<!--[if IE]> | ||
<script src="../../../assets/ie.js"></script> | ||
<![endif]--> | ||
</head> | ||
<body> | ||
<script type="text/template" data-template-name="app"> | ||
<section id="todoapp"> | ||
<header id="header"> | ||
<h1>todos</h1> | ||
<input id="new-todo" placeholder="What needs to be done?" autofocus> | ||
</header> | ||
{{^empty todosCollection}} | ||
<section id="main"> | ||
<input id="toggle-all" type="checkbox"> | ||
<label for="toggle-all">Mark all as complete</label> | ||
{{#collection todosCollection filter="filterTodoItem" item-view="todo-item" tag="ul" id="todo-list"}} | ||
<div class="view"> | ||
<input class="toggle" type="checkbox" {{#if completed}}checked{{/if}}> | ||
<label>{{title}}</label> | ||
<button class="destroy"></button> | ||
</div> | ||
<input class="edit" value="{{title}}"> | ||
{{/collection}} | ||
</section> | ||
{{view "stats" tag="footer" id="footer"}} | ||
{{/empty}} | ||
</section> | ||
<div id="info"> | ||
<p>Double-click to edit a todo</p> | ||
<p>Written by <a href="https://github.com/addyosmani">Addy Osmani</a> & <a href="https://github.com/beastridge">Ryan Eastridge</a></p> | ||
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> | ||
</div> | ||
</script> | ||
<script type="text/template" data-template-name="stats"> | ||
<span id="todo-count"><strong>{{remaining}}</strong> {{itemText}} left</span> | ||
<ul id="filters"> | ||
<li> | ||
{{#link "/" class="selected"}}All{{/link}} | ||
</li> | ||
<li> | ||
{{#link "/active"}}Active{{/link}} | ||
</li> | ||
<li> | ||
{{#link "/completed"}}Completed{{/link}} | ||
</li> | ||
</ul> | ||
{{#if completed}} | ||
<button id="clear-completed">Clear completed ({{completed}})</button> | ||
{{/if}} | ||
</script> | ||
<script src="../../../assets/base.js"></script> | ||
<script src="../../../assets/jquery.min.js"></script> | ||
<script src="../../../assets/lodash.min.js"></script> | ||
<script src="../../../assets/handlebars.min.js"></script> | ||
<script src="js/lib/backbone.js"></script> | ||
<script src="js/lib/backbone-localstorage.js"></script> | ||
<script src="js/lib/thorax.js"></script> | ||
<script> | ||
// Grab the text from the templates we created above | ||
Thorax.templates = { | ||
app: Handlebars.compile($('script[data-template-name="app"]').html()), | ||
stats: Handlebars.compile($('script[data-template-name="stats"]').html()) | ||
}; | ||
|
||
//initialize the app object | ||
window.app = {}; | ||
</script> | ||
<script src="js/models/todo.js"></script> | ||
<script src="js/collections/todos.js"></script> | ||
<script src="js/views/todo-item.js"></script> | ||
<script src="js/views/stats.js"></script> | ||
<script src="js/views/app.js"></script> | ||
<script src="js/routers/router.js"></script> | ||
<script src="js/app.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
var ENTER_KEY = 13; | ||
|
||
$(function() { | ||
Backbone.history.start(); | ||
// Kick things off by creating the **App**. | ||
var view = new Thorax.Views['app'](); | ||
$('body').append(view.el); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
(function() { | ||
'use strict'; | ||
|
||
// Todo Collection | ||
// --------------- | ||
|
||
// The collection of todos is backed by *localStorage* instead of a remote | ||
// server. | ||
var TodoList = Backbone.Collection.extend({ | ||
|
||
// Reference to this collection's model. | ||
model: window.app.Todo, | ||
|
||
// Save all of the todo items under the `"todos"` namespace. | ||
localStorage: new Store('todos-backbone'), | ||
|
||
// Filter down the list of all todo items that are finished. | ||
completed: function() { | ||
return this.filter(function( todo ) { | ||
return todo.get('completed'); | ||
}); | ||
}, | ||
|
||
// Filter down the list to only todo items that are still not finished. | ||
remaining: function() { | ||
return this.without.apply( this, this.completed() ); | ||
}, | ||
|
||
// We keep the Todos in sequential order, despite being saved by unordered | ||
// GUID in the database. This generates the next order number for new items. | ||
nextOrder: function() { | ||
if ( !this.length ) { | ||
return 1; | ||
} | ||
return this.last().get('order') + 1; | ||
}, | ||
|
||
// Todos are sorted by their original insertion order. | ||
comparator: function( todo ) { | ||
return todo.get('order'); | ||
} | ||
}); | ||
|
||
// Create our global collection of **Todos**. | ||
window.app.Todos = new TodoList(); | ||
|
||
}()); |
84 changes: 84 additions & 0 deletions
84
labs/architecture-examples/thorax/js/lib/backbone-localstorage.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// A simple module to replace `Backbone.sync` with *localStorage*-based | ||
// persistence. Models are given GUIDS, and saved into a JSON object. Simple | ||
// as that. | ||
|
||
// Generate four random hex digits. | ||
function S4() { | ||
return (((1+Math.random())*0x10000)|0).toString(16).substring(1); | ||
}; | ||
|
||
// Generate a pseudo-GUID by concatenating random hexadecimal. | ||
function guid() { | ||
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); | ||
}; | ||
|
||
// Our Store is represented by a single JS object in *localStorage*. Create it | ||
// with a meaningful name, like the name you'd give a table. | ||
var Store = function(name) { | ||
this.name = name; | ||
var store = localStorage.getItem(this.name); | ||
this.data = (store && JSON.parse(store)) || {}; | ||
}; | ||
|
||
_.extend(Store.prototype, { | ||
|
||
// Save the current state of the **Store** to *localStorage*. | ||
save: function() { | ||
localStorage.setItem(this.name, JSON.stringify(this.data)); | ||
}, | ||
|
||
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already | ||
// have an id of it's own. | ||
create: function(model) { | ||
if (!model.id) model.id = model.attributes.id = guid(); | ||
this.data[model.id] = model; | ||
this.save(); | ||
return model; | ||
}, | ||
|
||
// Update a model by replacing its copy in `this.data`. | ||
update: function(model) { | ||
this.data[model.id] = model; | ||
this.save(); | ||
return model; | ||
}, | ||
|
||
// Retrieve a model from `this.data` by id. | ||
find: function(model) { | ||
return this.data[model.id]; | ||
}, | ||
|
||
// Return the array of all models currently in storage. | ||
findAll: function() { | ||
return _.values(this.data); | ||
}, | ||
|
||
// Delete a model from `this.data`, returning it. | ||
destroy: function(model) { | ||
delete this.data[model.id]; | ||
this.save(); | ||
return model; | ||
} | ||
|
||
}); | ||
|
||
// Override `Backbone.sync` to use delegate to the model or collection's | ||
// *localStorage* property, which should be an instance of `Store`. | ||
Backbone.sync = function(method, model, options) { | ||
|
||
var resp; | ||
var store = model.localStorage || model.collection.localStorage; | ||
|
||
switch (method) { | ||
case "read": resp = model.id ? store.find(model) : store.findAll(); break; | ||
case "create": resp = store.create(model); break; | ||
case "update": resp = store.update(model); break; | ||
case "delete": resp = store.destroy(model); break; | ||
} | ||
|
||
if (resp) { | ||
options.success(resp); | ||
} else { | ||
options.error("Record not found"); | ||
} | ||
}; |
Oops, something went wrong.