4D au pays des Widgets

Christophe Keromen

Un widget est une miniapplication autonome, dédiée à une tâche déterminée. Cette note technique montre comment réaliser un widget affichant dans une palette flottante des informations récupérées depuis un moteur 4D. Nous utilisons la technologie de Yahoo en raison de son fonctionnement multiplateforme, mais la technique utilisée s'adapte sans difficulté à Dashboard, l'équivalent d'Apple apparu avec Tiger. ♪

- Titre : 4D au pays des Widgets

- Auteur : Christophe Keromen

- Parution : 1 février 2006

- Licence : Ce document est issu de https://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- Introduction

- Présentation des Widgets

- Trois catégories

- Exemples de Widgets

- 4D aussi ! (4D Watcher, 4D Quizz)

- Comparaison des deux technologies

- Moteur d'exécution

- Programmation Yahoo! Widget

- Programmation Dashboard Widget

- Exemple de réalisation avec 4D

- Un exemple loisir avec Juke-Box

- Autres exemples possibles

- L'incontournable HTTP

- Serveur HTTP de 4D

- Construction de la structure XML

- Réalisation du widget infos4D

- Structure du widget infos4D

- Les champs Text

- Déclaration du bouton

- Les balises d'actions

- Les fonctions JavaScript

- La méthode d'analyse du XML : doParse

- Les balises de préférences

- Plus de généricité avec une TextArea

- Lourdeur de l'approche déclarative

- Déclaration du nouvel objet

- Remplissage par programmation

- Le résultat final

- Finalisation

- Désactivation du mode debug

- Possibilités de débogage

- Packaging

- Ressources

- Yahoo! Widgets

- Dashboard pour Tiger

- Les ressources complémentaires conseillées par O'Reilly

- Base exemple

Introduction

Différentes notes techniques ont présenté des exemples d'applications tierces pouvant s'interfacer avec 4D. Cette note technique montre comment réaliser un widget; Il s'agit d'une miniapplication affichant dans une palette flottante des informations récupérées depuis un moteur 4D.

Nous utilisons la technologie de Yahoo en raison de son fonctionnement multiplateforme, mais la technique utilisée s'adapte sans difficulté à Dashboard, l'équivalent d'Apple apparu avec Tiger.

Présentation des Widgets

L'idée de miniapplications autonomes, dédiées à une tâche déterminée, et fonctionnant dans leur propre fenêtre n'est pas nouvelle, les plus anciens se souviendront des accessoires de bureau des anciens systèmes Mac.

En février 2003, le concept est réapparu sur Mac OSX sous une forme graphiquement très élaborée avec les widgets de Konfabulator. Demeurée relativement confidentielle, sans doute parce qu'il s'agissait d'un produit payant et mono plateforme, cette approche est aujourd'hui déclinée sous deux formes qui rencontrent bien plus de succès :

• les Yahoo! Widgets, suite au rachat de Konfabulator par Yahoo! et à sa mise à disposition gratuite ;

• les Widgets de Tiger (technologie uniquement Mac OS X).

Trois catégories

Les widgets se classent dans l'une des trois catégories suivantes :

Les accessoires

Ils sont autonomes et ne requièrent ni accès à Internet, ni à une autre application. On y retrouvera les classiques horloges, calepins, calculettes, etc.

Les applications

Ce sont des modules couplés à une application existante comme iTunes ou Carnet d'adresses. Ils procurent une interface simplifiée, souvent dédiée à la consultation d'une partie de l'information. Leur fonctionnement est en général local à la machine.

Les informateurs

Ils sont conçus pour échanger des données au travers d'un réseau, Internet ou Intranet. Ils permettent de surveiller des processus ou d'avoir en permanence des informations disponibles dans une palette flottante dans un coin de l'écran.

Points communs de ces différentes déclinaisons :

• réalisation graphique très poussée avec gestion de la transparence, donnant un aspect très ludique ;

• simplicité de l'interface ;

• personnalisation de l'aspect par l'utilisateur afin de faciliter l'appropriation.

Exemples de Widgets

Parmi les exemples classiques et qui se retrouvent aussi bien chez Yahoo qu'Apple, la prévision météorologique. Vous pourrez ainsi constater qu'il fait beau au moins une fois par semaine en Bretagne.

Le widget Yahoo ! Weather

Malgré son apparente simplicité, ce widget est assez complexe, car outre un paramétrage assez important de l'aspect visuel, il offre :

• par un clic sur la fenêtre la possibilité d'obtenir des informations plus détaillées affichées dans une zone type ‘tooltip' ;

• des possibilités d'interaction au travers par exemple des boutons ‘extented forecast' et ‘info' ;

• la possibilité de choisir la ville dont la prévision météo sera affichée et un menu déroulant (obtenu par clic droit ou ctrl-clic remémorant les dernières villes sélectionnées).

Quoique plus rarement, les widgets peuvent également proposer des zones de saisie, comme ici pour choisir la ville concernée par l'affichage :

Exemple de dialogue saisissable dans un widget

Notons par la même occasion qu'un widget n'est pas limité à une seule présentation, mais peut proposer différentes interfaces suivant le contexte.

4D aussi ! (4D Watcher, 4D Quizz)

4D Watcher

Au moment de la rédaction de cette note, 4D US a annoncé la disponibilité de son premier widget dédiée à la surveillance d'informations du monde 4D, tout en promettant d'autres réalisations dans le futur. Félicitations à 4D US, car le widget est fourni aussi bien en version Yahoo que Dashboard.

4D Watcher listant les échanges sur la liste iNug

4D Quiz

Vous trouverez sur www.4dquiz.com un widget Dashboard permettant de rechercher dans la documentation en ligne de 4D.

