mardi 21 septembre 2010

jqVideo - centralize your Flash video library using a jQuery mashup

This paper discusses how we put together a jQuery mashup or super plugin.

As Flash video is omnipresent on the web nowadays, we will use it as an example.

A jQuery Mashup is a means for managing the use of two or more plugins all together. For instance, our demonstration is using Flowplayer and jqModal in order to control the instantiation of inline and modal video while optimizing our page by minimizing Javascript and HTML page code footprint.

This strategy is also very helpful for maintenance purposes as it allows centralized code and eases integration.
For example, our video links  specify a target template and the specifics parameters to override the video plugin defaults.  Finally, some design issues such as width and height can also be addressed via CSS. So it is better to use width and height parametrization for custom sizing and CSS for standard rules.

As for jqModal, it requires a page to host the video. This requirement has been tweaked into a feature since it allow to either point to a specific page or custom template.

Set up a modal video:
a class="videoTrigger" href="assets/videoContainer or specific template with content.htm?play=true&video=http://myVideo.flv.smil&width=520&height=310"

Sets up an inline video with autoplay:
a class="inlineVideoDisplay inlineHDformat" href="http://myVideo.flv.smil?play=true" id="myVideoID"


Orchestration

Setup  methods are gathered into a namespace. They allow to arrange the proper video pattern and to pass specified HREF values to the Flowplayer video plugin and jqModal.

if (typeof jqVideo === 'undefined' || !jqVideo) {

    jqVideo = {};
}

Methods are declared this way :

jqVideo.myFunction = function (params) {}.

Aside our namespace is the a Flowplayer video instantiation plugin code using A Plugin Development Pattern. It allows to define and override Flowplayer default parameters while limiting video instantiation code footprint.


(function($) {

    $.fn.video = function(options) {
        var defaults = {

 Code...

      };

    };
})(jQuery);

jqModal trigger code is added to the DOM on init and values are passed via a parameter object to assure proper plugins communication. As mentioned earlier,  values are passed via the jqVideo.modal  namespace.


jqVideo.setModalParams = function(str)
   {
       var params = jqVideo.getHashValue(str);
       jqVideo.modal = { 
             play: params.play, 
             video: params.video, 
             width: params.width, 
             height: params.height };
       return false;
  }

Finally, in order to enhance our code furthermore, we are using another nice feature provided by jQuery to init our UI component with the document.ready(); function.

This method can be used from within our a library to automatically init our components without having to add any inline Javascript code.

To implement this functionality one need to use a specific trigger class which will differentiate the desired component type. For instance, in our video library model and inline video are differentiated using a specific class such as .inlineVideoDisplay or .videoTrigger.

$(document).ready(function () {
        var trigger = $('a.videoTrigger');
    
 if (trigger.length > 0)
        {

 code...
        }
    
 $('a.inlineVideoDisplay').each(function(){  

 code...

        });
});

Some more information is available at this location.

Please write your questions and comments.

mercredi 19 août 2009

Optimisez votre interface de pagination client

Outre l’aspect socialisation, le nouveau paradigme Web 2 et l’avancement des technologies client permettent d’implanter certaines tâches traditionnellement réservées au domaine serveur au niveau de l'interface utilisateur. Plusieurs librairies utilitaires viennent nous appuyer dans ce travail, elles nous secondent dans la mise au point des fonctionnalités avancées et nous permettent d'améliorer l'expérience utilisateur et d'optimiser les performances des serveurs par l’attribution d’une plus importante charge de traitement à l'application client.

Dans cet essai, nous aborderons une fonctionnalité de traitement de pagination au niveau client. Cette dernière permet de traiter dynamiquement les informations d’un tableau, de mettre en place une fonctionnalité de pagination et de tri de colonne. De plus, nous traiterons de la persistance1 du contexte qui permet d'enrichir l'expérience utilisateur dans un optique client sans état (stateless). Donc, encore une fois, sans pour autant dépendre de fonctionnalités serveur.

Librairies utilisées

Dans l’élaboration de notre interface, nous avons utilisé 2 librairies connues soit Sarissa et Prototype.

