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:
StandaloneGoogle JSTemplateAjax PagesPure (this can be used also along with jQuery)
JBSTjQFRagmentJSHTMLtwoBirdsJsonFX.NETjQuery plugins/dependentjTemplatechain.jsNanoJohn Resig's micro templatingSome other guidelines plus fixes to John Resig's micro templatingCustom made 1,
custom made 2It'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.