rangerer.at

PGadmin3 with empty password

After an update of PGadmin3 I was no longer able to save empty passwords for servers. Therefore whenever I wanted to connect to a server I had to click OK on the password dialog.

As this became annoying I found out that the only difference between my old servers (without the need for a password dialog) and my new servers was in the .pgpass file in my home directory.

This file contains one saved password per line and follows the syntax:

hostname:port:database:username:password

So I quickly added two new lines for my newly added servers using * for database and leaving password empty and got rid of the password dialog.

YUI custom events

When finally being able to hook into the lifecycle of the gallery-node-accordion module, I wanted to use events that other parts of the code could subscribe to. That way I wouldn't need to hard-wire otherwise unrelated business logic.

My first approach was to fire custom events on the corresponding nodes of the accordion: * accordion root fires accordion:ready * accordion section fires accordion:item:open and accordion:item:close

That way I would be able to subscribe to those events via Y, the accordion root or the corresponding section. In order to benefit from event bubbling and be able to subscribe in other parts of the code (with the same Y object) I needed to publish the events for the corresponding nodes:

accordion.publish('accordion:ready', { fireOnce: true, emitFacade: true, broadcast: 1 });
accordion.all('.yui3-accordion-item').each(function(node) {
    node.publish('accordion:item:open', { emitFacade: true, broadcast: 1 });
    node.publish('accordion:item:close', { emitFacade: true, broadcast: 1 });
});

While I liked the concept and flexibility of firing those events on certain nodes it turned out that there are certain limitations to that approach.

YUI().use('node', function(Y) {
    Y.use('gallery-node-accordion', function() {
        accordion.fire('accordion:ready');
    });
    Y.use('node', function() {
        Y.on('accordion:ready', function(e) { ... }, '.yui3-accordion-item');
    });
    Y.use('io', function() {
        Y.on('accordion:ready', function(e) { ... }, '.yui3-accordion-item');
    });
});

In the above code the subscriber in Y.use('node') triggers, but the subscriber in Y.use('io') does not. While in theory both should trigger (broadcasting being enabled and both sharing the same Y instance), there seem to be certain limitations to broadcasting events fired from a node. These restrictions seem to be specific to certain modules and/or if they were already loaded when the event is fired, but I don't fully understand the design decision behind it.

The easy solution is to fire and subscribe to custom events on the Y instance rather than using the specific nodes. You will loose the ability to limit your event subscription to a specific accordion instance or item via generic accordion:* events, but using application specific events will make your code cleaner and easier to understand. So I introduced the events people:accordion:ready, people:section:open and people:section:close leading to the following code:

Y.use('gallery-node-accordion', function() {
    Y.publish('people:accordion:ready', { fireOnce: true });
    Y.publish('people:section:open');
    Y.publish('people:section:close');
    Y.fire('people:accordion:ready');
});
Y.use('node', function() {
    Y.on('people:accordion:ready', function(e) { ... });
});
Y.use('io', function() {
    Y.on('people:section:open', function(e) { ... });
    Y.on('people:section:close', function(e) { ... });
});

YUI inheritance

At work we are using the gallery-node-accordion module from the YUI gallery to group content into sections. While the module is quite useful to transform already existing content into an accordion structure, it falls short on ways to hook into the accordion lifecycle. A typical task might be to load ads when opening a section or scroll to the head of the section once the accordion animation is complete. Normally you could accomplish those hooks by firing events for those specific moments in the accordion lifecycle and let users subscribe to those events to add business logic. Unfortunately caridy (the developer of gallery-node-accordion) did only leave todo comments instead of actually firing those events.

The proper solution would have been to fork caridys work from github, add those events, post a pull request for caridy and point our application to use my version of gallery-node-accordion. I decided to take the easy option and create a class that would inherit from Y.Plugin.NodeAccordion and overwrite the methods that I want to fire events for.

Y.namespace('Plugin').PeopleAccordion = Y.Base.create('PeopleAccordion', Y.Plugin.NodeAccordion, [], {
    initializer: function(config) { ... },
    _openItem: function(item) { ... }
}, { NS: 'accordion' });

It took me quite a while to get the above code working and have a functioning Y.Plugin.PeopleAccordion class that I could use. The main reason was that although NodeAccordion does already supply the static NS attribute it needs to be supplied by PeopleAccordion as well.

When using Y.Base.create the initializer function is augmented with the one from NodeAccordion and therefore you can simply add your code and both functions will be called. So the only thing left to do was firing an event in _openItem and forward the call to NodeAccordion._openItem. After playing around for another few hours I came to the conclusion that there is no way to call the parents _openItem method from the PeopleAccordion instance. I was able to get a prototype of NodeAccordion and call (or apply) the _openItem method, but as that method needs to operate on the instances attributes it was no good.

I was at a dead end and had to resort to replacing the click event handlers in PeopleAccordion with my own.

node.plug(Y.Plugin.Accordion, { ... });
node.accordion._eventHandler.detach();
node.accordion._eventHandler = this.delegate('click', function(e) {
    // custom code
    node.accordion.toggleItem( e.target );
    e.target.blur();
    e.halt();
}, '.yui3-accordion-item-trigger');