-
-
Save tony/74fd04799156ad4cc850 to your computer and use it in GitHub Desktop.
Updated for Backbone 1.1, Marionette 1.8.x. Update to use wreqr, bindTo -> listenTo
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
define([ | |
"underscore", | |
"backbone", | |
"marionette", | |
"vent" | |
], | |
/** | |
* Creates the primary `Marionette.Application` object that runs the admin framework. Provides a central point | |
* from which all other sub-applications are started, shown, hidden, and stopped. | |
* @requires module:underscore | |
* @requires module:backbone | |
* @requires module:marionette | |
* @requires module:supervent // this is a custom eventAggregator with Wreqr.commands() and Wreqr.reqres() properties | |
*/ | |
function (_, Backbone, Marionette, vent) { | |
var rootApp = new Marionette.Application(), | |
subApps = {}; | |
rootApp.subApps = subApps; | |
rootApp.addRegions({ | |
body: "body" | |
}); | |
rootApp.addInitializer(function (options) { | |
var i, il, anApp; | |
var that = this; | |
rootApp.router = new options.Router({ | |
controller: options.controller | |
}); | |
// All routers in the `applications` array should be Marionette.SubAppRouters. When the router is instantiated, | |
// the first parameter passed to the constructor (`anApp.name`) is interpreted as the prefix for all `routes` | |
// and `appRoutes` in the router. All routes will have the prefix prepended to them, then a forward slash, then | |
// the "regular" route. For example, if you have a route that's `search/:id` and the prefix is "library", then | |
// the actual route added to Backbone will be `library/search/:id`. | |
for(i= 0, il=options.applications.length; i<il; i++) { | |
anApp = options.applications[i]; | |
// TODO: should we hook this up here, or send out an event that tells apps where to display themselves? | |
anApp.parentRegion = that.body; | |
anApp.application.router = | |
new anApp.Router(anApp.name, { | |
controller: anApp.controller | |
}); | |
subApps[anApp.name] = anApp.application; | |
} | |
}); | |
rootApp.on("initialize:after", function () { | |
var that = this; | |
// Start all of our sub-applications | |
_.each(subApps, function(app, key, list){ | |
// notice we pass this app's region to the sub-app, so it can either grab the $el or use the region directly | |
app.start({parentRegion: that.body}); | |
// Trigger a notification and pass the app along in case anyone wants a reference | |
vent.trigger("starting:app:" + key, app); | |
}); | |
// And hook up history tracking | |
if (Backbone.history) { | |
Backbone.history.start(); | |
} | |
}); | |
// Now actually display the application | |
rootApp.on("start", function () { | |
vent.trigger("started:app:rootapp", rootApp); | |
vent.commands.execute("initialize:ui"); | |
}); | |
return rootApp; | |
}); |
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
//main.js | |
require([ | |
'app', | |
'router', | |
'controller', | |
'subapp/app', | |
'subapp/router', | |
'subapp/controller' | |
], | |
function(rootApp, AppRouter, AppController, subApp, SubAppRouter, SubAppController){ | |
"use strict"; | |
var options = { | |
controller: AppController, | |
Router: AppRouter, | |
applications: [ | |
{ | |
name: "contestcreator", | |
application: subApp, | |
controller: SubAppController, | |
Router: SubAppRouter | |
} | |
] | |
}; | |
rootApp.start(options); | |
}); |
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 is heavily based on Backbone.SubRoute (https://github.com/ModelN/backbone.subroute) by Dave Cadwallader, who | |
// helped me out in this discussion thread: | |
// https://groups.google.com/forum/?fromgroups=#!topic/backbone-marionette/KTw7USoA6Gs | |
define([ | |
'underscore', | |
'marionette' | |
], | |
/** | |
* A module that defines and adds Marionette.SubAppRouter to the Marionette object | |
* @module marionette.subapprouter | |
* @requires module:marionette | |
* @param {Function} Marionette - Marionette.js | |
* @exports marionette | |
*/ | |
function(_, Marionette) { | |
/** | |
* Creates an AppRouter with a particular prefix at the beginning of each route. | |
* @class Marionette.SubAppRouter | |
* @alias module:marionette.subapprouter | |
* @classdesc A router that prepends a specified prefix (passed on instantiation) onto each specified `route` or | |
* `appRoute`. Directly extends from Marionette.AppRouter to enable controller objects linked to the `appRoutes` | |
* hash. Useful for sub applications that live in sub-directories under root and want to handle all of their own | |
* routing. | |
*/ | |
Marionette.SubAppRouter = Marionette.AppRouter.extend({ | |
/** | |
* @constructs Marionette.SubAppRouter | |
* @param {string} [prefix] - The prefix string to prepend to all routes, making them act as if relative. If | |
* blank, then it just acts like a regular Backbone.Router. | |
* @param {Object} [options] - The options object expected by Marionette.AppRouter. | |
* @param {Object} [options.controller] - An object with function properties corresponding to the hash values | |
* from `routes` and `appRoutes`. | |
*/ | |
constructor: function(prefix, options) { | |
var controller, | |
appRoutes, | |
routes = {}; | |
// Prefix is optional, set to empty string if not passed | |
this.prefix = prefix = prefix || ""; | |
// SubRoute instances may be instantiated using a prefix with or without a trailing slash. | |
// If the prefix does *not* have a trailing slash, we need to insert a slash as a separator | |
// between the prefix and the sub-route path for each route that we register with Backbone. | |
this.separator = | |
( prefix.slice( -1 ) === "/" ) | |
? "" | |
: "/"; | |
// if you want to match "books" and "books/" without creating separate routes, set this | |
// option to "true" and the sub-router will automatically create those routes for you. | |
var createTrailingSlashRoutes = options && options.createTrailingSlashRoutes; | |
if(this.appRoutes) { | |
appRoutes = this.appRoutes; | |
controller = this.controller; | |
if(options && options.controller) { | |
controller = options.controller; | |
} | |
_.each(appRoutes, function(callback, path) { | |
if (path) { | |
// strip off any leading slashes in the sub-route path, | |
// since we already handle inserting them when needed. | |
if (path.substr(0) === "/") { | |
path = path.substr(1, path.length); | |
} | |
routes[prefix + this.separator + path] = callback; | |
if (createTrailingSlashRoutes) { | |
routes[prefix + this.separator + path + "/"] = callback; | |
} | |
} else { | |
// default routes (those with a path equal to the empty string) | |
// are simply registered using the prefix as the route path. | |
routes[prefix] = callback; | |
if (createTrailingSlashRoutes) { | |
routes[prefix + "/"] = callback; | |
} | |
} | |
}, this); | |
// Override the local sub-routes with the fully-qualified routes that we just set up. | |
this.appRoutes = routes; | |
} | |
Marionette.AppRouter.prototype.constructor.call(this, options); | |
} | |
}); | |
return Marionette.SubAppRouter; | |
}); |
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 is heavily over-engineered, honestly, as you should really only need a single vent across which | |
// to echo (which should be the "root" vent in your project). But since I bothered to make it more flexible, | |
// I figured I'd put up the more flexible version rather than the single-echo version I'm currently using. | |
// underscore.deepclone is just a module that adds the "cloneDeep" method from lo-dash to underscore, which | |
// is documented here: http://lodash.com/docs#cloneDeep | |
define([ | |
'underscore', | |
'marionette', | |
'wreqr', | |
'vent', | |
'vent.super', | |
'underscore.deepclone' | |
], | |
/** | |
* @module components/Vent.echo-supervent | |
* @requires module:underscore | |
* @requires module:Marionette | |
* @requires module:Wreqr | |
* @requires module:vent | |
* @requires module:base/Vent.super | |
*/ | |
function(_, Marionette, Wreqr, rootVent, SuperVent){ | |
var console = window.console; | |
var getOpt = Marionette.getOption; | |
var nullOrEmpty = function(obj) { | |
return (!obj || (obj && _.isEmpty(obj))); | |
}; | |
var isVent = function(obj) { | |
return (obj instanceof Wreqr.EventAggregator); | |
}; | |
/** | |
* @alias module:components/Vent.echo-supervent | |
* @constructor | |
* @param {String} namespace - The namespace to prepend onto events when echoing | |
* @param {Object} options - A hash of | |
*/ | |
var EchoVent = SuperVent.extend({ | |
/** | |
* The namespace to prepend onto events when echoing back across the root vent | |
* @property namespace | |
* @type String | |
* @default "" | |
*/ | |
namespace: "", | |
echoOn: {}, | |
debug: false, | |
constructor: function(namespace, vents, options) { | |
var args = _.toArray(arguments); | |
var that = this, | |
config = args[0]; | |
// Handle the one-big-config-param case | |
if (args.length === 1 && _.isObject(config) && !isVent(config)) { | |
namespace = config.namespace || this.namespace; | |
vents = config.echoOn || this.echoOn; | |
options = _.omit(config, ["namespace", "vents"]); | |
} | |
this.options = options || {}; | |
this.debug = getOpt(this, "debug"); | |
// Call the superclass constructor to get event binder mixed in early | |
SuperVent.prototype.constructor.apply(this, arguments); | |
this.namespace = namespace; | |
if (!nullOrEmpty(vents)) { | |
this._addInitialVents(vents); | |
} | |
// We want any event send through this aggregator to echo in the root vent as well - just prefixed by | |
// a namespace (the name of the app/module, typically) | |
this.bindTo(this, "all", function(){ | |
var args = _.toArray(arguments); | |
var originalName = args[0]; | |
var vents = that.echoOn; | |
// prefix namespace to event name | |
args[0] = [that.namespace, originalName].join(":"); | |
// re-trigger that event on every registered vent | |
_.each(vents, function (vent) { | |
vent.trigger.apply(vent, args); | |
if (that.debug) { | |
console.log("event \"%s\" triggered on %o with args %o", args[0], vent, _.rest(args, 1)); | |
} | |
}); | |
}); | |
}, | |
_addInitialVents: function(vents) { | |
var echoOn = nullOrEmpty(vents) ? false : vents, | |
that = this; | |
// Return if there's nothing to do | |
if (!echoOn) { return; } | |
// Clear current list of vents (we still have existing ones in the temp clone) | |
this.echoOn = {}; | |
var addEach = function(vent, key) { | |
var args = _.toArray(arguments); | |
// We don't want pure numeric indexes as custom IDs, so if we've gotten this via an array, set | |
// it to null so addEcho will generate a valid unique ID. | |
if (_.isNumber(key)) { args[1] = false; } | |
that.addEcho.apply(that, args); | |
}; | |
// If it's an array, or it's an object with id/aggregator pairs, go ahead and run it through addEach | |
if (isVent(echoOn)) { | |
this.addEcho(echoOn); | |
} else if (_.isArray(echoOn) || _.isObject(echoOn)) { | |
_.each(echoOn, addEach); | |
} else { | |
var msg = "Cannot add object to EchoVent with namespace " + this.namespace + ", unknown object"; | |
var err = new Error(msg); | |
err.catalyst = echoOn; | |
throw err; | |
} | |
}, | |
setNamespace: function(newNs) { | |
this.trigger("echovent:change:namespace:from:" + this.namespace + ":to:" + newNs, this); | |
this.namespace = newNs; | |
}, | |
addEcho: function(newVent, customIndex) { | |
customIndex = customIndex || _.unique("ev_"); | |
this.echoOn[customIndex] = newVent; | |
this.trigger("echovent:added:vent", newVent); | |
}, | |
removeEcho: function(vent) { | |
var vents = this.echoOn; | |
var ventName = _.isString(vent) ? vent : this.getIdOf(vent); | |
vent = vents[ventName]; | |
this.trigger("echovent:removed:vent:" + ventName, vents[vent]); | |
delete vents[ventName]; | |
}, | |
getIdOf: function(vent) { | |
var vents = this.echoOn; | |
if (nullOrEmpty(vents)) { return false; } | |
var vals = _.values(vents); | |
var index = vals.indexOf(vent); | |
return _.keys(vents)[index]; | |
} | |
}); | |
return EchoVent; | |
}); |
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
define([ | |
'backbone', | |
'marionette', | |
'wreqr' | |
], | |
/** | |
* A module that exports `SuperVent` - simply an event aggregator that has all the different `Wreqr` functionality | |
* built in. Should only be used by the root application, all other applications should use `EchoVent`. | |
* @module supervent | |
* @requires module:backbone | |
* @requires module:Marionette | |
* @requires module.Wreqr | |
* @exports SuperVent | |
*/ | |
function(Backbone, Marionette, Wreqr) { | |
var SuperVent = {}; | |
Wreqr.EventAggregator.extend = Backbone.Model.extend; | |
/** | |
* An event aggregator with Wreqr.Commands and Wreqr.RequestResponse objects added. | |
* @class SuperVent | |
* @alias module:supervent | |
* @classdesc A `Marionette.EventAggregator` object with `Wreqr.Commands` and `Wreqr.RequestResponse` instances | |
* built in. | |
* @property {Backbone.Wreqr.Commands} commands - A Wreqr.Commands instance | |
* @property {Backbone.Wreqr.RequestResponse} reqres - A Wreqr.RequestResponse instance | |
*/ | |
SuperVent = Wreqr.EventAggregator.extend({ | |
/** | |
* @constructs SuperVent | |
*/ | |
constructor: function(debug){ | |
this.debug = debug || false; | |
// And here's why it's a Super Vent | |
this.commands = new Backbone.Wreqr.Commands(); | |
this.reqres = new Backbone.Wreqr.RequestResponse(); | |
// Call the superclass constructor to get event binder mixed in | |
Wreqr.EventAggregator.prototype.constructor.apply(this, arguments); | |
if (this.debug) { | |
this.listenTo(this, "all", this.logToConsole, this); | |
} | |
}, | |
logToConsole: function() { | |
var args = Array.prototype.slice.call(arguments); | |
var eventName = args[0]; | |
console.log("vent triggered :%s with args %o", eventName, args.slice(1)); | |
} | |
}); | |
return SuperVent; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment