Commit b4e7bc94 authored by Paul Miller's avatar Paul Miller

Update Brunch with Chaplin.

1. Add Bower integration. Since Brunch automatically concatenates all Bower stuff, we don’t need bower_components in repository (added to .gitignore, except of todomvc-common).
2. Update Brunch to 1.7.
    * Added automatic source maps generation, source maps are in repo too.
3. Update Chaplin to 0.10.
    * Remove `chaplin` module, switch to global variable.
    * Switch to new declarative events format.
    * Switch to controller compositions instead of static controllers.
    * Remove `application` since Chaplin now has this functionality by default.
parent 87c5b345
bower_components/
!bower_components/todomvc-common
# NPM packages folder. # NPM packages folder.
node_modules/ node_modules/
......
Chaplin = require 'chaplin'
mediator = require 'mediator' mediator = require 'mediator'
routes = require 'routes'
HeaderController = require 'controllers/header-controller'
FooterController = require 'controllers/footer-controller'
TodosController = require 'controllers/todos-controller'
Todos = require 'models/todos' Todos = require 'models/todos'
Layout = require 'views/layout' Layout = require 'views/layout'
...@@ -13,28 +8,6 @@ module.exports = class Application extends Chaplin.Application ...@@ -13,28 +8,6 @@ module.exports = class Application extends Chaplin.Application
# “Controller title – Site title” (see Layout#adjustTitle) # “Controller title – Site title” (see Layout#adjustTitle)
title: 'Chaplin • TodoMVC' title: 'Chaplin • TodoMVC'
initialize: ->
super
# Initialize core components
@initDispatcher controllerSuffix: '-controller'
@initLayout()
@initMediator()
# Application-specific scaffold
@initControllers()
# Register all routes and start routing
@initRouter routes, pushState: no
# You might pass Router/History options as the second parameter.
# Chaplin enables pushState per default and Backbone uses / as
# the root per default. You might change that in the options
# if necessary:
# @initRouter routes, pushState: false, root: '/subdir/'
# Freeze the application instance to prevent further changes
Object.freeze? this
# Override standard layout initializer # Override standard layout initializer
# ------------------------------------ # ------------------------------------
initLayout: -> initLayout: ->
...@@ -42,19 +15,6 @@ module.exports = class Application extends Chaplin.Application ...@@ -42,19 +15,6 @@ module.exports = class Application extends Chaplin.Application
# no features to the standard Chaplin Layout, it’s an empty placeholder. # no features to the standard Chaplin Layout, it’s an empty placeholder.
@layout = new Layout {@title} @layout = new Layout {@title}
# Instantiate common controllers
# ------------------------------
initControllers: ->
# These controllers are active during the whole application runtime.
# You don’t need to instantiate all controllers here, only special
# controllers which do not to respond to routes. They may govern models
# and views which are needed the whole time, for example header, footer
# or navigation views.
# e.g. new NavigationController()
new HeaderController()
new FooterController()
new TodosController()
# Create additional mediator properties # Create additional mediator properties
# ------------------------------------- # -------------------------------------
initMediator: -> initMediator: ->
...@@ -64,4 +24,4 @@ module.exports = class Application extends Chaplin.Application ...@@ -64,4 +24,4 @@ module.exports = class Application extends Chaplin.Application
mediator.todos = new Todos() mediator.todos = new Todos()
mediator.todos.fetch() mediator.todos.fetch()
# Seal the mediator # Seal the mediator
mediator.seal() super
...@@ -3,22 +3,14 @@ ...@@ -3,22 +3,14 @@
<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>Chaplin & Brunch • TodoMVC</title> <title>Chaplin &amp; Brunch • TodoMVC</title>
<link rel="stylesheet" href="../../../../assets/base.css"> <link rel="stylesheet" href="../bower_components/todomvc-common/base.css">
<link rel="stylesheet" href="../bower_components/todomvc-common/base.js">
<!--[if IE]> <!--[if IE]>
<script src="../../../assets/ie.js"></script> <script src="../../../assets/ie.js"></script>
<![endif]--> <![endif]-->
<link rel="stylesheet" href="stylesheets/app.css"> <link rel="stylesheet" href="app.css">
<script src="app.js"></script>
<!-- Usually all these files are concatenated automatically
by brunch and you need just to import `vendor.js` -->
<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="javascripts/vendor.js"></script>
<script src="javascripts/app.js"></script>
<script>require('initialize');</script> <script>require('initialize');</script>
</head> </head>
<body> <body>
......
Chaplin = require 'chaplin' HeaderView = require 'views/header-view'
FooterView = require 'views/footer-view'
TodosView = require 'views/todos-view'
mediator = require 'mediator'
module.exports = class Controller extends Chaplin.Controller module.exports = class Controller extends Chaplin.Controller
beforeAction: ->
@compose 'footer', ->
params = collection: mediator.todos
@header = new HeaderView params
@footer = new FooterView params
@todos = new TodosView params
Controller = require 'controllers/base/controller'
FooterView = require 'views/footer-view'
mediator = require 'mediator'
module.exports = class FooterController extends Controller
initialize: ->
super
@view = new FooterView collection: mediator.todos
Controller = require 'controllers/base/controller'
HeaderView = require 'views/header-view'
mediator = require 'mediator'
module.exports = class HeaderController extends Controller
initialize: ->
super
@view = new HeaderView collection: mediator.todos
Controller = require 'controllers/base/controller' Controller = require './base/controller'
module.exports = class IndexController extends Controller module.exports = class IndexController extends Controller
title: 'Todo list' list: (params) ->
@publishEvent 'todos:filter', params.filterer?.trim() ? 'all'
list: (options) ->
@publishEvent 'todos:filter', options.filterer?.trim() ? 'all'
Controller = require 'controllers/base/controller'
TodosView = require 'views/todos-view'
mediator = require 'mediator'
module.exports = class TodosController extends Controller
initialize: ->
super
@view = new TodosView collection: mediator.todos
Application = require 'application' Application = require 'application'
routes = require 'routes'
# Initialize the application on DOM ready event. # Initialize the application on DOM ready event.
$ -> $ ->
app = new Application() new Application
app.initialize() controllerSuffix: '-controller', pushState: no, routes: routes
Chaplin = require 'chaplin'
utils = require 'lib/utils'
# Application-specific feature detection
# --------------------------------------
# Delegate to Chaplin’s support module
support = utils.beget Chaplin.support
# _(support).extend
# someMethod: ->
module.exports = support
Chaplin = require 'chaplin'
# Application-specific utilities # Application-specific utilities
# ------------------------------ # ------------------------------
......
mediator = require 'mediator' mediator = require 'mediator'
utils = require 'chaplin/lib/utils' utils = require './utils'
# Application-specific view helpers # Application-specific view helpers
# --------------------------------- # ---------------------------------
......
module.exports = require('chaplin').mediator module.exports = Chaplin.mediator
Chaplin = require 'chaplin'
Model = require 'models/base/model' Model = require 'models/base/model'
module.exports = class Collection extends Chaplin.Collection module.exports = class Collection extends Chaplin.Collection
......
Chaplin = require 'chaplin'
module.exports = class Model extends Chaplin.Model module.exports = class Model extends Chaplin.Model
# Mixin a synchronization state machine # Mixin a synchronization state machine
# _(@prototype).extend Chaplin.SyncMachine # _(@prototype).extend Chaplin.SyncMachine
Chaplin = require 'chaplin'
View = require 'views/base/view' View = require 'views/base/view'
module.exports = class CollectionView extends Chaplin.CollectionView module.exports = class CollectionView extends Chaplin.CollectionView
......
Chaplin = require 'chaplin'
require 'lib/view-helper' # Just load the view helpers, no return value require 'lib/view-helper' # Just load the view helpers, no return value
module.exports = class View extends Chaplin.View module.exports = class View extends Chaplin.View
......
...@@ -4,26 +4,26 @@ template = require 'views/templates/footer' ...@@ -4,26 +4,26 @@ template = require 'views/templates/footer'
module.exports = class FooterView extends View module.exports = class FooterView extends View
autoRender: yes autoRender: yes
el: '#footer' el: '#footer'
events:
'click #clear-completed': 'clearCompleted'
listen:
'todos:filter mediator': 'updateFilterer'
'all collection': 'renderCounter'
template: template template: template
initialize: -> render: ->
super
@subscribeEvent 'todos:filter', @updateFilterer
@modelBind 'all', @renderCounter
@delegate 'click', '#clear-completed', @clearCompleted
render: =>
super super
@renderCounter() @renderCounter()
updateFilterer: (filterer) => updateFilterer: (filterer) ->
console.log 'updateFilterer'
filterer = '' if filterer is 'all' filterer = '' if filterer is 'all'
@$('#filters a') @$('#filters a')
.removeClass('selected') .removeClass('selected')
.filter("[href='#/#{filterer}']") .filter("[href='#/#{filterer}']")
.addClass('selected') .addClass('selected')
renderCounter: => renderCounter: ->
total = @collection.length total = @collection.length
active = @collection.getActive().length active = @collection.getActive().length
completed = @collection.getCompleted().length completed = @collection.getCompleted().length
......
...@@ -4,12 +4,10 @@ template = require 'views/templates/header' ...@@ -4,12 +4,10 @@ template = require 'views/templates/header'
module.exports = class HeaderView extends View module.exports = class HeaderView extends View
autoRender: yes autoRender: yes
el: '#header' el: '#header'
events:
'keypress #new-todo': 'createOnEnter'
template: template template: template
initialize: ->
super
@delegate 'keypress', '#new-todo', @createOnEnter
createOnEnter: (event) => createOnEnter: (event) =>
ENTER_KEY = 13 ENTER_KEY = 13
title = $(event.currentTarget).val().trim() title = $(event.currentTarget).val().trim()
......
Chaplin = require 'chaplin'
# Layout is the top-level application ‘view’. # Layout is the top-level application ‘view’.
module.exports = class Layout extends Chaplin.Layout module.exports = class Layout extends Chaplin.Layout
initialize: -> listen:
super 'todos:filter mediator': 'changeFilterer'
@subscribeEvent 'todos:filter', @changeFilterer
changeFilterer: (filterer = 'all') -> changeFilterer: (filterer = 'all') ->
$('#todoapp').attr 'class', "filter-#{filterer}" $('#todoapp').attr 'class', "filter-#{filterer}"
...@@ -2,18 +2,19 @@ View = require 'views/base/view' ...@@ -2,18 +2,19 @@ View = require 'views/base/view'
template = require 'views/templates/todo' template = require 'views/templates/todo'
module.exports = class TodoView extends View module.exports = class TodoView extends View
events:
'click .toggle': 'toggle'
'dblclick label': 'edit'
'keypress .edit': 'save'
'blur .edit': 'save'
'click .destroy': 'destroy'
listen:
'change model': 'render'
template: template template: template
tagName: 'li' tagName: 'li'
initialize: ->
super
@modelBind 'change', @render
@delegate 'click', '.destroy', @destroy
@delegate 'dblclick', 'label', @edit
@delegate 'keypress', '.edit', @save
@delegate 'click', '.toggle', @toggle
@delegate 'blur', '.edit', @save
render: => render: =>
super super
# Reset classes, re-add the appropriate ones. # Reset classes, re-add the appropriate ones.
......
...@@ -4,16 +4,15 @@ TodoView = require 'views/todo-view' ...@@ -4,16 +4,15 @@ TodoView = require 'views/todo-view'
module.exports = class TodosView extends CollectionView module.exports = class TodosView extends CollectionView
el: '#main' el: '#main'
events:
'click #toggle-all': 'toggleCompleted'
itemView: TodoView itemView: TodoView
listSelector: '#todo-list' listSelector: '#todo-list'
listen:
'all collection': 'renderCheckbox'
'todos:clear mediator': 'clear'
template: template template: template
initialize: ->
super
@subscribeEvent 'todos:clear', @clear
@modelBind 'all', @renderCheckbox
@delegate 'click', '#toggle-all', @toggleCompleted
render: => render: =>
super super
@renderCheckbox() @renderCheckbox()
......
...@@ -2,6 +2,25 @@ ...@@ -2,6 +2,25 @@
"name": "todomvc-chaplin-brunch", "name": "todomvc-chaplin-brunch",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"todomvc-common": "~0.1.6" "todomvc-common": "~0.1.6",
"chaplin": "~0.10.0",
"underscore": "~1.4.4",
"backbone.localStorage": "~1.1.0",
"jquery": "~2.0.0"
},
"overrides": {
"todomvc-common": {
"main": "bg.png"
},
"backbone": {
"main": "backbone.js",
"dependencies": {
"underscore": "*",
"jquery": "*"
}
},
"underscore": {
"main": "underscore.js"
}
} }
} }
...@@ -136,10 +136,6 @@ ...@@ -136,10 +136,6 @@
} }
function getFile(file, callback) { function getFile(file, callback) {
if (!location.host) {
return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.');
}
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true); xhr.open('GET', findRoot() + file, true);
......
...@@ -2,28 +2,10 @@ exports.config = ...@@ -2,28 +2,10 @@ exports.config =
# See http://brunch.readthedocs.org/en/latest/config.html for documentation. # See http://brunch.readthedocs.org/en/latest/config.html for documentation.
files: files:
javascripts: javascripts:
joinTo: joinTo: 'app.js'
'javascripts/app.js': /^app/
'javascripts/vendor.js': /^vendor/
'test/javascripts/test.js': /^test[\\/](?!vendor)/
'test/javascripts/test-vendor.js': /^test[\\/](?=vendor)/
order:
# Files in `vendor` directories are compiled before other files
# even if they aren't specified in order.before.
before: [
'vendor/scripts/console-helper.js',
'vendor/scripts/jquery-1.8.2.js',
'vendor/scripts/underscore-1.4.2.js',
'vendor/scripts/backbone-0.9.2.js'
]
stylesheets: stylesheets:
joinTo: joinTo: 'app.css'
'stylesheets/app.css': /^(app|vendor)/
'test/stylesheets/test.css': /^test/
order:
before: ['vendor/styles/normalize-2.0.1.css']
after: ['vendor/styles/helpers.css']
templates: templates:
joinTo: 'javascripts/app.js' joinTo: 'app.js'
...@@ -11,16 +11,16 @@ ...@@ -11,16 +11,16 @@
"test": "brunch test" "test": "brunch test"
}, },
"dependencies": { "dependencies": {
"javascript-brunch": ">= 1.0 < 1.5", "javascript-brunch": ">= 1.0 < 1.8",
"coffee-script-brunch": ">= 1.0 < 1.5", "coffee-script-brunch": ">= 1.0 < 1.8",
"css-brunch": ">= 1.0 < 1.5", "css-brunch": ">= 1.0 < 1.8",
"stylus-brunch": ">= 1.0 < 1.5", "stylus-brunch": ">= 1.0 < 1.8",
"handlebars-brunch": ">= 1.0 < 1.5", "handlebars-brunch": ">= 1.0 < 1.8",
"uglify-js-brunch": ">= 1.0 < 1.5", "uglify-js-brunch": ">= 1.0 < 1.8",
"clean-css-brunch": ">= 1.0 < 1.5" "clean-css-brunch": ">= 1.0 < 1.8"
}, },
"devDependencies": { "devDependencies": {
"chai": "~1.2.0", "chai": "~1.2.0",
......
#todoapp.filter-active #todo-list .completed {
display: none; }
#todoapp.filter-completed #todo-list .active {
display: none; }
/*@ sourceMappingURL=app.css.map*/
\ No newline at end of file
{"version":3,"file":"public/app.css","sources":["app/views/filters.css"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA","sourcesContent":["#todoapp.filter-active #todo-list .completed {\n display: none; }\n\n#todoapp.filter-completed #todo-list .active {\n display: none; }\n"]}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -3,22 +3,14 @@ ...@@ -3,22 +3,14 @@
<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>Chaplin & Brunch • TodoMVC</title> <title>Chaplin &amp; Brunch • TodoMVC</title>
<link rel="stylesheet" href="../bower_components/todomvc-common/base.css"> <link rel="stylesheet" href="../bower_components/todomvc-common/base.css">
<link rel="stylesheet" href="../bower_components/todomvc-common/base.js">
<!--[if IE]> <!--[if IE]>
<script src="../../../assets/ie.js"></script> <script src="../../../assets/ie.js"></script>
<![endif]--> <![endif]-->
<link rel="stylesheet" href="stylesheets/app.css"> <link rel="stylesheet" href="app.css">
<script src="app.js"></script>
<!-- Usually all these files are concatenated automatically
by brunch and you need just to import `vendor.js` -->
<script src="../bower_components/todomvc-common/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="javascripts/vendor.js"></script>
<script src="javascripts/app.js"></script>
<script>require('initialize');</script> <script>require('initialize');</script>
</head> </head>
<body> <body>
......
...@@ -40,7 +40,7 @@ If you haven't already installed [Brunch](http://brunch.io), run: ...@@ -40,7 +40,7 @@ If you haven't already installed [Brunch](http://brunch.io), run:
Once you have Brunch, install this application's dependencies: Once you have Brunch, install this application's dependencies:
# from labs/dependency-examples/chaplin-brunch # from labs/dependency-examples/chaplin-brunch
npm install npm install & bower install
To build the app, run: To build the app, run:
......
/**
* Backbone localStorage Adapter
* https://github.com/jeromegn/Backbone.localStorage
*/
(function() {
// A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that.
// Hold reference to Underscore.js and Backbone.js in the closure in order
// to make things work even if they are removed from the global namespace
var _ = this._;
var Backbone = this.Backbone;
// 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.
// window.Store is deprectated, use Backbone.LocalStorage instead
Backbone.LocalStorage = window.Store = function(name) {
this.name = name;
var store = this.localStorage().getItem(this.name);
this.records = (store && store.split(",")) || [];
};
_.extend(Backbone.LocalStorage.prototype, {
// Save the current state of the **Store** to *localStorage*.
save: function() {
this.localStorage().setItem(this.name, this.records.join(","));
},
// 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 = guid();
model.set(model.idAttribute, model.id);
}
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
this.records.push(model.id.toString());
this.save();
return model.toJSON();
},
// Update a model by replacing its copy in `this.data`.
update: function(model) {
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
if (!_.include(this.records, model.id.toString())) this.records.push(model.id.toString()); this.save();
return model.toJSON();
},
// Retrieve a model from `this.data` by id.
find: function(model) {
return JSON.parse(this.localStorage().getItem(this.name+"-"+model.id));
},
// Return the array of all models currently in storage.
findAll: function() {
return _(this.records).chain()
.map(function(id){return JSON.parse(this.localStorage().getItem(this.name+"-"+id));}, this)
.compact()
.value();
},
// Delete a model from `this.data`, returning it.
destroy: function(model) {
this.localStorage().removeItem(this.name+"-"+model.id);
this.records = _.reject(this.records, function(record_id){return record_id == model.id.toString();});
this.save();
return model;
},
localStorage: function() {
return localStorage;
}
});
// localSync delegate to the model or collection's
// *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options, error) {
var store = model.localStorage || model.collection.localStorage;
// Backwards compatibility with Backbone <= 0.3.3
if (typeof options == 'function') {
options = {
success: options,
error: error
};
}
var resp;
switch (method) {
case "read": resp = model.id != undefined ? 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");
}
};
Backbone.ajaxSync = Backbone.sync;
Backbone.getSyncMethod = function(model) {
if(model.localStorage || (model.collection && model.collection.localStorage))
{
return Backbone.localSync;
}
return Backbone.ajaxSync;
};
// Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone.sync = function(method, model, options, error) {
return Backbone.getSyncMethod(model).apply(this, [method, model, options, error]);
};
})();
// Make it safe to do console.log() always.
(function (con) {
var method;
var dummy = function() {};
var methods = ('assert,count,debug,dir,dirxml,error,exception,group,' +
'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' +
'time,timeEnd,trace,warn').split(',');
while (method = methods.pop()) {
con[method] = con[method] || dummy;
}
})(window.console = window.console || {});
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