4D Quiz

Sécurité

Afin de prévenir les intrusions cachées sur le poste de l'utilisateur, le moteur de Yahoo demande confirmation avant d'exécuter un nouveau widget.

Comparaison des deux technologies

Quoique d'un rendu très voisin et qui suscita les polémiques au moment de la sortie de Tiger, les deux technologies diffèrent en réalité notablement dans leur réalisation.

Moteur d'exécution

Dashboard

Les widgets version Dashboard ne s'exécutent que sur Macintosh et seulement à partir de la version dite Tiger d'OS X, soit la 10.4. Ils ne nécessitent alors aucun composant supplémentaire après téléchargement. La technologie qui assure l'exécution des miniapplications se nomme Dashboard. Pour plus d'informations, consultez la liste des ressources à la fin de cette note technique.

Yahoo! Widget Engine

Pour exécuter un widget modèle Yahoo, il faut télécharger et installer l'environnement d'exécution sur chaque poste cible, l'équivalent du 4D Runtime. Cet inconvénient s'accompagne néanmoins d'une bonne nouvelle : l'engine Yahoo est disponible pour OSX ET pour Windows : http://widgets.yahoo.com/

Téléchargement de l'Engine

Sur PC il faut disposer au minimum de Windows 2000 tandis que la compatibilité sur Macintosh démarre avec Panther (version 10.3).

Notez que l'installation Windows pèse plus lourd que sa petite sœur macOS, car les développeurs ont dû intégrer dans la version Windows des fonctions qui existent nativement dans l'OS d'Apple.

Autant de process que de Widgets

Inconvénient des widgets : c'est très joli…mais ça consomme beaucoup de mémoire ! Chaque miniapplication tourne en tant que telle dans son propre process et consomme aisément quelques mégaoctets, plus de la ressource processeur.

Affichage dans le gestionnaire des tâches de Windows

Encore une bonne chose dont il ne faut pas abuser !

Bien entendu, l'avantage recherché c'est l'étanchéité entre miniapplications. Chacune disposant de son propre environnement d'exécution, aucun dysfonctionnement de l'une n'est susceptible de perturber le fonctionnement des autres.

Programmation Yahoo! Widget

La programmation des widgets de Yahoo s'effectue par une combinaison :

• de déclaration XML des composants de l'application : objets d'interface et événements ;

• de code JavaScript pour le contrôle des opérations ;

• d'images, de préférence au format png (Portable Networks Graphics) pour l'aspect habillage.

Une extensibilité est possible au travers d'appels :

• à des applescripts sur OSX ;

• à des objets COM sur Windows.

En outre, il est possible d'effectuer des appels à des commandes Terminal. Cette fonction repose sur les fonctionnalités natives UNIX de l'OS d'Apple et certaines commandes ont été émulées dans l'Engine Windows pour assurer la portabilité.

La prise en main repose sur :

• un tutoriel disponible sur le site de Yahoo ;

• un guide de référence assez spartiate malgré ses 300 pages ;

• les milliers de widgets existants.

En effet, les composants internes d'un widget ne sont pas compilés et sont librement consultables.

Il n'existe pas d'IDE pour programmer les Widgets et il n'est pas toujours évident de mettre en pratique une fonction décrite dans la documentation. Pour tester un widget il suffit à tout moment de cliquer sur son fichier qui se lance alors dans l'environnement d'exécution.

Programmation Dashboard Widget

Pour Tiger, un widget n'est rien d'autre qu'une page HTML utilisant les classiques : CSS2, DOM2, JavaScript, HTML. On peut donc considérer qu'un widget Apple n'est qu'une page HTML présentée dans Dashboard (qui utilise le moteur open source WebCore comme Safari) et non dans un navigateur.

Les widgets Apple sont extensibles :

• au travers de programmation Cocoa ;

• par des appels à des lignes de commandes UNIX ;

• par recours à des plugs-ins Internet (Flash, Quicktime, Java, etc.).

Conséquences appréciables :

• il est possible de tester directement (quoique partiellement) des widgets au sein de Safari et de bénéficier des options de débogage JavaScript ;

• si vous connaissez HTML et les CSS, vous savez programmer un widget, au contraire des widgets Yahoo qui demandent l'appropriation d'un langage déclaratif XML propriétaire.

Bien, je suppose que vous êtes maintenant impatients de voir d'un peu plus près la réalisation d'un exemple communiquant avec une base 4D.

Exemple de réalisation avec 4D

Le principe retenu pour notre exemple de widget consiste à afficher une palette flottante qui envoie une requête à 4D afin de récupérer de l'information présentée dans la palette.

Idées de réalisation : en réfléchissant à l'exemple à retenir, plusieurs idées nous sont venues.

Un exemple loisir avec Juke-Box

Parmi les widgets que j'utilise couramment, deux sont en relation avec iTunes. Je me suis donc fait la main sur la réalisation d'un widget très simple qui affiche le titre du morceau en cours de lecture dans l'application JukeBox de Roland Lannuzel et fournie gratuitement par 4D S.A.

Cet exemple, très basique, peut être affiné de différentes manières :

• en énonçant le morceau à chaque changement grâce à la fonction speak :

var textToSpeak = "Your computer is talking to you. What say you?";
speak(textToSpeak);

• en affichant d'autres informations sur le morceau ;

• en permettant de piloter le Juke-box depuis le widget.

Autres exemples possibles

Impression d'enveloppes

Sur le modèle du Dashboard widget/ Enveloppes, le widget :

• récupère l'adresse de retour du courrier ;
• permet de placer un logo ;
• permet de saisir des premières lettres d'un contact et affiche les coordonnées du contact ;
• offre la possibilité de saisir l'adresse complètement ;
• imprime l'enveloppe.

