I wouldn't have expected to write so much about events in enyo but there's a lot of depth to cover.  I previously posted about an Animated Grid Layout control I created which I later used in Score Keeper for the board selection.  I have a similar requirement for the main panes of InContact but also need to reorder the items.[[MORE]]

To enable the reordering, I wanted to defer the event handling to the new grid control rather than binding them in my app.  This keeps that logic contained to a separate control (one I could swap out for an alternate grid control if desired) and not muddling up my main app code.  The rub with this is that is was difficult to add an event handler to an enyo instance when you're not the owner of the control (in my scenario, the main app is the owner whereas the grid is just the container.  More on ownership and containment in Enyo Daily 3).

The rather simple solution I discovered was to add an instance function to the enyo instance during creation.  The function is bound (using enyo.bind) to the grid control so it can enable reordering.

One caution with this approach is that it will by default prevent calling a handler declared for the onX event for the instance.  You could call dispatchIndirectly yourself in your instance method but I'll leave that as homework.  :)

Here's a quick example.  I'll share the reorderable grid control once it's actually complete!

var _Example = {
    name:"com.technisode.example.App",
    kind:"Control",
    components:[
        {kind:"ApplicationEvents", name:"appEvents", onUnload:"logAppEvent"},
        {kind:"EventSender", name:"eventSender", onSend:"sent"},
        {kind:"Button", onclick:"clicked"},
        {kind:"ContainerListener", components:[
            {kind:"Button", caption:"Clicking me will be handled by my container"}
        ]},
        {kind:"Input", onkeypress:"press"},
        {kind:"Dashboard", name:"db"}
    ],
    create:function() {
        this.inherited(arguments);
       
        this.$.db.setLayers([{title:"My Dashboard", text:"important message"}]);
       
        // map all ApplicationEvents to logAppEvent
        var ae = this.$.appEvents;
        Object.keys(enyo.ApplicationEvents.prototype.events).forEach(function(key) {
            //enyo.log("setting handler for ",key);
            ae[key] = "logAppEvent";
        });
       
        enyo.dispatcher.rootHandler.addListener(this);
    },
    logAppEvent:function(source, e) {
        enyo.log("logAppEvent",e.type);
    },
    resizeHandler:function() {
        enyo.log("dimensions",window.document.body.offsetWidth,window.document.body.offsetHeight);
    },
    captureDomEvent:function(e) {
        enyo.log("A DOM event occured", e.type);
       
        // returning true would indicated the event is captured and prevent the bubble phase
        // thereby preventing the declared handlers (clicked in this case) from being called
       
        // returning false (or no explicit return) lets things continue
        return false;
    },
    dispatchDomEvent:function(e) {
        // like any other method, you could override dispatchDomEvent and implement custom routing
        e.myCustomField = "This is a custom field";
       
        return this.inherited(arguments);
    },
    press:function(source, event) {
        enyo.log(source, event);
    },
    clickHandler:function(source, event) {
        enyo.log("bubbled up to me", event.myCustomField);
       
        // calling event.stopPropagation() or returning true will end the bubble phase
    },
    clicked:function(source, event) {
        // trigger my custom events
        this.$.eventSender.go();
       
        // toggles event handler between send and secondSent ... just because ...
        this.$.eventSender.onSend = (this.$.eventSender.onSend === "sent") ? "secondSent" : "sent";
       
        // calling event.stopPropagation() or returning true will end the bubble phase
    },
    sent:function(source, one, two, three) {
        enyo.log("sent", one, two, three)
    },
    handleOnAlert:function(source, obj) {
        enyo.log("alerted", enyo.json.stringify(obj));
    },
    secondSent:function(source, one, two, three) {
        enyo.log("secondSent handles onSend now", one, two, three)
    },
    customGlobalEventHandler:function(sender, param) {
        enyo.log("customGlobalEvent", param.data);
    }
}

var _ContainerListener = {
    name:"ContainerListener",
    kind:"Control",
    createComponent:function() {
        var o = this.inherited(arguments);
        o.clickHandler = enyo.bind(this, "childClicked");
        return o;
    },
    childClicked:function(source, event) {
        enyo.log("one of my child nodes was clicked!")
    }
}

var _EventSender = {
    name:"EventSender",
    kind:"Component",
    events:{
        onSend:"handleOnSend",
        onAlert:{value:"handleOnAlert", caller:"sendAlert"}
    },
    create:function() {
        this.inherited(arguments);
       
        enyo.dispatcher.rootHandler.addListener(this);
    },
    go:function() {
        this.doSend(1,2,3);    // dispatchIndirectly
        this.sendAlert({a:1, b:2});
        window.enyo.dispatch({type:"customGlobalEvent", data:"something to pass"});
    },
    customGlobalEventHandler:function(sender, e) {
        enyo.log("also caught customGlobalEvent");
    }
}

enyo.kind(_ContainerListener);
enyo.kind(_EventSender);
enyo.kind(_Example);