headermask image

header image

Tout est XML

Comme il est d’usage de dire que dans Unix tout est fichier, dans OpenLaszlo, tout est XML.
Le format XML est la base de l’architecture du langage OpenLaszlo, des objets aux communications en passant par le stockage des informations, tout passe par le XML. Ce tutoriel va aborder plusieurs aspects de la programmation OpenLaszlo, parmi lesquels :

  • Création d’une classe basique avec la syntaxe XML
  • L’affichage d’un texte contenant des balises HTML simples
  • La création dynamique d’une arborescence XML avec XPath et un datapointer OpenLaszlo
  • Le traitement de chaine avec ECMAScript (basique)
  • L’instanciation différée d’un Objet OpenLaszlo
  • Le clonage d’objet à partir d’une arborescence XML

Tout un programme !!!

Donc pour illustrer toutes ces facettes du langage je partirais sur un exemple assez simple, la création d’un script qui prend une chaine contenant un texte simple en HTML contenant des balises Bold Italique et
. Lorsque l’on clique sur le texte, le script génère une fenêtre dans laquelle chaque mot sera indépendant et se surlignera en orange au passage de la souris.

Voila le résultat :

cliquez sur le texte

1 – Début du fichier…

…Toujours pareil, le canvas…

  1.  <canvas>
  2.     
  3.      <script>
  4.      <![CDATA[
  5.          html = "<h1><b>Titre</b></h1><br/>Un texte standard ou <i>italique</i><br/>et une troisième ligne."
  6.       ]]>
  7.      </script>
  8.     
  9.      <dataset name="dstexte">
  10.          <texte/>
  11.      </dataset>

Ici, rien de bien compliqué, on ouvre la balise canvas, puis on initialise la chaine « html » avec notre texte. Vient ensuite la création de notre dataset (l’arborescence XML) ne contenant qu’une balise .

2 – La classe UnMot
La classe UnMot permet, comme son nom le laisse vaguement deviner, de créer des objets mots. (besoin de plus de précisions ??)

  1.  <class name="UnMot" extends="view">
  2.          <attribute name="mot" type="string"></attribute>
  3.         
  4.          <method event="oninit">
  5.              // initialisation du mot
  6.              this.LeMot.setText(this.mot);
  7.          </method>
  8.         
  9.          <method event="onmouseover">
  10.              this.setAttribute("bgcolor","0xFFA500");
  11.          </method>
  12.         
  13.          <method event="onmouseout">
  14.              this.setAttribute("bgcolor","0xFFFFFF");
  15.          </method>
  16.         
  17.          <text name="LeMot"></text>
  18.      </class>

Donc la classe UnMot étend la classe view (et donc hérite de toutes ces capacités). J’ai rajouté un attribut de type chaine qui me permettra d’initialiser le mot que représentera l’objet. Viennent ensuite les méthodes.
La première, déclenchée à l’initialisation de l’objet, permet d’initialiser le texte de notre objet avec la variable contenu dans l’attribut mot.
La seconde, déclenchée lorsque la souris passe sur le mot, permet de colorer le fond (attribut bgcolor) de notre view, ici en orange.
La troisième, repasse le fond en blanc lorsque la souris sort de la view.

Puis un objet de type texte est initialisé qui affichera le mot.
Vous noterez qu’il aurait été un peu plus rapide de créer notre objet en le faisant hériter directement de la classe text, mais ca aurais été nettement moins fun :) .

3 – Affichage du texte et génération du XML

Bon, la on rentre dans le vif du sujet.

  1.  <text multiline="true" text="${html}" clickable="true">
  2.         
  3.          <method event="onclick">
  4.          <![CDATA[
  1.  var chaine = this.getText()
  2.             
  3.              // séparation des lignes avec les <br/>
  4.              var tLignes=chaine.split("<br/>");
  5.                                     
  6.              // initialisation du pointeur sur le dataset
  7.              dp=dstexte.getPointer()
  8.             
  9.              // pour chaques lignes
  10.              for (numL = 0; numL < tLignes.length; numL++){
  11.                 
  12.                  // positionement dans le dset
  13.                  dp.setXPath('dstexte:/texte');
  14.                 
  15.                  // création de <ligne></ligne>
  16.                  dp.addNode("ligne",null)
  17.  
  18.                  // Placement sur la ligne précédemment créée
  19.                  dp.setXPath('dstexte:/texte/ligne['+(numL+1)+']');
  20.                                 
  21.                  // séparation des mots avec les espaces
  22.                  var ligne = tLignes[numL]
  23.                  var tMots = ligne.split(" ");
  24.                 
  25.                  // pour chaques mots
  26.                  for (numM = 0; numM < tMots.length; numM++){
  27.                                         
  28.                      // si c'est le premier mot, la couleur sera bleu, sinon noir
  29.                      if (numM == 0){
  30.                          var col = '0x0000FF'
  31.                      }else{var col = '0x000000'}
  32.                     
  33.                      // création de <mot coul="0x0000FF" >LeMot</mot>
  34.                      dp.addNode("mot",tMots[numM],{coul:col})
  35.                  }
  36.              }
  37.  
  38.              // création de la fenêtre d'annotation
  39.              annotwin.completeInstantiation();
  1.  ]]>   
  2.          </method>
  3.         
  4.      </text>

