Adds auto-triggering on-ready-registrations.
authordsc <dsc@wikimedia.org>
Tue, 24 Apr 2012 19:55:04 +0000 (12:55 -0700)
committerdsc <dsc@wikimedia.org>
Tue, 24 Apr 2012 19:55:04 +0000 (12:55 -0700)
lib/base/base-mixin.co
lib/util/wait-event.co [new file with mode: 0644]

index 93385b5..fd10c59 100644 (file)
@@ -29,6 +29,55 @@ BaseBackboneMixin = exports.BaseBackboneMixin =
     
     
     
+    
+    ### 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
     
     /**
diff --git a/lib/util/wait-event.co b/lib/util/wait-event.co
new file mode 100644 (file)
index 0000000..96e2009
--- /dev/null
@@ -0,0 +1,101 @@
+{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
+
+
+