Personal tools

Mar 22, 2010

Conditional Javascript choices relying on server side configurations

Filed Under:

Have you ever felt the need of make a Javascript choice, executing some client side actions, basic this choice on some server side configuration? Here an interesting way I recently taked, thanks to the powerful jQuery plugins structure.

Describing the problem

I'm near to release a new version of collective.flowplayer_toolbar (so it's only on collective SVN right now).

The first version of the product (see the old blog post about it) give a new Javascript controlsbar to Flowplayer, disabling the native Flash ones.

In a production environment we received some comments about this choice because when disabling the Flash controls you are not able anymore to use fullscreen mode... bad!
The only solution is to keep both control systems because is not possible (due to security issue) to control fullscreen from Javascript.

Have both controls give us the best freedom (features and accessibility)... but to be honest I find it really ugly!

Flowplayer instance with controlsbar

So, the best way is to give some configuration for switch from:

  • using only the Javascript controls
  • using Javascript and Flash controls

The collective.flowplayer choice

The same kind of problem has been overcome by collective.flowplayer.
Some of the configuration you can manage in the flowplayer_properties leave to changes in the client-side Javascript behaviour.

I'm not sure that this is the only motivation, but this problem is solved there providing the Javascript code not using a static file (so not giving a Javascript text source) but using a zope 3 view.
This view (collective.flowplayer.js) return a text/javascript content type:

    def __call__(self, request=None, response=None):
        """ Returns global configuration of the Flowplayer taken from portal_properties """
        self.update()
        self.request.response.setHeader("Content-type", "text/javascript")
        ...

The problem with this approach is that you must work with Javascript code written inside Python string... not really comfortable.

Of course, the problem is only for the developer. Client side the Web browser will not feel any difference.

Experimental approach

As far as Flowplayer works well with jQuery (and jQuery Tools, that is a requirement of collective.flowplayer using plone.app.jquerytools) I'm trying a second approach to the problem. This because:

  • I don't like to write Javascript inside strings
  • I really like the power of jQuery plugins support, and this has become more clear to me after having read recently a book on this.

Following the server side collective.flowplayer approach I added a new property to the portal_properties tool (it's only a single property, so I added it to the same flowplayer_properties sheet).

This boolean property (toolbar_flash_controlsbar) must only controls this single line of Javascript code:

this.getPlugin("controls").hide();

I need a way to execute this only when the flag is True.

The solution is simple: execute this line of code only when a Javascript variable is evaluated true/false:

if (!show_flash_controlsbar)
	this.getPlugin("controls").hide();

Now we only need a way to controls from the server this client variable, but first it's better to provide a default initialization.

As far as this control is executed inside a Flowplayer event, we can add the initialization before or after this piece of code, directly in the flow of page execution (so the order matters not):

var show_flash_controlsbar = false;

So the default is also the default of the toolbar_flash_controlsbar property (False) and we will not see that Flash controls.

Now we really need a way to react to a True value server-side.

The way used here is to include another (minimal) Javascript piece of code, this time after the ones above (so, if included, it must be after the initialization of the show_flash_controlsbar).

show_flash_controlsbar = true;

To keep controls on the inclusion of this we need to put this into a separate Javascript file and include it conditionally after the ones above.

The Generic Setup inclusion take this form:

 <javascript cacheable="True"
             compression="safe"
             cookable="True"
             enabled="True"
             id="++resource++flowplayer_toolbar_js/flash_controlsbar.js"
             insert-after="++resource++flowplayer_toolbar_js/flowplayer.accessible-controls.js"
             expression="portal/portal_properties/flowplayer_properties/toolbar_flash_controlsbar|nothing"
             inline="False"/>

In the expression TAL we check and evaluate the server side property (and for security we also put a |nothing, to prevent all sort of errors if, for example, the property has been deleted).

The portal_javascript tool take care of providing this Javascript script or not, even when the it's merged with other and cached.

Don't you talked of jQuery somewhere above?

The only problem with this approach is the namespace pollution. We must be sure that the name of this variable is not used/taken from other Javascript code somewhere in Plone.

jQuery and its plugins technology help in this, and also help to keep things more clear and ordered.

With the syntax below we can make this definition available only inside the jQuery environment, preventing that non-jQuery Javascript code change/use this variable.

jQuery.show_flash_controlsbar = false;

Note the use of jQuery name, not $ or jq: this is how plugin development guidelines recommend to define and use jQuery object when developing plugins.

In our code we can continue testing the condition in this way:

if (!jq.show_flash_controlsbar)
	this.getPlugin("controls").hide();

This is better than before, but we can make it more robust.

This approach can fails only if another jQuery plugin define and use the same name for an internal variable of function (ok, this is paranoyc); if this become true we can get errors or unexpected results.

The best jQuery way is to define an internal namespace, making this problem more and more difficult to arise.

So finally we define the variable in this way:

jQuery.flowplayer_toolbar = {
	/**
	 * Some other script can put this to true to enable also the native controlsbar plugin
	 * This is the only way to get features like the fullscreen.
	 */
	show_flash_controlsbar: false
};

And we test it like this:

if (!jq.flowplayer_toolbar.show_flash_controlsbar)
	this.getPlugin("controls").hide();

The last benefit of using an internal jQuery namespace is to keep similar variables or function together. May be that some other variables (or global functions?) can be needed in future version of this product, so we can add them inside the same namespace.

Going back: is this approach better or not?

Aswer: I don't know. This works well with browser cache but as you can see it needs some more work than providing a dynamic Javascript source from the server using a view.

The most annoying things is the registration of an additional entry inside the portal_javascript tool only for put an additional line of Javascript code... this is not a problem for one script, but can you imagine this repeated for all collective.flowplayer configuration (or for other products too)?
The numbers of entries will explode!