De plus, nous avons développé une librairie qui nous permet de gérer la fonctionnalité de pagination ainsi que la persistance de l’interface. Au niveau de cette fonctionnalité, nous allons utiliser le terme «Pager», un script utilisant le modèle de programmation «Singleton». En ce qui a trait au traitement de tri des données, nous avons utilisé la librairie Sarissa. Celle-ci permet de les télécharger via AJAX les fichiers de données et de traiter l'assemblage d'un fichier XML et d'un gabarit XSLT. Finalement, nous avons utilisé la librairie Prototype comme librairie de base dans la programmation de «Pager» ainsi que pour mettre au point une couche fonctionnelle (Javascript) entièrement détachée de la couche de présentation (XHTML).

Initialiser les composantes

// Initialisation de l'interfaces avec Prototype

Event.observe(window,  'load', function() {


// Détection  de la zone dynamique
if ($("tableau")){

// Détection de la persistance existantes 
    pager.detectePersistance();

// Attribution des valeurs de persistance 
    var ordre = pager.colOrder;
    var colonne = pager.colSort;
    var type =  pager.colType;

// Chargement des données avec Sarissa
    loadXML(ordre, colonne,  type)

};
}); 

Affichage des données

Dans un premier temps, la librairie Sarissa est responsable du téléchargement asynchrone des fichiers de donnée XML et du gabarit XSLT puis, de générer le tableau de donnée.
//  import XML
var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("GET",  "disparus.xml", false);
    xmlHttp.send(null);
    var xmlDoc = xmlHttp.responseXML;

// Instancie le processeur XSLT
....var xsltProcessor = new  XSLTProcessor();

// Import XSLT
    var xslHTTPRequest = new  XMLHttpRequest();
    xslHTTPRequest.open("GET",  "disparus.xsl", false);
    xslHTTPRequest.send(null);
    var xslDoc =  xslHTTPRequest.responseXML;

// Creation  du document XML en mémoire
    var resultDocument =  Sarissa.getDomDocument();
    xsltProcessor.importStylesheet(xslDoc)
De plus, Sarissa est utilisé pour contrôler le tri des colonnes par le paramétrage des variables d’affichage du gabarit XSLT via Javascript. Ainsi, lors d’un clique sur la rubrique de colonne, la valeur de tri de colonne est acheminée au gabarit XSLT puis, le tableau régénéré dans l’ordre désirée à la page 1 de la séquence de pagination.
//  Appel
onclick="pageCourante('1'); 
loadXML('ascending','date','text','ordreDate')”

//  Passe  paramètre de la persistance au XSLT
xsltProcessor.setParameter(null,  "ordre", ordre);
xsltProcessor.setParameter(null,  "colonne", colonne);
xsltProcessor.setParameter(null,  "type", type);

... Code ...

//Ordre de tri par Date//
<xsl:choose>
    <xsl:when test="$colonne = 'date' "> 
        <xsl:choose>   
        <xsl:when test="$ordre = 'descending' ">     
         <div id="ordreDate" class="desc" 
         onclick="pageCourante('1');
         loadXML('ascending','date','text','ordreDate')">
         Par ordre de <strong>date</strong></div>
        </xsl:when>  
        <xsl:otherwise>    
         <div id="ordreDate" class="asc" 
         onclick="pageCourante('1');
         loadXML('descending','date','text','ordreDate')">
         Par ordre de <strong>date</strong></div>
        </xsl:otherwise>
        </xsl:choose>
    </xsl:when>
    <xsl:otherwise>   
         <div id="ordreDate" class=""  
         onclick="pageCourante('1');
         loadXML('descending','date','text','ordreDate')">
         Par ordre de <strong>date</strong></div>
    </xsl:otherwise>
</xsl:choose>
Ensuite, est mis en œuvre «Pager» qui se charge de l’affichage du nombre d’éléments désirés par page. Par exemple, un tableau comportant de 31 et 40 éléments dont 4 éléments sont visibles pour chaque séquence, affiche une pagination de 10 pages. La page courante est aussi mise en surbrillance selon les pratiques courantes en ergonomie d’interface2.
//Initialisation du compteur de pagination 
// (tableau, #éléments)
pager.init("tableau_dynamique","4");
Compteur de pagination généré par Pager