Transfert de fichiers vers le serveur

Sur le modèle de FTPbeam, un glisser-déposer d'un document vers le widget équivaut à transférer le document vers le serveur 4D qui se charge de le ranger et l'indexer.

Tableau de bord

Le widget présente des informations issues de 4D qui sert d'intégrateur en allant interroger différentes sources de données : comptabilité, gestion, automates…

Moniteur

Le widget liste, à des fins de surveillance, des processus en cours d'accomplissements sur le serveur 4D : par exemple des imports réguliers ou des synchronisations entre sites distants.

Infos4D.widget…

J'ai finalement retenu l'idée de présenter des informations sur l'application 4D en cours d'exécution et sur son environnement. Au travers d'une simple palette flottante, toute information sur son environnement que le langage 4D permet de récupérer devient ainsi consultable depuis n'importe quel ordinateur connecté à Internet.

Pour son aspect multiplateforme, j'ai choisi une réalisation en widget Yahoo.

Le widget Infos4D

L'incontournable HTTP

Pour communiquer, le widget et 4D doivent disposer d'un protocole commun : les widgets reposent sur http pour communiquer avec l'extérieur. Nous trouvons ainsi une nouvelle illustration de l'ouverture extraordinaire que représente la disponibilité d'un serveur http dans 4D lui-même. Malgré sa fréquente restriction au simple usage de serveur Web, le serveur http de 4D constitue un outil de communication interapplications très puissant et reposant sur un standard !

Serveur HTTP de 4D

4D reçoit une requête http (une seule dans cet exemple), traitée dans la méthode base Sur connexion Web.

J'ai généré une base exemple à partir du template 4D de gestion de contacts. Puis, j'ai simplement activé le serveur Web de la base.

Voici le code de la Méthode base Sur connexion Web qui répond à la requête provenant du widget et lui renvoie les informations souhaitées, structurées dans un flux XML :

code 4D : méthode Sur Connexion Web

C_TEXTE($1;$2;$3;$4;$5;$6)
Au cas ou
: ($1="/get_4Dinfos")
C_TEXTE($_vt_refRoot)
$_vt_refRoot:=DOM Creer ref XML("infos4D")

ADM_get_4D_infos ($_vt_refRoot)

C_BLOB($_vx_blobXML)
DOM EXPORTER VERS VARIABLE($_vt_refRoot;$_vx_blobXML)
ENVOYER BLOB HTML($_vx_blobXML;"text/xml")
DOM FERMER XML($_vt_refRoot)
Fin de cas

Ce code est totalement indépendant du client, il ne préjuge pas de son appel par le widget et se contente de générer très simplement une structure XML bien formée.

Nous vérifions que ce code fonctionne correctement en saisissant comme URL dans notre navigateur local :
http://localhost/get_4Dinfos

Le navigateur nous affiche la réponse du serveur Web de la base :

La réponse XML du serveur 4D

Construction de la structure XML

La construction du contenu XML s'effectue dans la méthode ADM_get_4D_infos dont voici le source :

code 4D : méthode ADM_get_4D_infos

` ----------------------------------------------------
` Nom utilisateur (OS) : christophe Keromen
` Date et heure : 28/02/06, 11:42:26
` ----------------------------------------------------
` Methode : ADM_get_4D_infos
` Description :
` récupérer des infos sur 4D, l'appli en cours d'exécution et son environnement
`
` Parametres :
` $1:TEXTE:Ref Element racine
`
` Version : 1
` Appel : ADM_get_4D_infos (Ref Element racine )
` ----------------------------------------------------

C_TEXTE($1;$_vt_refRoot)
$_vt_refRoot:=$1

C_BOOLEEN($_vb_siAutonome)
$_vb_siAutonome:=Vrai
C_BOOLEEN($_vb_siIndentation)
$_vb_siIndentation:=Vrai

DOM ECRIRE OPTIONS XML($_vt_refRoot;"UTF-8";$_vb_siAutonome;$_vb_siIndentation)

Si (Vrai) ` infos statiques
C_TEXTE($_vt_refStatiques)
$_vt_refStatiques:=DOM Creer element XML($_vt_refRoot;"infos_statiques")

C_TEXTE($_vt_versionApplication)
$_vt_versionApplication:=Version application(*) `Exemple : la chaine "B0120602" représente
` une version beta 12 de la version 6.0.2
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refStatiques;"version_application";$_vt_versionApplication)

C_TEXTE($_vt_fichierApplication)
$_vt_fichierApplication:=Fichier application
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refStatiques;"fichier_application";$_vt_fichierApplication)

C_TEXTE($_vt_siCompile)
$_vt_siCompile:="oui"*Num(Application compilee)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refStatiques;"appli_compilee";$_vt_siCompile)

C_TEXTE($_vt_fichierStructure)
$_vt_fichierStructure:=Fichier structure
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refStatiques;"structure";$_vt_fichierStructure)
Fin de si

Si (Vrai) ` Fichier de données
C_TEXTE($_vt_refData)
$_vt_refData:=DOM Creer element XML($_vt_refRoot;"data")

C_TEXTE($_vt_fichierDonnees)
$_vt_fichierDonnees:=Fichier donnees
DOM_AJOUTER_ELEMENT_SIMPLE($_vt_refData;"fichiers";$_vt_fichierDonnees)

C_TEXTE($_vt_fichierDonneesVerrouille)
$_vt_fichierDonneesVerrouille:=("oui"*Num(Fichier donnees verrouille))+("non"*Num(Non(Fichier donnees verrouille)))
DOM_AJOUTER_ELEMENT_SIMPLE($_vt_refData;"data_verrouillees";$_vt_fichierDonneesVerrouille)