Nous créons un objet de type text avec comme texte la valeur de notre variable html, l’attribut « multiline » permet, comme son nom l’indique le changement de ligne, ici avec la balise HTML </br>. L’attribut « clickable » à vrai permet de définir la zone de texte comme clickable. A chaque click sur le texte un évènement « onclick » est envoyé au niveau de l’objet text et intercepté par la méthode qui à l’attribut event="onclick".

Intéressons nous maintenant à la méthode.
Le but de cette méthode est de créer l’arborescence XML suivante dans le dataset dstexte :

  1.  <texte>
  2.      <ligne>
  3.          <mot coul= « 0x0000FF»><h1><b>Titre</b></h1></mot>
  4.      </ligne>
  5.      <ligne>
  6.          <mot coul= « 0x0000FF»>Un</mot>
  7.          <mot coul= « 0x000000»>texte</mot>
  8.          <mot coul= « 0x000000»>standard</mot>
  9.          <mot coul= « 0x000000»>ou</mot>
  10.          <mot coul= « 0x000000»><i>italique</i></mot>
  11.      </ligne>
  12.      <ligne>
  13.          <mot coul= « 0x0000FF»>et</mot>
  14.          <mot coul= « 0x000000»>une</mot>
  15.          <mot coul= « 0x000000»>standard</mot>
  16.          <mot coul= « 0x000000»>troisième</mot>
  17.          <mot coul= « 0x000000»>ligne.</mot>
  18.      </ligne>
  19.  </texte>

Cette arborescence XML sera utilisée par la suite pour créer notre fenêtre.

Le texte de notre objet text est stocké dans la variable « chaine ». Puis nous créons un tableau nommé tLignes[] dans lequel nous stockerons chaque lignes de notre texte en se basant sur la balise HTML <br/> comme séparateur de ligne (la balise <br/> est normalisé par le W3C mais les navigateurs étant assez laxiste vis-à-vis de la syntaxe il est possible de rencontrer </br> voir même <br> et toutes les variantes avec majuscules).
Nous créons un DataPointer appelé dp qui va nous permettre de naviguer dans l’arborescence XML du dataset dstexte. Pour le moment dp pointe sur la racine de notre dataset.

Pour chaque lignes de notre texte (la longueur du tableau tLignes[]), nous commençons par nous placer au niveau de la balise <texte> à l’aide d’une requête XPath (dp.setXPath('dstexte:/texte');) puis nous créons notre balise <ligne> avec la méthode « dp.addNode("ligne",null)« .
Nous allons nous arrêter un moment sur la méthode addNode().

Cette méthode permet de créer un nouveau nœud dans notre arborescence, sa syntaxe est la suivante :

LzDatapointer.addNode(name, text, attrs)

LzDatapointer est notre pointer sur notre arborescence XML, pour nous dp.
name est le nom du nœud
text est le texte entre la balise de début et de fin
attrs sont les attribut de notre balise, sous la forme d’un objet de type {attribut1 :val, attribut2 :val}

Par exemple, pour créer le nœud suivant :
<humain sexe= « masculin » age= « 32 »>Marc</humain>

Nous utiliserons la syntaxe suivante :
dp.addNode("humain", "Marc", {sexe :’masculin’, age :32})

Si la balise ne contient pas de texte, il est impératif d’indiquer la constante « null » pour l’attribut « text ».

Une fois notre balise <ligne> créée nous nous plaçons à son niveau avec un requête XPath un peu plus évolué que la précédente. La requête « dstexte:/texte/ligne['+(numL+1)+'] » permet d’indiquer sur quelle ligne on souhaite se placer, pour cela nous utilisons la valeur de numL que nous incrémentons de 1 car XPath commence à compter à partir de 1. Ainsi la requête « dstexte:/texte/ligne[1] » nous place sur la première ligne, « dstexte:/texte/ligne[2] » sur la deuxième, etc…

En suite un tableau tMots[] est créer pour contenir chaque mots de notre ligne, nous utiliserons l’espace comme séparateur.

Ensuite, pour chaque mots de notre ligne, nous choisissons la couleur bleu (0×0000FF en hexadécimale) si c’est le premier sinon nous initialiserons la couleur à noir (0×000000).
Une fois la couleur déterminé nous créons la balise <mot> avec addNode().

Une fois toutes les lignes et mots traités nous invoquons l’instanciations complète de la fenêtre annotwin avec la méthode completeInstantiation().
Ce point un peu délicat du langage sera abordé dans le paragraphe suivant.

