+
+ ### Events
+
+ /**
+ * Whether we're ready.
+ * @type Boolean
+ */
+ ready : false
+
+
+ /**
+ * Triggers the 'ready' event if it has not yet been triggered.
+ * Subsequent listeners added to this event will be auto-triggered.
+ * @returns {this}
+ */
+ triggerReady: ->
+ return this if @ready
+ @ready = true
+ @trigger 'ready', this
+ this
+
+ /**
+ * Resets the 'ready' event to its non-triggered state, firing the
+ */
+ resetReady: ->
+ return this unless @ready
+ @ready = false
+ @trigger 'ready-reset', this
+ this
+
+ /**
+ * Wrap {@link Backbone.Event#on} registration to handle registrations
+ * on 'ready' after we've broadcast the event. Handler will always still
+ * be registered, however, in case the emitter is reset.
+ *
+ * @param {String} events Space-separated events for which to register.
+ * @param {Function} callback
+ * @param {Object} [context]
+ * @returns {this}
+ */
+ on: (events, callback, context) ->
+ return this if not callback
+ Backbone.Events.on ...
+ if @ready and _.contains events.split(/\s+/), 'ready'
+ callback.call context or this, this
+ this
+
+
+
### Synchronization
/**
--- /dev/null
+{EventEmitter} = require 'events'
+
+
+/**
+ * @class An EventEmitter with a ratchet-up waiting counter.
+ */
+class exports.WaitingEmitter extends EventEmitter
+
+ /**
+ * Count of outstanding tasks.
+ * @type Number
+ */
+ waitingOn : 0
+
+
+ /**
+ * Increment the waiting task counter.
+ * @returns {this}
+ */
+ wait: ->
+ count = @waitingOn
+ @waitingOn += 1
+ # console.log "#this.wait! #count --> #{@waitingOn}"
+ # console.trace()
+ @trigger('start-waiting', this) if count is 0 and @waitingOn > 0
+ this
+
+ /**
+ * Decrement the waiting task counter.
+ * @returns {this}
+ */
+ unwait: ->
+ count = @waitingOn
+ @waitingOn -= 1
+ # console.warn "#this.unwait! #{@waitingOn} < 0" if @waitingOn < 0
+ # console.log "#this.unwait! #count --> #{@waitingOn}"
+ # console.trace()
+ @trigger('stop-waiting', this) if @waitingOn is 0 and count > 0
+ this
+
+ /**
+ * @param {Function} fn Function to wrap.
+ * @returns {Function} A function wrapping the passed function with a call
+ * to `unwait()`, then delegating with current context and arguments.
+ */
+ unwaitAnd: (fn) ->
+ self = this
+ ->
+ # console.log "#self.unwaitAnd( function #{fn.name or fn.displayName}() )"
+ # console.trace()
+ self.unwait(); fn ...
+
+
+
+/**
+ * @class An EventEmitter that auto-triggers new handlers once "ready".
+ */
+class exports.ReadyEmitter extends EventEmitter
+ readyEventName : 'ready'
+ ready : false
+
+ /**
+ * Triggers the 'ready' event if it has not yet been triggered.
+ * Subsequent listeners added to this event will be auto-triggered.
+ * @returns {this}
+ */
+ triggerReady: ->
+ return this if @ready
+ @ready = true
+ @emit @readyEventName, this
+ this
+
+ /**
+ * Resets the 'ready' event to its non-triggered state, firing the
+ */
+ resetReady: ->
+ return this unless @ready
+ @ready = false
+ @emit "#{@readyEventName}-reset", this
+ this
+
+
+ /**
+ * Wrap {@link EventEmitter#on} registration to handle registrations
+ * on 'ready' after we've broadcast the event. Handler will always still
+ * be registered, however, in case the emitter is reset.
+ *
+ * @param {String} events Space-separated events for which to register.
+ * @param {Function} callback
+ * @param {Object} [context]
+ * @returns {this}
+ */
+ on: (events, callback, context) ->
+ return this if not callback
+ super ...
+ if @ready and _.contains events.split(/\s+/), @readyEventName
+ callback.call context or this, this
+ this
+
+
+