TABLEAU ALPHA(255;$_taNomSegment;0)
LISTE SEGMENTS DE DONNEES($_taNomSegment)

C_TEXTE($_vt_nbreSegments)
$_vt_nbreSegments:=Chaine(Taille tableau($_taNomSegment))
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refData;"nbreSegments";$_vt_nbreSegments)
Fin de si

Si (Vrai) ` serialisation et licence
C_TEXTE($_vt_refSerial)
$_vt_refSerial:=DOM Creer element XML($_vt_refRoot;"infos_serialisation")

LIRE INFORMATIONS SERIALISATION($_vl_cle;$_vt_utilisateur;$_vt_société;$_vl_nbreConnexions;$_vl_nbreConnexionsMax)

DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refSerial;"utilisateur";$_vt_utilisateur)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refSerial;"societe";$_vt_société)
DOM_AJOUTER_ELEMENT_SIMPLE($_vt_refSerial;"nbreConnexions";Chaine($_vl_nbreConnexions))
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refSerial;"nbreConnexionsMax";Chaine($_vl_nbreConnexionsMax))

C_TEXTE($_vt_licenceDisponible)
Si (Licence disponible(Licence 4D Write ))
$_vt_licenceDisponible:="oui"
Sinon
$_vt_licenceDisponible:="Non"
Fin de si

$_vt_refElement:=DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refSerial;"licenceWrite";$_vt_licenceDisponible)
Fin de si

Si (Vrai) ` informations dynamiques
C_REEL($_vn_totalMemory;$_vn_PhysicallMemory)
C_ENTIER LONG($_vl_FreeMemory;$_vl_process)
AP AVAILABLE MEMORY ($_vn_totalMemory;$_vn_PhysicallMemory;$_vl_FreeMemory;$_vl_process)

C_TEXTE($_vt_refStatiques)
$_vt_refStatiques:=DOM Creer element XML($_vt_refRoot;"infos_dynamiques")
DOM_AJOUTER_ELEMENT_SIMPLE($_vt_refStatiques;"totalMemory";Chaine($_vn_totalMemory/(1024*1024);"### ### M"))
DOM_AJOUTER_ELEMENT_SIMPLE($_vt_refStatiques;"physicalMemory";Chaine($_vn_PhysicallMemory/(1024*1024);"### ### M"))
DOM_AJOUTER_ELEMENT_SIMPLE($_vt_refStatiques;"freeMemory";Chaine($_vl_FreeMemory/(1024*1024);"### ### M"))
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refStatiques;"processStack";Chaine($_vl_process/1024;"### ### k")+(1000*"-"))
Fin de si

Note importante

Attention, le moteur d'analyse XML (parseur) du widget n'aime pas les éléments vides. Plus exactement, il faut vérifier qu'un élément porte une valeur textuelle avant d'essayer de la lire. Pour ne pas alourdir le code JavaScript, nous n'avons pas effectué ces contrôles dans le widget de démo. C'est donc du côté 4D, qu'il faut vérifier que chaque élément XML contient bien une valeur.

La méthode utilitaire DOM_AJOUTER_ELEMENT_SIMPLE qui permet d'ajouter directement un élément simple et sa valeur au parent courant est la suivante :

code 4D : méthode DOM_AJOUTER_ELEMENT_SIMPLE

` ----------------------------------------------------
` Nom utilisateur (OS) : christophe Keromen
` Date et heure : 23/09/04, 07:04:31
` ----------------------------------------------------
` Methode : DOM_AJOUTER_ELEMENT_SIMPLE
` Description :
` Ajout d'un element simple et de sa valeur
`
` Parametres :
` $1:TEXTE:Ref Element Parent
` $2:TEXTE:Nom Element
` $3:TEXTE:Valeur Element
`
` Version : 1
` Appel : DOM_AJOUTER_ELEMENT_SIMPLE(Ref Element Parent;Nom Element;Valeur Element )
` ----------------------------------------------------

C_TEXTE($1)
C_TEXTE($_vt_RefParent)
$_vt_RefParent:=$1

C_TEXTE($2)
C_TEXTE($_vt_NomElement)
$_vt_NomElement:=$2

C_TEXTE($3)
C_TEXTE($_vt_ValeurElement)
$_vt_ValeurElement:=$3

C_TEXTE($_vt_RefElement)
`Ajout d'un champ et de sa valeur
$_vt_RefElement:=DOM Creer element XML($_vt_RefParent;$_vt_NomElement)

DOM ECRIRE VALEUR ELEMENT XML($_vt_RefElement;$_vt_ValeurElement)

Les commandes 4D appelées pour récupérer de l'information ne présentent pas de difficultés. Notez que nous utilisons également 4D Pack pour obtenir des informations concernant la mémoire.

D'autres possibilités de récupération d'informations intéressantes existent, mais n'ont pas été mises en œuvre dans cet exemple. Nous les avons indiquées pour mémoire à la fin de la méthode encadrées par des Si(Faux) :

code 4D

Si (Faux) `plug-ins
`Licence 4D Draw Entier long 808464694
`Licence 4D for OCI Entier long 808465208
`Licence 4D View Entier long 808465207
`Licence 4D Web Entier long 808464945
`Licence 4D Write Entier long 808464697
`Licence Web 4D Client Entier long 808465209
`Licence SOAP 4D Client Entier long 808465465
`Licence 4D SOAP Entier long 808465464
`Licence 4D ODBC Pro Entier long 808464946
`Licence 4D for ADO Entier long 808465714
`Licence 4D for MySQL Entier long 808465712
`Licence 4D for PostgreSQL Entier long 808465713
`Licence 4D for Sybase
TABLEAU ENTIER LONG($_Tl_tabNumeros;0)
TABLEAU TEXTE($_Tt_tabNoms;0)
LIRE LISTE PLUGIN($_Tl_tabNumeros;$_Tt_tabNoms)
Fin de si

Si (Faux) `environnement systeme
C_ENTIER LONG($vlPlatform;$vlSystem;$vlMachine;$vlLangue)
PROPRIETES PLATE FORME($vlPlatform;$vlSystem;$vlMachine;$vlLangue)
C_TEXTE($_vt_nomMachine;$_vt_nomPossesseur)
$_vt_nomMachine:=Nom de la machine
$_vt_nomPossesseur:=Nom du possesseur
Fin de si

