Commit 6f094c7e authored by Addy Osmani's avatar Addy Osmani

Merge pull request #639 from paulmillr/topics/update-brunch-chaplin

Update Brunch with Chaplin.
parents 401846de 52188567
bower_components/
!bower_components/todomvc-common/
# NPM packages folder.
node_modules/
......
Chaplin = require 'chaplin'
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'
Layout = require 'views/layout'
# The application object
module.exports = class Application extends Chaplin.Application
......@@ -13,48 +7,6 @@ module.exports = class Application extends Chaplin.Application
# “Controller title – Site title” (see Layout#adjustTitle)
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
# ------------------------------------
initLayout: ->
# Use an application-specific Layout class. Currently this adds
# no features to the standard Chaplin Layout, it’s an empty placeholder.
@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
# -------------------------------------
initMediator: ->
......@@ -62,6 +14,8 @@ module.exports = class Application extends Chaplin.Application
mediator.user = null
# Add additional application-specific properties and methods
mediator.todos = new Todos()
# If todos are fetched from server, we will need to wait
# for them.
mediator.todos.fetch()
# Seal the mediator
mediator.seal()
super
......@@ -3,22 +3,13 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Chaplin & Brunch • TodoMVC</title>
<link rel="stylesheet" href="../../../../assets/base.css">
<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.js">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
<link rel="stylesheet" href="stylesheets/app.css">
<!-- 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 src="app.js"></script>
<script>require('initialize');</script>
</head>
<body>
......
Chaplin = require 'chaplin'
module.exports = class Controller extends Chaplin.Controller
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'
HeaderView = require 'views/header-view'
FooterView = require 'views/footer-view'
TodosView = require 'views/todos-view'
mediator = require 'mediator'
module.exports = class IndexController extends Controller
title: 'Todo list'
module.exports = class IndexController extends Chaplin.Controller
# The method is executed before any controller actions.
# We compose structure in order for it to be rendered only once.
beforeAction: ->
@compose 'structure', ->
params = collection: mediator.todos
@header = new HeaderView params
@footer = new FooterView params
list: (options) ->
@publishEvent 'todos:filter', options.filterer?.trim() ? 'all'
# On each new load, old @view will be disposed and
# new @view will be created. This is idiomatic Chaplin memory management:
# one controller per screen.
list: (params) ->
filterer = params.filterer?.trim() ? 'all'
@publishEvent 'todos:filter', filterer
@view = new TodosView collection: mediator.todos, filterer: (model) ->
switch filterer
when 'completed' then model.get('completed')
when 'active' then not model.get('completed')
else true
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'
routes = require 'routes'
# Initialize the application on DOM ready event.
$ ->
app = new Application()
app.initialize()
new Application
controllerSuffix: '-controller', pushState: false, 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
# ------------------------------
# Delegate to Chaplin’s utils module
utils = Chaplin.utils.beget Chaplin.utils
# _(utils).extend
# someMethod: ->
module.exports = utils
mediator = require 'mediator'
utils = require 'chaplin/lib/utils'
# Application-specific view helpers
# ---------------------------------
# http://handlebarsjs.com/#helpers
# Conditional evaluation
# ----------------------
# Choose block by user login status
Handlebars.registerHelper 'if_logged_in', (options) ->
if mediator.user
options.fn(this)
else
options.inverse(this)
# Map helpers
# -----------
# Make 'with' behave a little more mustachey
Handlebars.registerHelper 'with', (context, options) ->
if not context or Handlebars.Utils.isEmpty context
options.inverse(this)
else
options.fn(context)
# Inverse for 'with'
Handlebars.registerHelper 'without', (context, options) ->
inverse = options.inverse
options.inverse = options.fn
options.fn = inverse
Handlebars.helpers.with.call(this, context, options)
# Evaluate block with context being current user
Handlebars.registerHelper 'with_user', (options) ->
context = mediator.user?.serialize() or {}
Handlebars.helpers.with.call(this, context, options)
module.exports = require('chaplin').mediator
module.exports = Chaplin.mediator
Chaplin = require 'chaplin'
Model = require 'models/base/model'
module.exports = class Collection extends Chaplin.Collection
# Use the project base model per default, not Chaplin.Model
model: Model
# Mixin a synchronization state machine
# _(@prototype).extend Chaplin.SyncMachine
Chaplin = require 'chaplin'
module.exports = class Model extends Chaplin.Model
# Mixin a synchronization state machine
# _(@prototype).extend Chaplin.SyncMachine
Model = require 'models/base/model'
module.exports = class Todo extends Model
# It is a very good idea to have base Model / Collection
# e.g. Model = require 'models/base/model'
# But in this particular app since we only have one
# model type, we will inherit directly from Chaplin Model.
module.exports = class Todo extends Chaplin.Model
defaults:
title: ''
completed: no
......
Collection = require 'models/base/collection'
Todo = require 'models/todo'
module.exports = class Todos extends Collection
module.exports = class Todos extends Chaplin.Collection
model: Todo
localStorage: new Store 'todos-chaplin'
......
Chaplin = require 'chaplin'
View = require 'views/base/view'
module.exports = class CollectionView extends Chaplin.CollectionView
# This class doesn’t inherit from the application-specific View class,
# so we need to borrow the method from the View prototype:
getTemplateFunction: View::getTemplateFunction
useCssAnimation: true
Chaplin = require 'chaplin'
require 'lib/view-helper' # Just load the view helpers, no return value
module.exports = class View extends Chaplin.View
# Precompiled templates function initializer.
getTemplateFunction: ->
......
View = require 'views/base/view'
template = require 'views/templates/footer'
View = require './base/view'
template = require './templates/footer'
module.exports = class FooterView extends View
autoRender: yes
autoRender: true
el: '#footer'
events:
'click #clear-completed': 'clearCompleted'
listen:
'todos:filter mediator': 'updateFilterer'
'all collection': 'renderCounter'
template: template
initialize: ->
super
@subscribeEvent 'todos:filter', @updateFilterer
@modelBind 'all', @renderCounter
@delegate 'click', '#clear-completed', @clearCompleted
render: =>
render: ->
super
@renderCounter()
updateFilterer: (filterer) =>
updateFilterer: (filterer) ->
filterer = '' if filterer is 'all'
@$('#filters a')
.removeClass('selected')
.filter("[href='#/#{filterer}']")
.addClass('selected')
renderCounter: =>
renderCounter: ->
total = @collection.length
active = @collection.getActive().length
completed = @collection.getCompleted().length
......
View = require 'views/base/view'
template = require 'views/templates/header'
View = require './base/view'
template = require './templates/header'
module.exports = class HeaderView extends View
autoRender: yes
autoRender: true
el: '#header'
events:
'keypress #new-todo': 'createOnEnter'
template: template
initialize: ->
super
@delegate 'keypress', '#new-todo', @createOnEnter
createOnEnter: (event) =>
ENTER_KEY = 13
title = $(event.currentTarget).val().trim()
return if event.keyCode isnt ENTER_KEY or not title
@collection.create {title}
@$('#new-todo').val ''
createOnEnter: (event) =>
ENTER_KEY = 13
title = $(event.currentTarget).val().trim()
return if event.keyCode isnt ENTER_KEY or not title
@collection.create {title}
@$('#new-todo').val ''
Chaplin = require 'chaplin'
# Layout is the top-level application ‘view’.
module.exports = class Layout extends Chaplin.Layout
initialize: ->
super
@subscribeEvent 'todos:filter', @changeFilterer
changeFilterer: (filterer = 'all') ->
$('#todoapp').attr 'class', "filter-#{filterer}"
View = require 'views/base/view'
template = require 'views/templates/todo'
View = require './base/view'
template = require './templates/todo'
module.exports = class TodoView extends View
template: template
tagName: 'li'
events:
'click .toggle': 'toggle'
'dblclick label': 'edit'
'keypress .edit': 'save'
'blur .edit': 'save'
'click .destroy': 'destroy'
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
listen:
'change model': 'render'
render: =>
super
# Reset classes, re-add the appropriate ones.
@$el.removeClass 'active completed'
className = if @model.get('completed') then 'completed' else 'active'
@$el.addClass className
template: template
tagName: 'li'
destroy: =>
@model.destroy()
......
CollectionView = require 'views/base/collection-view'
template = require 'views/templates/todos'
TodoView = require 'views/todo-view'
CollectionView = require './base/collection-view'
template = require './templates/todos'
TodoView = require './todo-view'
module.exports = class TodosView extends CollectionView
el: '#main'
container: '#main'
events:
'click #toggle-all': 'toggleCompleted'
itemView: TodoView
listSelector: '#todo-list'
listen:
'all collection': 'renderCheckbox'
'todos:clear mediator': 'clear'
template: template
initialize: ->
super
@subscribeEvent 'todos:clear', @clear
@modelBind 'all', @renderCheckbox
@delegate 'click', '#toggle-all', @toggleCompleted
render: =>
super
@renderCheckbox()
......
......@@ -2,6 +2,25 @@
"name": "todomvc-chaplin-brunch",
"version": "0.0.0",
"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"
}
}
}
\ No newline at end of file
}
......@@ -136,10 +136,6 @@
}
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();
xhr.open('GET', findRoot() + file, true);
......
......@@ -2,28 +2,10 @@ exports.config =
# See http://brunch.readthedocs.org/en/latest/config.html for documentation.
files:
javascripts:
joinTo:
'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'
]
joinTo: 'app.js'
stylesheets:
joinTo:
'stylesheets/app.css': /^(app|vendor)/
'test/stylesheets/test.css': /^test/
order:
before: ['vendor/styles/normalize-2.0.1.css']
after: ['vendor/styles/helpers.css']
joinTo: 'app.css'
templates:
joinTo: 'javascripts/app.js'
joinTo: 'app.js'
......@@ -11,16 +11,16 @@
"test": "brunch test"
},
"dependencies": {
"javascript-brunch": ">= 1.0 < 1.5",
"coffee-script-brunch": ">= 1.0 < 1.5",
"javascript-brunch": ">= 1.0 < 1.8",
"coffee-script-brunch": ">= 1.0 < 1.8",
"css-brunch": ">= 1.0 < 1.5",
"stylus-brunch": ">= 1.0 < 1.5",
"css-brunch": ">= 1.0 < 1.8",
"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",
"clean-css-brunch": ">= 1.0 < 1.5"
"uglify-js-brunch": ">= 1.0 < 1.8",
"clean-css-brunch": ">= 1.0 < 1.8"
},
"devDependencies": {
"chai": "~1.2.0",
......
This diff is collapsed.
This diff is collapsed.
......@@ -3,22 +3,13 @@
<head>
<meta charset="utf-8">
<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.js">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
<link rel="stylesheet" href="stylesheets/app.css">
<!-- 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 src="app.js"></script>
<script>require('initialize');</script>
</head>
<body>
......
#todoapp.filter-active #todo-list .completed {
display: none; }
#todoapp.filter-completed #todo-list .active {
display: none; }
......@@ -40,7 +40,7 @@ If you haven't already installed [Brunch](http://brunch.io), run:
Once you have Brunch, install this application's dependencies:
# from labs/dependency-examples/chaplin-brunch
npm install
npm install & bower install
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