y.event's Emitter now uses an interface similar to node.js
authordsc <david.schoonover@gmail.com>
Mon, 31 Jan 2011 09:30:29 +0000 (01:30 -0800)
committerdsc <david.schoonover@gmail.com>
Mon, 31 Jan 2011 09:30:29 +0000 (01:30 -0800)
28 files changed:
src/Y/modules/y.config.cjs
src/Y/modules/y.event.cjs
src/Y/modules/y.scaffold.cjs
src/evt.cjs
src/ezl/loop/eventloop.cjs
src/ezl/mixins/speciated.cjs
src/ezl/util/data/datafile.cjs
src/ezl/util/data/loader.cjs
src/tanks/config.cjs
src/tanks/effects/buff.cjs
src/tanks/game.cjs
src/tanks/item/container.cjs
src/tanks/map/level.cjs
src/tanks/map/pathing/traversal.cjs
src/tanks/map/rock.cjs
src/tanks/map/wall.cjs
src/tanks/mixins/inventoried.cjs
src/tanks/mixins/quantified.cjs
src/tanks/thing/bullet.cjs
src/tanks/thing/item.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
src/tanks/ui/configui.cjs
src/tanks/ui/inventory/backpack.cjs
src/tanks/ui/inventory/containerui.cjs
src/tanks/ui/inventory/equipslot.cjs
src/tanks/ui/main.cjs
src/tanks/ui/pathmapui.cjs

index 55ebb00..719aa6f 100644 (file)
@@ -32,7 +32,7 @@ Y.YObject.subclass('Config', function(Config){
                 value = (value === undefined ? def : value);
                 
                 meta.obj[meta.key] = value;
-                this._fireMutation('set', key, old, value, meta.obj);
+                this._emitMutation('set', key, old, value, meta.obj);
             }
             return this;
         },