Si (Faux) `informations sur les data
`Commandes et sections pour Définition structure
`Enregistrements dans table
`Lire parametre base ({table; }sélecteur)
`Taille cache données
`Timeout 4D Server Entier long 13
`Timeout 4D Client
`Mode écriture cache
`Numéro automatique table
Fin de si

Réalisation du widget infos4D

Intéressons-nous maintenant à la réalisation de la partie cliente, le widget. Outre l'aspect interface que nous verrons plus loin, comment s'effectue la communication http avec 4D ?

URL.fetch()

Depuis l'origine, les widgets disposent d'une fonction JavaScript URL.fetch() qui permet de récupérer de l'information depuis un serveur http. La méthode utilisée est GET ou POST et si le serveur renvoie du XML, celui-ci doit être analysé « manuellement ».

XMLHTTPRequest

Depuis la version 3 de l'Engine, un nouvel objet de communication est proposé : XMLHTTPRequest.

Derrière ce nom un peu barbare se cache le fondement d'une des évolutions les plus notables du Web actuel.

Initialement proposé par Microsoft pour Internet Explorer, cet objet est maintenant également supporté par Safari et Firefox. Il permet au code JavaScript d'une page HTML d'échanger de l'information avec le serveur Web SANS provoquer le rafraîchissement complet de la page. Couplé avec le Dynamic HTML (modification du contenu de la page au moyen de l'objet DOM piloté par JavaScript), il s'agit du fameux Ajax dont la presse informatique fait grand cas et qui représente l'un des piliers de la fameuse évolution vers le Web version 2. Cette approche fait l'objet d'une note technique dédiée.

Avantages de cet objet :
• son API est commune avec celles des autres implémentations (celles des navigateurs) ;
• il charge directement les informations retenues dans un arbre DOM qu'il suffit d'interroger pour récupérer les données à afficher.

Le code JavaScript de communication

Voici un exemple de fonction JavaScript se chargeant de la communication avec 4D :

function get4Dinfos()
{
var req = new XMLHttpRequest();
req.open( "POST", "http://192.168.1.13/get_4Dinfos", false );
req.send();
if ( req.status == 200 )
{
try
{
var docXML=req.responseXML;
//print( docXML.toXML() );
doParse(docXML);
}
catch(e)
{
print("An error occurred: " + e);
}
}
else{
razControls();
}
}

Commentaires

var req = new XMLHttpRequest();
C'est la ligne qui crée en mémoire un nouvel objet XMLHttpRequest, référencé dans la variable req.

req.open()
La méthode open de l'objet XMLHttpRequest est invoquée pour ouvrir une communication avec l'URL souhaitée en utilisant la méthode POST en mode synchrone (le code attend la réponse avant de poursuivre).

req.send()
La requête HTTP vers l'URL est réellement envoyée et la réponse est reçue et analysée

req.status
Cette propriété de l'objet reçoit le code statut de la requête http, 200 indique que tout s'est bien déroulé

req.responseXML
Cette propriété de l'objet contient l'arbre DOM correspondant à l'analyse du contenu XML de la réponse.

Notes :
• La ligne passée en commentaire :
print( docXML.toXML() );
permet lorsque le commentaire est retiré d'envoyer vers la console de débogage le contenu de l'arbre DOM.
Nous y reviendrons plus loin.
• Sur MacOSX, pour que ce code fonctionne il faut que la requête POST envoie réellement des données. Nous modifierons donc le code en conséquence afin d'envoyer une structure XML minimale, ne contenant qu'un élément racine <root>. Se reporter plus loin au listing de get4Dinfos pour cela.

Structure du widget infos4D

Un widget est déclaré par une structure XML comprenant des éléments dotés de propriétés.

Voici la structure principale de l'exemple :

La racine est l'élément widget, son attribut minimumVersion précise qu'il faut disposer au minimum de la version 3.0 de l'Engine pour utiliser le widget, car c'est la version où a été introduit l'objet XMLHTTPRequest.

L'objet about-box permet de définir une ou plusieurs images qui serviront à l'affichage d'un à propos.

L'objet window contient par défaut les autres composants d'interface. En dehors d'un certain nombre de sous-éléments (title, height, width, visible, opacity, level, shadow) qui permettent de préciser certaines propriétés, on peut constater que window comprend plusieurs autres éléments correspondant à des déclarations d'objets d'interface :

• deux images : l'une servant pour le fond, l'autre pour afficher un bouton (« Refresh » afin de renvoyer une requête vers 4D pour rafraîchir les informations) ;
• quatre éléments text qui afficheront de l'information relue depuis l'arbre DOM ;
• un élément textarea sur lequel nous reviendrons dans un second temps.

Au même niveau que l'élément window (en « sibling » pour reprendre la terminologie adéquate), nous trouvons :

• deux éléments preference qui indiquent les préférences par défaut que le widget offre en modification ;
• deux éléments action qui décrivent les actions à effectuer lorsque l'événement indiqué comme valeur de l'attribut « trigger » est déclenché. Nous déclarons ainsi deux gestionnaires d'événements, l'un sur chargement du widget (onLoad), l'autre sur modification des préférences par l'utilisateur (onPreferencesChanged).

Les champs Text

Les quatre premières lignes d'information sont affichées par des champs texte.

Voici un exemple de déclaration :

<text>
<name>version_4D</name>
<color>#FEFEFE</color>
<size>13</size>
<alignment>left</alignment>
<vOffset>60</vOffset>
<hOffset>30</hOffset>
</text>

Le sous-élément name identifie de manière unique un objet dans la structure et servira d'identifiant pour manipuler l'objet par code JavaScript.

Les autres sous-éléments décrivent des propriétés de l'objet : couleur, taille, alignement, position horizontale et verticale.

Il est également possible de déclarer un objet Text directement par programmation :

newObjectName = new Text()

Déclaration du bouton

Voici la déclaration du bouton :

<image src="Resources/btn_GO.png">
<name>buttonGo</name>
<vOffset>450</vOffset>
<hOffset>430</hOffset>
<onMouseUp>
get4Dinfos();
</onMouseUp>
</image>

On y retrouve des propriétés identiques à celles du Text (name, vOffset, hOffset…).

L'apparence du bouton est décrite par une image dont le chemin d'accès est précisé dans l'attribut « src ».

L'action effectuée par le bouton est définie en déclarant un gestionnaire d'événement get4Dinfos(), fonction JavaScript qui sera appelée sur l'événement onMouseUp, c'est-à-dire sur clic souris.

Les balises d'actions

La description du comportement du widget s'effectue au travers de balises <action>. Voici le code de l'élément action correspond à l'événement onLoad, c'est-à-dire sur chargement du widget :

<action trigger="onLoad">
include("infos4D.js");
version_4D.data ="chargement..."
get4Dinfos()
updateBehavior();
</action>

Tout d'abord, la commande include permet d'inclure un fichier externe. Dans notre exemple, ce fichier « infos4D.js » contiendra toutes les fonctions JavaScript nécessaires au fonctionnement du widget.

Ensuite nous affectons la chaine « chargement… » à la propriété data de l'objet Text version_4D.

Puis nous appelons successivement les fonctions JavaScript contenues dans le fichier externe : get4Dinfos() et updateBehavior();.

Les fonctions JavaScript

Par le mécanisme associant déclaration de gestionnaire d'événements et possibilité de définir le code JavaScript dans un ou plusieurs fichiers externes, nous obtenons une séparation des couches satisfaisantes.

Voici le code de la fonction updateBehavior() qui applique des propriétés de mise en forme sur chargement et aussi après modification des préférences par l'utilisateur :

function updateBehavior()
{
version_4D.font = preferences.textFontPref.value;
version_4D.color = preferences.textColorPref.value;

fichier_appli.font = preferences.textFontPref.value;
fichier_appli.color = preferences.textColorPref.value;

appli_compil.font = preferences.textFontPref.value;
appli_compil.color = preferences.textColorPref.value;

structure.font = preferences.textFontPref.value;
structure.color = preferences.textColorPref.value;

myTextArea.font= preferences.textFontPref.value;
myTextArea.color = preferences.textColorPref.value;
}

L'objet preferences est automatiquement géré par le widget Engine.

Enfin le code de la fonction get4Dinfos() qui gère la communication entre le widget et 4D :

function get4Dinfos()
//pour le Mac
var docXML;
docXML=XMLDOM.createDocument();
root=docXML.createElement("root");
docXML.appendChild(root);
//pour le Mac (fin)
var req = new XMLHttpRequest();
print (req);
req.open( "POST", "http://192.168.1.162/get_4Dinfos", false );
req.send(docXML);
if ( req.status == 200 )
{
try
{
var docXML=req.responseXML;
//print( docXML.toXML() );
doParse(docXML);
}
catch(e)
{
print("An error occurred: " + e);
}
}
else{
print('req.status : '+req.status);
razControls();
}

Dans un premier bloc, nous retrouvons l'objet XMLHTTPRequest présenté plus haut :

var req = new XMLHttpRequest();
req.open( "POST", "http://localhost/get_4Dinfos", false );
req.send(docXML);

Puis en cas de succès (req.status == 200), nous chargeons le résultat dans un arbre DOM var docXML=req.responseXML; : nous appelons alors une fonction JavaScript qui analyse la structure XML et affecte les valeurs aux objets d'interface : doParse(docXML);.

La méthode d'analyse du XML : doParse

Voici pour la première partie de cette fonction :

// les champs TEXT
version_4D.data="version 4D : "+docXML.evaluate( "string(infos4D/infos_statiques/version_application)") ;
fichier_appli.data="appli : "+docXML.evaluate( "string(infos4D/infos_statiques/fichier_application)") ;
appli_compil.data="application compilée : "+docXML.evaluate( "string(infos4D/infos_statiques/appli_compilee)") ;
structure.data="structure : "+docXML.evaluate( "string(infos4D/infos_statiques/structure)") ;

La propriété data des objets Text est renseignée d'après la valeur relue dans un élément par le code :
docXML.evaluate(« string(infos4D/infos_statiques/version_application) »).

La fonction docXML.evaluate() évalue une expression XPath (la désignation d'un élément XML dans la hiérarchie par une expression de chemin) pour retrouver un élément précis et la fonction string() convertit la valeur de cet élément en chaine.

Les balises de préférences

Nous retrouvons le même mécanisme déclaratif pour décrire les deux préférences que le widget offre en modification à l'utilisateur : la couleur du texte et la police d'affichage :

<preference>
<name>textColorPref</name>
<title>Text Color:</title>
<type>color</type>
<defaultValue>#FEFEFE</defaultValue>
<description>Sélectionner la couleur du texte.</description>
</preference>

Voici le dialogue de préférences correspondant :

Dialogue de préférences

Insistons sur le fait que ce dialogue s'obtient simplement en deux déclarations ! Seules deux préférences sont proposées par défaut :

• le niveau de la fenêtre, c'est-à-dire le comportement de la fenêtre par rapport aux autres fenêtres : Flottante, Avant-plan, Normale, Arrière-plan, Affichage à tête-haute uniquement (expression étrange désignant un mode n'affichant que les widgets) ;
• l'opacité de la fenêtre (qui permet de fixer sous forme de pourcentage la transparence).

Préférences par défaut

Plus de généricité avec une TextArea

Lourdeur de l'approche déclarative

Définir, comme nous l'avons fait jusque-là, chaque information par la déclaration d'un objet Text est long et peu générique. Une première amélioration passerait par une déclaration par programmation d'un objet Text associé à un élément de la structure XML.

Une autre possibilité consiste à concaténer les informations relues depuis la structure XML dans un autre type d'objet, la TextArea, qui permet le multiligne.

Déclaration du nouvel objet

On utilise l'élément <textarea> :

<textarea>
<name>myTextArea</name>
<color>#FEFEFE</color>
<size>13</size>
<alignment>left</alignment>
<vOffset>120</vOffset>
<hOffset>30</hOffset>
<width>350</width>
<height>350</height>
<editable>false</editable>
</textarea>

Notez l'élément <editable> qui permet de déclarer comme saisissable ou non le contenu de la zone.

Remplissage par programmation

Nous avons, côté 4D, regroupé les informations par thème :

code 4D

Si (Vrai) ` Fichier de données
C_TEXTE($_vt_refData)
$_vt_refData:=DOM Creer element XML($_vt_refRoot;"data")

