Tuesday, August 11, 2009

GSoC09: Statistics module 8th weekly report: Refactoring, fixes... and jQuery 1.3!

So... the penultimate week of GSoC came and passed, being a great moment for maintenance tasks like refactoring, bug fixing and coding small features left behind during project track. Beside that, following a Madhusudan's proposal, we've finally upgraded to jQuery 1.3 (commit 5723f329d0) and to JQueryUI 1.7 (commit 4e2789b8e8)!

Here's the story: following Pawel's suggestions, I've made little changes to dashboard's code to transclude itself when encountering DIVs with class "melangeDashboard" instead of MELANGE tags with attribute type "statistic_dashboard". Furthermore, I removed the fancy stack menu (that was too different from the rest of the application style) and put only a "new chart" link in the upper side of the page. That should be a temporary solution, as more features will come into place we'll integrate another menu.

A couple of bug fixes:
  • Before this week, if you changed the visualization type right after widget creation, it wouldn't be saved on the backend. It would have happened only if you changed it after one dashboard reloading. Now it's fixed.
  • Fixed the bug that Daniel Hans mention in his post: now, when switching the virtual statistic, the visualization is properly refreshed.
I've focused also on some refactoring. First of all I've finally changed what Daniel Hans and Lennie suggested me when I've sent patches to the mailing list (mainly on Python code) :)

The main work, however, has been on the Javascript side, mainly to avoid code duplication (for example code for updating a widget in the backend was present three times) and to separate dashboard code and widget code better. For example, before the refactoring, code to move and delete widgets in the dashboard was written in the widget code, while these are functions more related to dashboard logic.

But while porting the logic to move widgets inside the dashboard has been an easy task, as the dashboard already contained all the code to make the widgets move (using jQuery sortable plugin and binding the function to update the backend to the "stop" event), widget deletion has not been so easy to accomplish. Why? Because the deletion button is properly created in the widget class, and the function to delete the widget in the backend is in the dashboard, so in another scope.

That could be easily workarounded if I made the deletion code a "privileged" function of the dashboard... but, to me, this is not correct design. Deleting a widget (and so the chart entity in the backend) is a crucial task, so the function that does this should not be callable from other code. The solution? Publish/Subscribe pattern! While this is a built in behavior in Dojo, this is not the case in jQuery namespaced events (they can be used as topics), but then it happens that using a "click.widgetdeletion" namespace, when it bubbles up through the DOM, it loses the "widgetdeletion" part (so appearing as a common "click" event). Furthermore, to have a clean code, I would have liked to pass a reference to the widget instance with the event, but no data could be passed along with the event in jQuery 1.2.6.

So I've made a quite ugly hack, using common event delegation, detecting the target object and calling the deletion function if it has the "remove" class (which happens to be the class of the remove button... but what if we want to call that class with some other name in the future?), and using jQuery traversing to detect the ID of the widget from the real id of the parent div DOM node (which has a pattern like widget-IDNUMBER, that is extracted using a simple regex) and then using it to retrieve the widget instance from the widget list array that is present as a variable in the dashboard. Something like:

var widget_id = jQuery((jQuery(event.originalTarget).parents(".widget"))[0]).attr("id");
var widget_instance = widgets_list[/^widget-(\d*)$/.exec(widget_id)[1]];

Pretty ugly, isn't it?

But then, after jQuery 1.3, and using the new Event object, all is smoother now:

This is what happens after confirming deletion:

jQuery(this).trigger({type: "widgetdeletion",widget: _self}).remove();

You can notice just an event triggering, with a specific type (as a publish/subscribe topic) and passing the widget instance along with the event. In the dashboard, catching the event is as easy as:

dashboard.bind("widgetdeletion",function (event) {updateOnDelete(event.widget);});

That is, binding the "widgetdeletion" event and calling the proper function, passing it the widget instance reference. Simpler, cleaner and not prone to be messed if something in the HTML code changes (as the classes assigned to a widget or to its remove button).

Some enhancements made during the week has been the live editing of widget titles and support for the new get_available_statistics function (made by Daniel Hans), that has proper rights checking and returns all statistics (GSoC/GHOP, 2008/2009..and so on) with their proper scope path and link ids.

For refactoring purposes again, I've tried to look for a suitable browser side templating engine. In Dojo, there's the faboulous dojox.dtl Django porting. But what about jQuery? There's a bunch of technologies out there (made as jQuery plugin or standalone), as:

Standalone
Google JSTemplate
Ajax Pages
Pure (this can be used also along with jQuery)
JBST
jQFRagment
JSHTML
twoBirds
JsonFX.NET

jQuery plugins/dependent
jTemplate
chain.js
Nano
John Resig's micro templating
Some other guidelines plus fixes to John Resig's micro templating
Custom made 1, custom made 2

It's really not easy to deal with all these different options, and to explore major advantages/drawbacks. I'll try to do my best during this week, because it's really a must-have for us, to avoid ugly mixing between JavaScript and HTML code.

No comments:

Post a Comment