@@ -45,7 +45,7 @@ Y.YObject.subclass('Config', function(Config){
                 
                 if ( meta.obj ) {
                     delete meta.obj[meta.key];
-                    this._fireMutation('remove', key, old, undefined, meta.obj);
+                    this._emitMutation('remove', key, old, undefined, meta.obj);
                 }
             }
             return this;
@@ -54,7 +54,7 @@ Y.YObject.subclass('Config', function(Config){
         /**
          * @private
          */
-        _fireMutation : function _fireMutation(evt, key, oldval, newval, obj){
+        _emitMutation : function _emitMutation(evt, key, oldval, newval, obj){
             if (newval !== oldval){
                 var data = {
                     'key'    : key,
@@ -64,8 +64,8 @@ Y.YObject.subclass('Config', function(Config){
                     'obj'    : obj,
                     'root'   : this
                 };
-                this.fire(evt+':'+key, this, data);
-                this.fire(evt, this, data);
+                this.emit(evt+':'+key, this, data);
+                this.emit(evt, this, data);
             }
             return this;
         },
@@ -180,7 +180,7 @@ Y.YObject.subclass('Config', function(Config){
                     key = 'set:'+key;
                 return key;
             }, this);
-            this.addEventListener(events, function(evt){
+            this.on(events, function(evt){
                 obj[evt.data.key.split('.').pop()] = evt.data.newval;
             });
             return this;
index 2e15d46..0135273 100644 (file)
@@ -1,14 +1,23 @@
-var Y = require('Y').Y;
-Y['event'] = exports;
+var Y = require('Y').Y
+,
 
 /**
  * A simple event.
  * TODO: Detect jQuery event objects.
  * TODO: If DOM event, wrap with consistent API (use jQuery if present).
  */
-var Event =
+Event =
 exports['Event'] =
 Y.YObject.subclass('Event', {
+    _stopped : false, // whether propagation is stopped
+    
+    type : 'event', // event type
+    target : null,  // target of the emitter
+    trigger : null, // object which caused this event
+    data : null,    // event data
+    
+    
+    
     init : function init( type, target, trigger, data ){
         data = data || {};
         for (var k in data) this[k] = data[k];
@@ -19,6 +28,21 @@ Y.YObject.subclass('Event', {
         this.trigger = trigger || target;
     },
     
+    /**
+     * @see http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/events.html#Events-Event-stopPropagation
+     */
+    stopPropagation : function stopPropagation(){
+        this._stopped = true;
+        return this;
+    },
+    
+    /**
+     * @see http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/events.html#Events-Event-isPropagationStopped
+     */
+    isPropagationStopped : function isPropagationStopped(){
+        return this._stopped;
+    },
+    
     toString: function(){ return "Event("+this.type+")"; }
 })
 
@@ -26,33 +50,93 @@ Y.YObject.subclass('Event', {
  * A simple multicaster.
  */
 , methods = {
-    getQueue : function getQueue(evt){
-        var Qs = this.queues;
-        if ( !Qs[evt] )
-            Qs[evt] = Y([]);
-        return Qs[evt];
+    
+    listeners : function listeners(evt){
+        return this.queues[evt] ? this.queues[evt].clone() : new Y.YArray();
     },
     
-    addEventListener : function addEventListener(evts, fn){
+    willTrigger : function willTrigger(evt){
+        var queue = this.queues[evt];
+        return !!( (queue && queue.length) || (this.parent && this.parent.willTrigger(evt)) );
+    },
+    
+    hasListener : function hasListener(evt){
+        var queue = this.queues[evt];
+        return !!(queue && queue.length);
+    },
+    
+    addListener : function addListener(evts, fn){
         fn = fn.toFunction();
         if ( !Y.isArray(evts) )
             evts = evts.split(/\s+/);
-        evts.forEach(function addEvent(evt){
-            this.getQueue(evt).push(fn);
-        }, this);
+        
+        var queues = this.queues
+        ,   i=0, L=evts.length, evt=evts[i];
+        for (; i<L; evt=evts[++i])
+            (queues[evt] || (queues[evt] = new Y.YArray()))
+                .push(fn);
+        
         return this.target;
     },
     
-    removeEventListener : function removeEventListener(evt, fn){
-        this.getQueue(evt).remove(fn.toFunction());
+    removeListener : function removeListener(evts, fn){
+        fn = fn.toFunction();
+        if ( !Y.isArray(evts) )
+            evts = evts.split(/\s+/);
+        
+        var queues = this.queues
+        ,   i=0, L=evts.length, evt=evts[i];
+        for (; i<L; evt=evts[++i])
+            if ( queues[evt] ) queues[evt].remove(fn);
+        
+        return this.target;
     },
     
-    fire : function fire(evtname, trigger, data, async){
-        var evt = new Event(evtname, this.target, trigger, data);
-        if (async)
-            setTimeout(this.dispatchEvent.bind(this, evt), 10);
-        else
-            this.dispatchEvent(evt);
+    removeAllListeners : function removeAllListeners(evts){
+        if ( !Y.isArray(evts) )
+            evts = evts.split(/\s+/);
+        
+        for ( var i=0, L=evts.length, evt=evts[i]; i<L; evt=evts[++i] )
+            delete this.queues[evt];
+        
+        return this;
+    },
+    
+    once : function once(evts, fn){
+        fn = fn.toFunction();
+        var self = this;
+        return self.addListener(evts,
+            function onceHandler(){
+                self.removeListener(evts, arguments.callee);
+                fn.apply(this, arguments);
+            });
+    },
+    
+    emit : function emit(evtname, trigger, data){
+        var q = this.queues[evtname]
+        ,   L = q ? q.length : 0
+        ;
+        if ( !L )
+            return this;
+        
+        q = q._o.slice(0);
+        var A = arguments
+        ,   target = this.target
+        ,   evt = new Event(evtname, target, trigger, data)
+        ,   method = (A.length > 3 ? 'apply' : 'call')
+        ,   args   = (A.length > 3 ? [evt].concat(Y(A,3)) : evt)
+        ;
+        for (var i=0, fn=q[i]; i<L; fn=q[++i])
+            fn[method](target, args);
+        
+        if (this.parent && !evt._stopped)
+            this.parent.emit.apply(this.parent, A);
+        
+        return evt;
+    },
+    
+    emitAsync : function emitAsync(evtname, trigger, data){
+        setTimeout(this.emit.bind.apply(this.emit, [this].concat(Y(arguments))), 10);
         return evt;
     },
     
@@ -62,8 +146,11 @@ Y.YObject.subclass('Event', {
      * XXX: does not handle degenerate or recursive event dispatch
      */
     dispatchEvent : function dispatchEvent(evt){
-        this.getQueue(evt.type).invoke('call', evt.target, evt);
-        if (this.parent) this.parent.fire(evt.type, evt.trigger, evt.data);
+        var queue = this.queues[evt.type];
+        if (queue && queue.length)
+            queue.invoke('call', evt.target, evt);
+        if (this.parent)
+            this.parent.emit(evt.type, evt.trigger, evt.data);
         return evt;
     },
     
@@ -71,12 +158,14 @@ Y.YObject.subclass('Event', {
      * Decorates object with bound methods to act as a delegate of this hub.
      */
     decorate : function decorate(delegate){
-        if (!delegate) return;
+        if (!delegate)
+            return delegate;
         for (var k in methods)
             delegate[k] = methods[k].bind(this);
         return delegate;
     }
-},
+}
+,
 
 Emitter =
 exports['Emitter'] =
@@ -94,8 +183,11 @@ Y.YObject.subclass('Emitter',
         }
     }, methods) )
 ;
+methods.on = methods.addListener;
 Emitter.methods = methods;
 
 // Global Event Hub
 Emitter.global = new Emitter()
 Emitter.global.decorate(exports);
+
+Y['event'] = exports;
index ea0f200..8b1a1fa 100644 (file)
@@ -104,7 +104,7 @@ Y.subclass('Field', {
         if (val !== this.val) {
             this.old = this.val;
             this.val = val;
-            this.fire('change', this, {
+            this.emit('change', this, {
                 'key'    : this.id,
                 'oldval' : this.old,
                 'newval' : this.val,
@@ -157,11 +157,11 @@ function create(config, el){
             var field = new Field(chain, def, value);
             fields[chain] = field;
             group.append(field.el);
-            config.addEventListener('set:'+chain, function onConfigSet(evt){
+            config.on('set:'+chain, function onConfigSet(evt){
                 // console.log('Config at '+evt.data.key+' changed!', field, evt.newval, evt);
                 field.update(evt.data.newval);
             });
-            field.addEventListener('change', function onFieldChange(evt){
+            field.on('change', function onFieldChange(evt){
                 // console.log('Field '+evt.data.key+' changed!', field, evt.newval, evt);
                 config.set(evt.data.key, evt.data.newval);
             });
index 611ed6a..bb45e16 100644 (file)
@@ -82,7 +82,7 @@ function ConstructorTemplate() {
         // Perform actions that should only happen once in Constructor
         instance.__emitter__ = new Emitter(instance, cls);
         
-        cls.fire('create', instance, {
+        cls.emit('create', instance, {
             'instance' : instance,
             'cls'      : cls,
             'args'     : Y(args)
@@ -92,7 +92,7 @@ function ConstructorTemplate() {
         if ( isFunction(initialise) )
             initialise.apply(instance, args);
         
-        cls.fire('created', instance, {
+        cls.emit('created', instance, {
             'instance' : instance,
             'cls'      : cls,
             'args'     : Y(args)
@@ -125,7 +125,7 @@ function createInitialise(cls){
             if (result) instance = result; // XXX: I think this needs to go away
         }
         
-        instance.fire('init', instance, {
+        instance.emit('init', instance, {
             'instance' : instance,
             'cls'      : cls,
             'args'     : Y(arguments)
@@ -274,12 +274,12 @@ function Class(className, Parent, members){
     // Notify mixins to let them finish up any customization
     if (mixins.length)
         mixins.forEach(function(mxn){
-            if ( mxn && isFunction(mxn.fire) )
-                mxn.fire('mixin', NewClass, { 'mixin':mxn, 'cls':NewClass });
+            if ( mxn && isFunction(mxn.emit) )
+                mxn.emit('mixin', NewClass, { 'mixin':mxn, 'cls':NewClass });
         });
     
     // Notify parent of the subclass
-    ParentEmitter.fire('subclass',
+    ParentEmitter.emit('subclass',
         NewClass, {
             'className' : className,
             'parent'    : Parent,
@@ -417,18 +417,18 @@ function mixin(cls, _mxn){
         bases.unshift(mxn);
         
         // Register onCreate to fire whenever a new instance is initialized
-        if ( isFunction(cls.addEventListener) && hasOwn.call(mproto,'onCreate') && isFunction(onCreate) )
-            cls.addEventListener('create', onCreate);
+        if ( isFunction(cls.on) && hasOwn.call(mproto,'onCreate') && isFunction(onCreate) )
+            cls.on('create', onCreate);
         
         // Fire the mixin event on this Mixin only if we're done constructing the class
-        if ( isFunction(mxn.fire) && mixin.caller !== Class )
-            mxn.fire('mixin', cls, { 'mixin':mxn, 'cls':cls });
+        if ( isFunction(mxn.emit) && mixin.caller !== Class )
+            mxn.emit('mixin', cls, { 'mixin':mxn, 'cls':cls });
     });
     return cls;
 }
 
 
-Mixin.addEventListener('subclass',
+Mixin.on('subclass',
     function onMixinSubclass(evt){
         var d       = evt.data
         ,   mxn     = d.child
@@ -437,7 +437,7 @@ Mixin.addEventListener('subclass',
         ;
         
         if ( isFunction(onMixin) )
-            mxn.addEventListener('mixin', onMixin);
+            mxn.on('mixin', onMixin);
         
         // console.log('Mixin.subclass()', mxn, '<', d.parent, 'onMixin:', onMixin);
     });
index 7ba9fc9..02f1406 100644 (file)
@@ -67,7 +67,7 @@ Emitter.subclass('EventLoop', {
         
         this.running = true;
         this.now = new Date().getTime() - (this.elapsedAtStop || this.targetTime);
-        this.fire('start');
+        this.emit('start');
         
         if (this.elapsedAtStop > 0)
             this.sleepMinus(this.elapsedAtStop);
@@ -83,7 +83,7 @@ Emitter.subclass('EventLoop', {
         this.elapsedAtStop = Math.min(this.targetTime, new Date().getTime() - this.now);
         this.timer = null;
         this.running = false;
-        this.fire('stop');
+        this.emit('stop');
     },
     
     sleepMinus : function sleepMinus(tFrame){
@@ -105,7 +105,7 @@ Emitter.subclass('EventLoop', {
         clearTimeout(this.timer);
         this.timer = null;
         
-        this.fire('tick', this, {
+        this.emit('tick', this, {
             'now'     : this.clock,
             'elapsed' : this.perceived,
             'ticks'   : ++this.ticks
index 13357f3..dad1630 100644 (file)
@@ -93,7 +93,7 @@ Mixin.subclass('Speciated', {
             ;
             Species.__species_props__ = props;
             
-            cls.fire('speciate', Species, { 'id':id, 'species':Species, 'cls':cls });
+            cls.emit('speciate', Species, { 'id':id, 'species':Species, 'cls':cls });
             return Species;
         },
         
index 093afb4..b074682 100644 (file)
@@ -21,14 +21,14 @@ new evt.Class('DataFile', {
     },
     
     load : function load(){
-        this.fire('load');
+        this.emit('load');
         jQuery.getJSON(this.path, this.process);
         return this;
     },
     
     process : function process(data){
         this.data = data;
-        this.fire('process', data);
+        this.emit('process', data);
         // console.group('DataFile: processing '+this.path);
         
         var types = Y(data.types)
@@ -57,7 +57,7 @@ new evt.Class('DataFile', {
         }, this);
         
         // console.groupEnd();
-        this.fire('complete');
+        this.emit('complete');
         return this;
     },
     
@@ -65,7 +65,7 @@ new evt.Class('DataFile', {
      * @private
      */
     die : function die(reason){
-        this.fire('error', this, { 'reason':reason });
+        this.emit('error', this, { 'reason':reason });
         throw new Error(this+' Error: '+reason);
     },
     
index b2f564a..9e00b8d 100644 (file)
@@ -60,7 +60,7 @@ new evt.Class('Loader', {
     start : function start(){
         if (!this.running) {
             this.running = true;
-            this.fire('start');
+            this.emit('start');
             this.nextJob();
         }
         return this;
@@ -73,7 +73,7 @@ new evt.Class('Loader', {
      */
     stop : function stop(){
         if (this.running) {
-            this.fire('stop');
+            this.emit('stop');
             this.running = false;
         }
         return this;
@@ -90,7 +90,7 @@ new evt.Class('Loader', {
                 && self.queue.length === 0 
                 && self.inFlight.length === 0 ) {
             self.running = false;
-            self.fire('complete');
+            self.emit('complete');
             return;
         }
         
@@ -101,9 +101,9 @@ new evt.Class('Loader', {
         
         var job = self.queue.shift();
         // console.log(self+'.nextJob() --> '+job);
-        job.addEventListener('complete', function onJobComplete(evt){
+        job.on('complete', function onJobComplete(evt){
             // console.log(self+'.onJobComplete('+job+')');
-            job.removeEventListener('complete', arguments.callee);
+            job.removeListener('complete', arguments.callee);
             self.inFlight.remove(job);
             self.nextJob();
         });
index a30c26f..433d37b 100644 (file)
@@ -21,7 +21,7 @@ exports['config'] = new Config(defaults)
 ;
 
 // Updates the midpoint square when square-size changes
-config.addEventListener('set:pathing.pathSquare', function(evt){
+config.on('set:pathing.pathSquare', function(evt){
     var sq = evt.data.newval;
     config.set('pathing.pathSquareMid', new Vec(sq/2, sq/2));
 });
index cd8b6e4..65be09b 100644 (file)
@@ -45,8 +45,8 @@ new evt.Class('Buff', {
         this.applyStatMods();
         
         var data = { 'buff':this, 'unit':this.target, 'owner':this.owner };
-        this.fire('buff.conjure', this.target, data);
-        this.target.fire('buff.conjure', this, data);
+        this.emit('buff.conjure', this.target, data);
+        this.target.emit('buff.conjure', this, data);
     },
     
     tick : function tick(elapsed, now){
@@ -63,8 +63,8 @@ new evt.Class('Buff', {
         // var evt = 'buff.die.'+reason // We'll add this when we add polymorphic dispatchers
         var evt = 'buff.die'
         ,   data = { 'buff':this, 'unit':this.target, 'owner':this.owner };
-        this.fire(evt, this.target, data);
-        this.target.fire(evt, this, data);
+        this.emit(evt, this.target, data);
+        this.target.emit(evt, this, data);
         return this;
     },
     
@@ -95,7 +95,7 @@ new evt.Class('Buff', {
     
 });
 
-Buff.addEventListener('speciate',
+Buff.on('speciate',
     function onSpeciate(evt){
         var NewBuff = evt.data.species;
         if (NewBuff.fn.timeout === -1)
index cb81a02..5ca3037 100644 (file)
@@ -72,9 +72,9 @@ Y.subclass('Game', {
         this.viewport.append(this.map.ui);
         
         // automatically keep track of units
-        Thing.addEventListener('created', this.addUnit);
-        Thing.addEventListener('destroy', this.killUnit);
-        this.addEventListener('tick', this.tick);
+        Thing.on('created', this.addUnit);
+        Thing.on('destroy', this.killUnit);
+        this.on('tick', this.tick);
         
         this.level.setup();
         if (this.player) {
@@ -87,14 +87,14 @@ Y.subclass('Game', {
             this.isReplay = true;
             this.loadLog(replayFile);
         } else
-            this.fire('ready', this);
+            this.emit('ready', this);
     },
     
     destroy : function destroy(){
         this.stop();
         this.player.destroy();
-        Thing.removeEventListener('created', this.addUnit);
-        Thing.removeEventListener('destroy', this.killUnit);
+        Thing.removeListener('created', this.addUnit);
+        Thing.removeListener('destroy', this.killUnit);
         this.root.remove();
         this.resetGlobals();
     },
@@ -142,7 +142,7 @@ Y.subclass('Game', {
         
         if ( this.gameover )
             setTimeout(function(){
-                self.fire(self.gameover, self.player);
+                self.emit(self.gameover, self.player);
   &nb