C_TEXTE($_vt_fichierDonnees)
$_vt_fichierDonnees:=Fichier donnees
$_vt_refElement:=DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refData;"fichiers";$_vt_fichierDonnees)

C_TEXTE($_vt_fichierDonneesVerrouille)
$_vt_fichierDonneesVerrouille:=("oui"*Num(Fichier donnees verrouille))+("non"*Num(Non(Fichier donnees verrouille)))
$_vt_refElement:=DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refData;"data_verrouillees";
$_vt_fichierDonneesVerrouille)

TABLEAU ALPHA(255;$_taNomSegment;0)
LISTE SEGMENTS DE DONNEES($_taNomSegment)
C_TEXTE($_vt_nbreSegments)
$_vt_nbreSegments:=Chaine(Taille tableau($_taNomSegment))
$_vt_refElement:=DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_refData;"nbreSegments";
$_vt_nbreSegments)
Fin de si

Voici la structure XML correspondante :

<data>
<fichiers>
D:\Server_DEV\4D_NT_CK\_en cours\contacts\contacts.4DD
</fichiers>
<data_verrouillees>non</data_verrouillees>
<nbreSegments>1</nbreSegments>
</data>

Nous dédions une portion de code JavaScript à l'exploitation d'une telle structure. Il ne suffit plus de récupérer la valeur d'un élément XML désigné par un chemin XPath comme précédemment, nous devons recourir à une navigation DOM depuis l'élément parent <data> :