4 – La fenêtre annotwin et fin

  1.  <window id="annotwin" initstage="defer">
  2.          <simplelayout axis="y" spacing="1"/>
  3.  
  4.          <view name="ligne" datapath="dstexte:/texte/*">
  5.              <simplelayout axis="x" spacing="1"/>
  6.              <UnMot datapath="mot" mot="$path{'text()'}" fgcolor="$path{'@coul'}"/>
  7.          </view>
  8.      </window>
  9.     
  10.  </canvas>

Cette fenêtre est générée automatiquement à partir du fichier XML dstexte, la difficulté est que, dans notre cas, comme le dataset est vide jusqu’au click sur le texte, la fenêtre ne pourra être correctement générée.

C’est la qu’intervient les niveaux d’instanciation d’un objet définit avec l’attribut initstage.
L’attribut initstage est définit au niveau de la classe node dont hérite pratiquement tout les objets OpenLaszlo (dont le classe window) et permet de définir quand sera appelé la méthode « init » et, par extension l’émission de l’événement « oninit ». Les valeurs de l’attribut initstage sont les suivants :

immediate : La méthode init est appelé immédiatement comme dernier niveau d’instanciation de l’objet.

early : La méthode init est appelée immédiatement après que la vue (objet view) et ses enfants ont été instanciés.

normal : (par défaut) La méthode init est appelée quand le père (de l’objet) est initialisé.

late : La méthode init est appelée pendant un moment de repos processeur (idle time). Pour vérifier si la méthode init à été appelé il suffit de contrôler la valeur de l’attribut « isinited« . Il est possible de forcer l’initialisation avec la méthode « completeInstantiation ».

defer : La méthode init n’est pas appelé sans une invocation explicite de la méthode « completeInstantiation ».

L’attribut initstage permet de définir très finement le moment d’initialisation d’un objet.

Pour notre fenêtre j’ai utilisé le mode defer car le contenu de celle-ci est inconnu tant que la procédure de traitement du texte n’est pas terminé, nous ne connaissons donc pas la taille du contenue. En l’occurrence, la fenêtre est créer et sont contenu s’initialise lors de la création du fichier XML (nous revenons sur cette étape par la suite) puis, une fois le fichier XML terminé et tout le contenu créer, on appel la méthode completeInstantiation() qui initialisera la fenêtre et adaptera la taille au contenu.

Le contenu de la fenêtre sera donc les lignes et mots de notre chaine HTML. Le premier objet simplelayout permet de positionner les lignes de notre texte sur l’axe des y, donc verticalement.

Pour la suite du contenu, nous utiliserons le système de réplication automatique. C’est un des aspects d’OpenLaszlo qui rend le langage si puissant. Il est possible de générer des objets en fonction d’une arborescence XML , et cela de manière dynamique, c’est-à-dire que si le contenu du XML est modifié, les objets créer à partir de celui-ci sont modifiés aussi.

Ce tutoriel utilise l’ « ancienne » méthode de réplication, une nouvelle à été mis en place dans la version 4.0 mais encore trop peu documenté.

Donc, nous créons un objet view nommé ligne, c’est dans cette espace que nous alignerons les mots (grâce au simplelayout axée sur x). Nous attribuons un datapath à cette vue, datapath="dstexte:/texte/*", l’attribut datapath utilise le langage XPath pour gérer la réplication, en l’occurrence nous allons attribuer tous les enfants du nœud texte comme point de base de réplication de notre vue, donc la vue ligne (et son contenu) se répliquera à chaque nœud <ligne> de notre dataset.

Une fois notre vue « ligne » créer, sont contenu hérite du pointeur sur le dataset qui la généré, donc pour la première ligne »dstexte:/texte/ligne[1] » et ainsi de suite…

Le contenu de notre ligne sera une succession d’objet UnMot qui serons générés automatiquement en indiquant datapath="mot". comme à l’initialisation de la vue ligne le pointeur est placé sur le niveau de notre ligne, il suffit de définir sur quelle nœud l’objet doit baser sa réplication.

Nous récupérons ensuite le texte contenu entre les balises avec la commande $path{'text()'} ainsi que la couleur d’écriture avec la commande $path{'@coul'}.

Les objets mots et lignes seront donc générer en prenant le dataset dstexte comme squelette, sachant que toute modification dans le dataset provoquera une modification dans les objets. Il est ainsi possible de rajouter une ligne ou changer la couleur d’un mot en modifiant l’attribut « coul » d’un nÅ“ud <mot>.

Vous pouvez essayer en ajoutant un bouton avec le code suivant :

  1.  <button>tst
  2.          <method event="onclick">
  3.              dp=dstexte.getPointer();
  4.              dp.setXPath('dstexte:/texte/ligne[2]/mot[2]');
  5.              dp.setNodeAttribute("coul",'0xFF0000' )
  6.          </method>
  7.      </button>

Ce bouton crée un pointeur sur le dataset puis se place au niveau du 2eme mot de la 2eme ligne, puis modifie la valeur de l’attribut « coul » pour le mettre à rouge (0xFF0000 en héxa). Si vous cliquez sur le bouton après avoir cliqué sur le texte, le mot « texte » deviendra rouge, alors que seul de dataset à été changé.