Une autre fonctionnalité d’intérêt est la possibilité de paramétrer des ancres. Elle permet de repérer l’emplacement de l’élément dans le tableau et de l'afficher en surbrillance. Cette fonctionnalité est possible par l’utilisation de JSON et des capacités de programmation avancées dont nous procure la librairie Prototype. La valeur de l'ancre est ainsi associée au rang dans laquelle elle se trouve au sein d'un tableau (ancre : rangée du tableau).
Par exemple, l'URL suivant nous dirige vers le 2e item de la page 6 (22 / 4 = 5.5).

http://clients.teksavvy.com/~cberube/demos/pager_demo/index.html#forwardTo:22

Persistance de l’interface

Au niveau de la persistance client, nous avons implanté une technique AJAX qui utilise la réécriture d’URL pour sauvegarder l’état de l’interface utilisateur.
Pour ce faire, le dièse(#) est utilisé au lieu du modèle de sérialisation3 traditionnel tel que démontré dans l'image suivante.
persistance avec hash
Cette technique nous permet de procéder à la réécriture d’URL sans avoir d’effet immédiat sur le page courante. La persistance sera uniquement enregistrée dans l’historique du fureteur lors du passage (clique) vers une nouvelle page. Dans le même ordre d’idée, l’URL peut ainsi être emmagasiné en signet, puis l’état de la page régénérée lors de l’initialisation de l’interface.
//  indexColumn="date"  ordre="descending" datatype="number"
pager.changePersistance(ordre,  colonne, type);

// Vérification et initialisation de la persistance

if (typeof ordre == "undefined"){ordre = pager.colOrder;}
if (typeof colonne == "undefined"){colonne = pager.colSort;}
if (typeof type == "undefined"){type = pager.colType;}

Avantages

L’utilisation d’application cliente autonome génère plusieurs avantages.
Dans un premier temps, elle permet de diminuer la charge d’utilisation des serveurs en limitant le nombre de connexions HTTP et, dans un deuxième temp, elle permet d’utiliser la mémoire tampon du fureteur pour diminuer le temps d’attente de l’utilisateur. La diminution de la charge du serveur est principalement attribuable au cycle de mise à jour de la page. En effet, la gestion de la pagination à l’aide de « Pager » permet d’éliminer les connexions subséquentes à l’initialisation puisque les données sont transférées dès l’initialisation en format XML.
De plus, l’utilisation de services de diffusion de contenu public (CDN4) tel Google et OAL pour le téléchargement de librairie permet de maximiser la consommation de bande passante tant au niveau serveur que client. Les CDN visent en effet ce double objectif puisque les librairies provenant de ces sources peuvent être utilisées pour plusieurs applications distinctes et sont persistantes dans la mémoire tampon du fureteur.
Finalement, l’utilisation de la mémoire cache peut être maximisée par la mise en place de fonctionnalité de contrôle de version afin de limiter le téléchargement répétitif des données par l’entremise de Etags5 ou par la configuration d'un date d'expiration au niveau du fichier pour les données statiques.

Conclusion

Nous avons démontré une application cliente qui diminue le temps d’attente de l’utilisateur et permet, par conséquent, d’agrémenter son expérience et d’améliorer la perception d'efficacité qu’il en retire. De plus, au niveau technique, ce type d’interface procure une indépendance de plate-forme et permet une portabilité accrue.
Cela met en relief les grands avantages du nouveau paradigme Web 2 et l'utilisation d'interfaces utilisateurs plus autonomes.
La mise en place d’une architecture modulaire et l’utilisation de techniques AJAX permettent ainsi de mettre au point des fonctionnalités Web multi plate-forme moins exigeantes en termes de performance des infrastructures. Il s’agit d’une approche doublement gagnante (win-win) puisque autant l'application cliente que les systèmes d'hébergement en retirent de grands avantages en terme de performance et de maximisation de la bande passante.

Démo : http://clients.teksavvy.com/~cberube/demos/pager_demo/index.html
  1. Sérialisation : http://fr.wikipedia.org/wiki/S%C3%A9rialisation
  2. Navigation visible: http://www.cui.unige.ch/isiwiki/index.php/Cours_ISI_-_Les_principes_de_Tognazzini#Navigation_visible
  3. Persistance : http://fr.wikipedia.org/wiki/Persistance_(informatique)
  4. CDN : http://code.google.com/apis/ajaxlibs/
  5. Etags : http://developer.yahoo.com/performance/rules.html#etags