Famous / engine

MIT License
1.75k stars 250 forks source link

node.emit() does not emit to children that have onRecieve set as a component #428

Open skaraman opened 9 years ago

skaraman commented 9 years ago

Hello,

The function node.emit() doesn't send an event to children that have an onReceive function added as a component:

For example:

FamousEngine.init();

// Initialize with a scene; then, add a 'node' to the scene root
var scene = FamousEngine.createScene();
var rootNode = scene.addChild();

rootNode.onReceive = function(event, payload) {
  if(event==='click'){
    rootNode.emit('custom_event',payload);
  }
};

var myNode = rootNode.addChild();
myNode.setSizeMode('absolute', 'absolute')
    .setAbsoluteSize(160, 160)
    .setPosition(0,0,0);
myNode.addUIEvent('click');

var boxElement = new DOMElement(myNode);
boxElement.setProperty('background', '#bbb');
boxElement.setContent('Click Me');

var myComponent = {
  id: null,
  node: null,
  onMount: function (node) {
      this.id = node.addComponent(this);
      this.node = node;
  },
  onReceive: function (event, payload) {
    console.log('received: ',event,payload);
  }
};

myNode.addComponent(myComponent);

Expected console output:

received:  click MouseEvent {type: "click", defaultPrevented: false, timeStamp: 1437079352167, detail: 1, screenX: 30…}
received:  custom_event MouseEvent {type: "click", defaultPrevented: false, timeStamp: 1437079352167, detail: 1, screenX: 30…}

Actual console output:

received:  click MouseEvent {type: "click", defaultPrevented: false, timeStamp: 1437079352167, detail: 1, screenX: 30…}

This is due to an issue in Dispatch.prototype.dispatch which only emits an event to a child of the emitter if that child has onReceive set as a direct or prototype function. I've modified the Dispatch.prototype.dispatch function to also emit to any components of the child that have an onRecieve direct or prototype function.

/**
 * dispatch takes an event name and a payload and dispatches it to the
 * entire scene graph below the node that the dispatcher is on. The nodes
 * receive the events in a breadth first traversal, meaning that parents
 * have the opportunity to react to the event before children.
 *
 * @param {String} path path of the node to send the event to
 * @param {String} event name of the event
 * @param {Any} payload data associated with the event
 *
 * @return {undefined} undefined
 */
Dispatch.prototype.dispatch = function dispatch (path, event, payload) {
    if (!path) throw new Error('dispatch requires a path as it\'s first argument');
    if (!event) throw new Error('dispatch requires an event name as it\'s second argument');

    var node = this._nodes[path];
    if (!node)
        throw new Error('No node registered at path: ' + path);

    this.addChildrenToQueue(node);
    var child;

    while ((child = this.breadthFirstNext())){
        if (child && child.onReceive){
            child.onReceive(event, payload);
        // added this to cover the case when onReceive is added as a component
        } else if (child){
            for(var i = 0; i < child.getComponents().length; i++){
                if (child._components[i].onReceive) {
                    child._components[i].onReceive(event, payload);
                }
            }
        }
    }
};