La directive #define
Si votre page est un outline, y compris lorsque cet outline est inclus dans une autre page, celle-ci peut avoir un glossaire "privé". C'est possible grâce à la directive #define.
Il est plus simple de donner un exemple pour expliquer comment elle fonctionne. Supposons qu'au début de votre outline vous ayez les lignes suivantes :
#define "héraclite"
À l'origine, le monde le plus beau
est un tas d'ordures répandu au hasard.
Ainsi, dans votre outline, à chaque fois qu'il y aura "{héraclite}" (sans les guillemets), ce qui ressemble à l'appel d'une macro, la phrase "À l'origine, le monde le plus beau est un tas d'ordures répandu au hasard." lui sera substitué.
Ce que #define évalue concerne tout ce qui ce qui est subordonné à la ligne de définition. Cela peut concerner autant de lignes que vous le désirez, qui seront rendues ensemble dans une seule chaîne de caractères sans aucun encodage HTML.
(Autrement dit, les indentations peuvent être significatives dans un outline, selon le renderer que vous utilisez, mais les éléments qui se trouvent dans un #define ne sont pas considérés comme ayant des indentations.)
Bien sûr la partie principale de l'outline comporte des indentations, et celles-ci sont prises en compte lors de la génération du HTML (en fonction du renderer). Par conséquent, si vous utilisez le renderer par défaut -- qui met les codes appropriés <li> devant chaque ligne -- une ligne comportant
{héraclite}
sera rendue avec des disques au niveau approprié avec un seul disque, toute la phrase constituant un seul article de liste.
Ça peut être très utile pour contourner les restrictions imposése par les outlines et leurs renderers. Par exemple, nous rendons notre outline avec le rendeder par défaut et nous désirons avoir des éléments qui auront une grande longueur, disons 600 caractères.
Toutefois, les outlines de Frontier sont limitées à 255 caractères. Qu'allons-nous faire ? Si nous séparons notre texte en plusieurs lignes d'outline, Frontier rendra cela avec des balles pour chaque ligne comme s'il s'agissait d'éléments différents.
D'accord, vous pourriez écrire votre propre renderer. Mais pour contourner ce problème, c'est tout de même plus simple d'utiliser le mécanisme du #define.
Un truc similaire vous permet d'utiliser le renderer par défaut et d'avoir tout de même une ligne avec balles comportant plusieurs paragraphes. (Voyez-vous comment faire cela ? Trouver la solution à ce problème est laissé comme exercice au lecteur.)
Faire circuler l'information de haut en bas dans la hiérarchie
Bien qu'un appel à #define ne puisse apparaître que dans un outline, on peut y faire référence depuis n'importe quel autre type de document. Mais oui, vous pouvez utiliser un appel à #define pour envoyer de l'information d'un document à un autre !
Par exemple, supposons que votre modèle ("template") soit un outline. Cet outline pourra donc contenir une directive #define. Si ce qui y est défini est appelé "héraclite", alors un appel {héraclite} fonctionnera dans n'importe quelle page qui utilisera ce modèle, même si cette page n'est pas un outline.
Ainsi nous avons utilisé un #define pour faire circuler l'information vers le bas de la hiérarchie.
En général et en pratique, l'usage le plus courant est de tirer parti du mécanisme de #define pour faire circuler l'information vers le haut de la hiérarchie. Ce mécanisme est souvent utilisé pour transmettre à un modèle un élément d'information qui changera pour chaque page qui sera rendue avec ce modèle.
Supposons par exemple que chaque page ait un titre qui soit différent de celui du titre de la fenêtre (qui est défini par #title), et que ce titre doive être formaté dans un lettrage de grande dimension et de couleur rouge au début de chaque page, c'est-à-dire avant le{bodytext}.
Puisque ce titre doit apparaître dans chaque page, et puisque il est à l'extérieur du {bodytext}, cela tombe sous le sens d'en confier l'insertion au modèle. De cette façon, si plus tard nous voulons diminuer la taille et la couleur des caractères, nous n'aurons qu'à changer le HTML dans le modèle et rendre une nouvelle fois nos pages.
Nous aurons donc une ligne de texte dans le modèle qui sera juste avant le <bodytexte> et qui ressemblera à ceci :
<center>
<h1><font size="7" color="#cc9900">{monTitre}</font></h1>
</center>
Puis dans chaque page (ou outline) qui sera rendue avec ce modèle, nous aurons la ligne de texte suivante :
#define "monTitre"
à laquelle sera subordonnée une ligne avec le titre désiré (sans les guillemets). Par exemple cela pourrait être :
#define "myTitle"
Ceci est un titre super.
Lors du rendu de chaque page, l'appel {monTitre} dans le modèle sera remplacé par la valeur définie dans le corps de la page. Nous avons fait circuler la valeur vers le haut, depuis la page jusqu'au modèle qui la contient. Essayez cela avec la pageQuatre si vous en avez envie.
Directives personnalisées
À présent vous devez vous dire : "Mais le mécanisme du #define ne fonctionne que pour les outlines ! Qu'arrivera t'-il si je veux utiliser cette méthode pour mes pages qui ne sont pas des outlines ?". La solution est d'utiliser une directive que vous allez définir vous-même.
En effet, vous êtes toujours libre de définir vos propres directives. Si vous avez une ligne de texte dans votre page qui ressemble à ceci :
#monTitre "Ceci est un titre super"
Frontier ne se plaindra pas qu'il ne connait aucune directive "#monTitre". Au contraire, il fera avec "#monTitre" ce qu'il fait avec chaque directive.
Mais que fait Frontier avec les directives ? Au moment où il se prépare à rendre une page, il collecte les directives incluses dans la table du site et dans la page elle-même, puis les stocke dans une table spéciale appelée html.data.adrPageTable.
Donc, dans notre exemple, Frontier va simplement créer une entrée dans websites.["#data"] appelée "monTitre" (le nom de la table doit être entre crochets droits et guillemets car le signe "#" est illégal dans le cas d'une adresse). Puis Frontier donnera à cette entrée la valeur que vous désiriez qu'elle ait, c'est-à-dire dans notre exemple "Ceci est un titre super".
La seule différence entre cette directive et celle qui sont prédéfinies est que Frontier n'est pas paramétré pour faire quelque chose avec cette information, cette tâche vous revient.
Un des moyens d'utiliser la valeur d'une directive personnelle, c'est de l'appeler avec la même syntaxe, du genre macro, que nous avons utilisée avec #define. Dans ce cas, toute occurrence de {monTitre} dans votre page sera remplacée par "Ceci est un super titre" (sans les guillemets).
Pourquoi cela fonctionne-t'il ? Parce que (prenez une grande respiration, maintenant) un appel de macro est simplement une expression en UserTalk ; une expression en UserTalk peut être le nom de n'importe quelle entrée de la base de données ; et n'importe quelle entrée de la table websites.["#data"] est automatiquement accessible à Frontier lorsqu'elle est référencée dans un appel de macro (nous en avons parlé dans la section "Includes et macros", vous en souvenez-vous ?). Donc Frontier trouve "monTitre" comme entrée de websites.["#data"], et lui substitue sa valeur qui est "Ceci est un titre super".
Il serait donc aussi possible de dire {html.data.adrPageTable^.myTitle} -- mais pourquoi se donner cette peine ?
Et #define fonctionne de la même façon. Il crée dans websites.["#data"] une entrée qui sera disponible, au moment où la page sera rendue, pour tous les appels de macros vers le haut ou le bas de la hiérarchie.
En fait, vous pourriez utilisez une directive personnalisée dans votre outline au lieu de #define si ce n'est que puisque les outlines sont limités à 255 caractères, vous ne pourrez définir de cette façon une chaîne de caractères plus longue.
Directives personnalisées et balises HTML
Je vous vois très bien en ce moment vous demander comment vous allez utiliser les directives personnalisées pour vous aider à générer du HTML. Mais attention ! les balises HTML (les éléments entre les crochets) et les directives sont protégées du traitement des macros, vous vous en souvenez ? Vous devez donc être futé pour obtenir le HTML que vous voulez.
Un lecteur m'a écrit pour me décrire ce genre de problème. Chacune de ses pages Web est entièrement constituée d'un tableau, dont certaines cellules, en haut et en bas, sont communes à toutes les pages. Le début et la fin de chaque tableau sont donc générés par le modèle, et ce n'est que la partie centrale des tableaux qui est définie dans chaque page individuelle.
Mais il y a un problème. Des pages différentes ont un nombre différent de colonnes. Le modèle doit établir les dimensions des colonnes avec un code comme celui-ci:
<td colspan=??? height=36>
La question est de savoir comment le modèle déterminera ce qu'il faut mettre à la place des ??? pour chaque page particulière.
La solution : une directive personnalisée ! De la façon suivante, elle peut indiquer pour chaque page combien de colonnes il doit y avoir :
#nombCol "3"
L'information sera envoyée au modèle, qui pourra s'en servir de la manière suivante pour générer le HTML, n'est-ce pas ?
<td colspan={nombCol} height=36>
Faux ! L'appel d'une macro à l'intérieur de crochets ne sera pas vu.
Heureusement, il y a plusieurs solutions. En voici une : enlever simplement la protection.
\<td colspan={nombCol} height=36>
En voici une autre : générer la totalité de la balise.
{"<td colspan=" + nombCol + " height=36>"}
Ou encore : générer séparément chaque élément du code.
{"<td colspan="}\{nombCol} \{"height=36>"}
Trucs futés
Cette capacité à obtenir la valeur d'une directive en mettant son nom entre accolades est vraiment super. Mais les choses peuvent devenir assez compliquées à cause de la manière dont les pages peuvent être incluses les une dans les autres : un modèle dans lequel est incluse une page, dans laquelle est peut-être incluse une autre page, etc.
Une directive qui est à l'un des niveaux supérieurs de la hiérarchie, par exemple dans un modèle, est visible jusqu'au niveau inférieur de la hiérarchie dans une page rendue avec ce modèle, puis dans un objet rendu dans cette page avec {renderObject}, et ainsi de suite. C'est ainsi parce que toutes les directives sont rassemblées dans websites.["#data"], avant que les appels de macro ne soient interprétés.
Mais une directive personnalisée insérée dans une page incluse n'est visible à l'extérieur de sa propre page que dans ce qui vient physiquement après l'appel de {renderObject} qui l'a inclus.
Supposons par exemple que vous n'ayez aucune définition pour #monTitre dans secondePage, mais que vous ayez une définition pour #monTitre dans troisièmePage. Et supposons encore que dans votre modèle vous ayez la phrase "{monTitre}"(sans les guillemets) quelque part avant le {bodytext}, et une seconde fois quelque part après {bodytext}.
Que pensez-vous qu'il arrivera si vous rendez secondePage pour la voir dans le fureteur ? Essayez pour voir ! L'expression "{monTitre}" du haut de la page a généré une erreur, mais l'expression "{monTitre}" du bas a été remplacée par votre valeur de mon #monTitre !
Il en est ainsi car {renderObject} est l'appel d'une macro, comme l'est {monTitre}. Quand le premier {monTitre} a été évalué, Frontier ne s'était pas encore préoccupé de {renderObject}. Par conséquent, aucune entrée "monTitre" n'était présente dans websites.["#data"], et c'est cela qui a produit l'erreur.
Mais quand {renderObject} a été interprété par Frontier, ce dernier a commencé à travailler sur troisièmePage, et il ajouté les directives de cette page à websites.["#data"] où elles sont demeurées. Par conséquent, quand la dernière expression de {monTitre} a été atteinte par Frontier, il y avait dans websites.["#data"] une valeur pour faire la substitution.
Maintenant, vous pensez probablement : "Je parie que c'est encore un problème qui pourra être résolu par un second tour de traitement des macros." La réponse est non, quand nous faisons le second tour, le premier {monTitre} a disparu, il a été remplacé par le message d'erreur !
Mais il y a une solution...
Obtenir des directives d'une autre page
Nous allons utiliser une macro pour nous sortir de l'embarras. Il y a une solution qui implique une directive par défaut (et un second tour de traitement des macros), mais ce second tour de traitement des macros peut être un casse-tête (par exemple, cela peut provoquer un problème au niveau de la conformité des URL parce qu'ils seront traités deux fois, etc.). Il est en général préférable d'éviter ce second tour de traitement des macros.
Voici un script macro qui ira chercher toutes les directives d'un objet qui n'est pas la page qui est en train d'être rendue. Vous n'avez pas besoin d'inclure cette page distante, mais en général, c'est ce vous ferez probablement.
Note : cette macro est inutile à moins que vous ne désiriez faire référence à des directives qui sont dans une page distante avant votre appel à la macro renderObject.
Vous n'avez pas besoin de comprendre ce script, vous n'avez qu'à le reproduire tel qu'il apparaît ici. Ce script devra aller dans votre table websites.monPremierSite.#tools. La table #tools est une table vraiment appropriée pour stocker vos macros. Vous pouvez aussi les ranger dans user.html.macros, ce qui vous permettra d'utiliser ces macros dans plusieurs sites.
Il faut prêter attention à la chose suivante lorsque vous serez en train de taper ce script : il ne doit pas avoir d'espace après la barre oblique inversée dans la ligne qui est en grisé. La barre oblique inversée est un signe qui permet d'indiquer à Frontier que la ligne se poursuit à la ligne suivante. Vous ne pourrez pas compiler ce script si la barre oblique inversée n'est pas le dernier caractère de la ligne.
Mettez la ligne suivante au sommet de votre modèle ("template").
{getDirectives (pageIncluse)}
Cette ligne indique à la page en train d'être rendue qu'il faut aller chercher les directives de la page distante avant de faire le traitement des macros.
Après cette ligne, vous pouvez faire référence dans votre modèle ou dans votre page, à n'importe quelle directive qui figure dans la page distante.
Mettez la directive suivante dans secondePage :
#pageIncluse "websites.monPremierSite.troisièmePage"
Cette directive indique au modèle dans quelle page il faut aller chercher les directives.
Un peu plus loin dans secondePage, vous aurez votre appel à renderObject(). Ça pourrait ressembler à ceci :
{renderObject (@websites.monPremierSite.troisièmePage)}
Ou à cela :
{renderObject (pageIncluse)}
Vous ne voudrez peut être pas allez chercher les directives d'un autre objet pour chaque page de votre site. Si c'est le cas, vous pouvez modifier la macro getDirectives pour qu'elle devienne inopérante quand pageIncluse est vide.
Pour ce faire, mettez une directive par défaut #pageIncluse dans websites.monPremierSite.#prefs.pageIncluse. La valeur de cette directive devrait être "", soit une chaîne de caractère vide.
Puis dans la macro getDirectives, mettez une ligne en-dessous des variables déclarées comme locales (au sommet du script, et après les éléments compris dans "local") :
if adrObject == ""
return ("")
La variable adrObject contient à présent ce que la directive pageIncluse contient. Si c'est "", le script donnera pour résultat "" et ne fera donc rien de spécial.
|