// DATA
myTextArea.data="\nData\n";
var myitem=docXML.evaluate("infos4D/data");
var firstItem=myitem.item(0);
for(var i = 0; i < firstItem.childNodes.length; i++)
{
var zeChild=firstItem.childNodes.item(i).firstChild;
myTextArea.data+="* "+firstItem.childNodes.item(i).tagName+" : "+zeChild.data+"\n";
}

var firstItem=myitem.item(0); : référence le premier enfant de infos4D/data, la numérotation commence à 0

La collection des enfants est contenue dans firstItem.childNodes et le nombre d'éléments s'obtient par la propriété length de cette collection. Nous bouclons sur chacun des éléments pour en extraire :

• le nom de l'élément : firstItem.childNodes.item(i).tagName
• la valeur de l'élément : firstItem.childNodes.item(i).firstChild.data

La notion importante est que dans DOM la valeur d'un élément est un nœud de type texte porté par le premier enfant de l'élément lui-même. À l'instar d'autres bibliothèques de code (en Java par exemple), cette subtilité est masquée dans 4D qui ne considère que l'élément et masque la notion de nœud.

Nous trouverons dans le code JavaScript, autant de portions de code équivalentes que de regroupement d'informations. Bien entendu, il serait souhaitable de retravailler ce code pour en faire une fonction paramétrable.

Le résultat final

Le widget sous Windows avec une barre de défilement.

Notez pour l'objet TextArea :

• la barre de défilement verticale, gérée automatiquement ;
• le retour à la ligne automatique dans la zone.

C'est pour mettre en évidence ces automatismes que nous avons artificiellement rajouté des tirets après la dernière information.

Finalisation

La majeure partie du travail est effectuée. Reste à aborder deux aspects :

• le mode débogage ;
• le « paquetage », c'est-à-dire l'étape finale pour bâtir un widget exploitable par l'utilisateur final.

Désactivation du mode debug

Juste sous la déclaration du widget, se trouve l'élément debug qui indique si, lors de l'exécution, la console doit s'afficher ou non :

<debug>on</debug>

Console de Yahoo ! Widget Engine

Les valeurs possibles pour l'élément sont : errors, on, off, verbose. Se reporter au manuel de référence pour leur signification.

Sans recourir à cet élément, le raccourci-clavier « control et touche majuscule » lors de la sélection du menu du Widget Engine permet de faire apparaître une option « Debug mode » dans le menu. Le fait de cocher cette option provoquera l'affichage de la console pour tous les widgets lancés par la suite.

Les actions proposées sous forme de boutons se résument à pouvoir recharger le widget et le fermer.

La zone supérieure de la fenêtre permet d'afficher des informations par programmation pour suivre l'exécution du programme, par exemple :

print( docXML.toXML() );

affiche le contenu du flux XML contenu dans docXML. La commande log() peut également être utilisée. Les erreurs rencontrées sont automatiquement reportées dans cette zone ainsi que les chargements, comme ici celui du fichier JavaScript « infos4D.js ».

Possibilités de débogage

Il est aussi possible de provoquer des actions par saisie de commandes dans la zone « Evaluate ». Voici ce qu'indique l'aide en ligne :

Widget Engine Debugging Commands:
/dump object       print all the properties of 'object'
/trace function       print a message every time 'function' is called
/untrace function       stop tracing 'function'
/watch object.property       display a message when the given 'property' of 'object' changes
(arbitrary JavaScript)       execute the given JavaScript
/help       this list of commands

En saisissant par exemple /trace doParse, puis en cliquant sur le bouton « Refresh » du widget, on obtient l'affichage suivant :

Tracepoint set
03/11/06 09:40:03.625: Traced function called: doParse()

La commande /dump est intéressante, car elle permet d'obtenir toutes les propriétés d'un objet ainsi que les valeurs courantes de ces propriétés.
Par exemple pour /dump myTextArea :

Object « myTextArea »:

myTextArea[color]: #FEFEFE
myTextArea[colour]: #FEFEFE
myTextArea[scrollbar]: true
myTextArea[data]:
Data
* fichiers : D:\Server_DEV\4D_NT_CK\_en cours\contacts\contacts.4DD
(…)
myTextArea[font]: Arial
myTextArea[name]: myTextArea
myTextArea[hOffset]: 30
myTextArea[vOffset]: 120
myTextArea[height]: 350
myTextArea[width]: 350
myTextArea[zOrder]: 7
myTextArea[opacity]: 255
myTextArea[alignment]: left
myTextArea[onMouseDown]: null
myTextArea[onMouseUp]: null
myTextArea[onMouseEnter]: null
myTextArea[onMouseExit]: null
myTextArea[onKeyDown]: null
myTextArea[onKeyUp]: null
myTextArea[onKeyPress]: null
myTextArea[onDragEnter]: null
myTextArea[onDragExit]: null
myTextArea[onMouseMove]: null
myTextArea[onDragDrop]: null
myTextArea[text]:
(…)
myTextArea[tooltip]:
myTextArea[size]: 13
myTextArea[style]: none
myTextArea[bgOpacity]: 0
myTextArea[bgColor]: #FFFFFF
myTextArea[bgColour]: #FFFFFF
myTextArea[lines]: true
myTextArea[columns]: true
myTextArea[editable]: false
myTextArea[spellcheck]: true
myTextArea[hAlign]: left
myTextArea[vAlign]: top
myTextArea[contextMenuItems]:
myTextArea[window]: [object Window]
myTextArea[onMultiClick]: null
myTextArea[onContextMenu]: null
myTextArea[secure]: false
myTextArea[visible]: 1
myTextArea[superview]: [object Root]
myTextArea[onLoseFocus]: null
myTextArea[onGainFocus]: null
myTextArea[thumbColor]: null

Vous pouvez ainsi constater que nous n'avons fait qu'effleurer le sujet des propriétés des objets ! Cependant, il n'existe pas de mode trace comme on peut en trouver dans 4D.

Packaging

La structure du Widget doit être la suivante : un dossier « Contents » comprenant les différents éléments du widget. Ce dossier widget étant lui-même contenu dans un dossier portant le nom du widget suivi de l'extension .widget.

Organisation du widget

L'étape finale de réalisation du paquetage diffère suivant la plateforme. Sur Macintosh, l'opération est terminée ! Le widget se présente comme un paquet affichant l'icône caractéristique des widgets. Pour obtenir maintenant son contenu, il faudra sélectionner l'option « Afficher le contenu du paquet » dans le menu contextuel obtenu par ctrl-clic sur l'élément dans le Finder.

Sur Windows en revanche, il faut recourir au glisser-déposer sur un widget fourni en libre téléchargement sur le site de Yahoo : Widget_Converter. Le widget ainsi obtenu est une archive au format zip. Remplacez l'extension .widget par .zip et vous pourrez consulter le contenu de tout widget.

Ressources

Voici une liste de ressources complémentaires pour votre voyage au pays des widgets. Bonne route…

Yahoo! Widgets

• Le site officiel

• Le forum konfabulator compte plus de 13 000 membres.

• Un wiki très intéressant et non dépendant de Yahoo est à consulter ici

• Article dans le n° 81 de « Programmez ! »

Dashboard pour Tiger

• Site officiel

• Un très bon article sur les différences de fond entre entre Dashboard et Konfabulator

• Developing Dashboard Widgets: Quick and easy miniapplications

• Tutorial « Build a Dashboard Widget »

• Traduction en français

• Dashboard Programming Guide

• Dashboard Reference

• Des exemples de widgets sur la page Dashboard Sample Code

• Dossier dans le n° 13 de « Vous et votre Mac »

Les ressources complémentaires conseillées par O'Reilly

• JavaScript: The Definitive Guide, 4th Edition by David Flanagan (O'Reilly Media, 2001)

• Cascading Style Sheets: The Definitive Guide, 2d Edition by Eric Meyer (O'Reilly Media, 2004)

• Dynamic HTML: The Definitive Reference, 2d Edition by Danny Goodman (O'Reilly Media, 2002)

Base exemple

Télécharger la base exemple.