Overblog
Editer l'article Suivre ce blog Administration + Créer mon blog
17 octobre 2008 5 17 /10 /octobre /2008 12:00

Le programme reclasseBody.js dont le mode d'emploi se trouve dans cet article lit l'URL de la page, la découpe en fines lamelles et en déduit des classes qu'il ajoute au body de la page. Le mode d'emploi du programme se trouve dans l'autre article, merci de vous y reporter si c'est ça qui vous intéresse. Mais si vous voulez en apprendre un peu plus sur la tambouille interne ou prendre des idées pour vos propres recettes, bavardons encore un peu.

Parlons d'URL OverBlog

Encore ? Encore. Le mode d'emploi du programme ne donne que quelques principes très généraux, il y a aussi des cas particuliers, et il faut revoir tout ça avant de plonger dans la programmation.

Notez bien que ma  science  des URL OverBlog ne vient que de la pratique et de la réflexion, que je ne suis pas dans le secret des dieux, que ce que je vais vous expliquer peut se périmer ou se compléter. Ça s'est d'ailleurs déjà produit, dans ce cas je m'adapte après  un certain temps  ;-)

URL OB : cas général

L'URL se présente comme http://URLduBlog/nn-nature-identifiant.html. Détaillons :

  • le début de l'URL, http://URLduBlog/ est toujours le même pour un même blog, évidemment ;
  • la fin .html aussi, elle indique simplement que la page est écrite en langage HTML (il y a une exception, pas gênante) ;
  • au milieu, nn-nature-identifiant, qui est le  nom  du fichier HTML affiché, donne une identité unique à la page :
    • nature indique ce à quoi on a affaire : index (page d'accueil), categorie, archive etc. . C'est toujours le premier ou le deuxième  mot  du nom de fichier.
    • identifiant est toujours composé de chiffres, c'est le numéro propre à l'article, à la catégorie… Il n'est pas toujours présent (exemple : les pages d'accueil). Quand il est présent, il est toujours le dernier mot du nom de fichier, sauf dans deux cas (cf. infra).
    • nn est un numéro d'ordre, lorsque le contenu affiché doit se segmenter en plusieurs pages HTML. C'est un simple compteur à un ou plusieurs chiffres, généralement le rang du premier article affiché dans la page. Pour un blog à 5 articles/page 0-index : page d'accueil, 5-index : 1ère suite de l'accueil, 10-index : 2e suite de l'accueil, etc. . Lorsque le contenu, par nature, doit tenir dans une seule page même très longue, il n'y a pas de numéro d'ordre – exemples : un article seul, le top 50 des articles les plus commentés, les CGU…

URL OB : cas particuliers

l'accueil du blog
Son URL est :
 http://URLduBlog/ 
sans aucun nom de fichier. Ce nom de fichier absent, par défaut, est index.html ou, si l'on veut faire apparaître un numéro d'ordre : 0-index.html. Faites l'essai sur votre blog, vous verrez que ça marche !
articles postérieurs à octobre 2009
Ils incorporent à l'url le titre de l'article, sous forme de mots séparés par des tirets. Ce titre s'écrit après le mot article et avant le numéro identifiant l'article dans l'url, ce qui donne :
 http://URLduBlog/article-titreDarticle-numeroDarticle.html 
Le blogueur peut modifier, voire supprimer complètement cette série de mots.
pages de commentaires
Pour les articles récents l'URL suit le schéma général : nn-comments-numeroDarticle, où nn compte les commentaires. Les articles plus anciens suivent un principe différent : l'URL de l'article suivie d'un '-6' ou de '-comments', soit nn-article-numeroDarticle-6 ou, avec le titre : nn-article-titreDarticle-numeroDarticle-6. C'est un des cas où le numéro d'identification n'est pas à la fin du nom de fichier.
"pages" (OB-pages)
Il s'agit de ces 'articles' sans possibilité de commentaire, en nombre limité, et dotés d'un système de catégories différent de celui des vrais articles. Je les appelle OB-pages dans la suite. Elles ont un chemin d'accès particulier : http://URLduBlog/pages/. De plus, c'est l'autre cas où le numéro d'identification n'est pas à la fin du nom de fichier. Il est au début, ce qui donne :
 http://URLduBlog/pages/numeroDePage-titreDePage.html 
archives
Elles suivent le schéma général mais l'identifiant se compose de la date de l'archive (mois-jour-année ou mois-année selon le cas), ce qui donne :
 http://URLduBlog/nn-archive-mm-jj-aaaa.html http://URLduBlog/nn-archive-mm-aaaa.html 
Le jour et le mois s'écrivent tantôt avec zéro initial, tantôt sans : il ne faut donc pas espérer s'y retrouver en comptant les caractères. Le numéro d'ordre va de 40 en 40, cet intervalle n'est pas réglable.
liste des articles
Son URL est :
 http://URLduBlog/nn-liste-articles.html 
Pas de numéro d'identification, il serait bien inutile. Le numéro d'ordre va de 40 en 40, cet intervalle n'est pas réglable.
liste des articles les plus commentés
Son URL est :
 http://URLduBlog/top-articles.html 
Pas de numéro d'identification, pas de numéro d'ordre.
CGU du blog
Hé oui, elles aussi ! Leur URL est :
 http://URLduBlog/reglement-blog.php 
Notez que l'extension est .php au lieu de .html, et que ça nous est complètement égal pour la suite.

De tout cet indispensable pinaillage émergent heureusement quelques régularités :

  • le nom de fichier s'organise en mots séparés par des tirets ;
  • les OB-Pages ont un schéma d'URL bien à elles et un chemin d'accès particulier. Tout ce qui n'est pas OB-Page respecte les règles qui suivent ;
  • si le premier mot est purement numérique, c'est forcément un numéro d'ordre ;
  • le mot qui suit le numéro d'ordre (donc le premier ou le deuxième) ne contient aucun chiffre et donne la nature de la page ;
  • le dernier mot, dans la plupart des cas, est un identifiant unique propre à la page concernée. Première exception : si ce dernier mot est un '6' ou 'comments', il faut considérer qu'on est sur une page de nature comments et oublier ce '6' ou 'comments'. Deuxième exception : les archives, où les derniers mots forment à eux tous l'identifiant de la page ;
  • s'il y a des mots entre la nature de page et le dernier mot, ils forment à eux tous un complément d'identification : titre d'article ou autre.

C'est toute cette littérature que nous allons maintenant traduire en instructions JavaScript.

Ce que fait le programme

C'est assez simple (je sais, je dis toujours que c'est  assez simple  …) :

  1. récupérer l'URL de la page – c'est une chaîne de caractères ;
  2. découper cette URL à hauteur des slash et repérer la présence éventuelle du répertoire /pages/ ;
  3. extraire le filename ;
  4. découper le filename à hauteur des tirets pour en extraire les diverses informations ;
  5. de là, fabriquer de deux à quatre noms de classes…
  6. … et les ajouter au body.

Le programme accomplit ces tâches en séquence, sans aiguillages très compliqués, nous le passerons donc en revue dans l'ordre où il est écrit. C'est un joli petit échantillon de trituration de chaînes de caractères et de tableaux, vous allez peut-être vous instruire…

Récupérer l'URL

Toutes les informations, absolument toutes, présentes dans la fenêtre du navigateur sont regroupées, aux yeux de l'interpréteur JavaScript, dans une seule structure de données (un  objet ) nommée window. Cet objet comporte plusieurs subdivisions (ou attributs, ou facettes, ou sous-objets…).

La plus volumineuse et la plus utilisée est bien sûr le document proprement dit. On le nomme window.document ou, pour faire court, document. C'est une présentation à l'usage de JavaScript du source HTML soumis au navigateur, tout comme le contenu affiché à l'écran en est une présentation à l'usage de vos yeux. Dans les articles précédents nous n'avions exploité que window.document.

Mais l'objet window contient aussi des renseignements sur le navigateur utilisé, les dimensions de l'écran… toutes choses qui ne dépendent pas du document affiché et n'ont donc pas leur place dans le sous-objet document. Pour en savoir plus, vous pouvez vous reporter à la description de l'objet JavaScript qui figure dans ce très bon tutoriel.

Ce qui nous intéresse ici est window.location, copie exacte du contenu de la barre d'adresse de votre navigateur. Un petit exemple rapide :

Cet objet comporte plusieurs sous-objets pour décomposer l'URL. Le nom du fichier et son chemin d'accès, qui seuls nous intéressent ici, se trouvent dans window.location.pathname. Un autre petit exemple rapide :

Le résultat de ces essais (présence ou non d'un slash initial) peut dépendre du navigateur. On va donc programmer  blindé , sans hypothèse trop confiante, et commencer le programme par

 function reclasseBody(){ var nomFichier = window.location.pathname; // nomFichier est une chaîne de caractères } 
… ce qui permet de cocher le 1er point de la liste de courses.

Récupérer le répertoire /pages éventuel

On veut découper la chaîne à hauteur des slash s'il y en a, justement il existe une méthode pour ça, nommée split. Elle renvoie un tableau contenant les différents tronçons (certains pouvant être vides) :

 nomFichier = nomFichier.split('\/'); // nomFichier est maintenant un tableau 

Le tout dernier élément est le nom du fichier (avec l'extension .html ou .php) et l'avant-dernier, si l'on a affaire à une OB-page, est le répertoire des OB-pages. Un test nous le dira, son résultat est conservé dans la variable booléenne isPage déclarée pour l'occasion :

 var isPage=(nomFichier.length >=2) && (nomFichier[nomFichier.length - 2]=='pages') ; 

Notez la précaution de vérifier d'abord qu'il y a au moins deux éléments avant de tester l'avant-dernier, et cochons la 2e ligne de la liste.

Récupérons aussi le tout dernier élément (=filename+fileext) :

 nomFichier = nomFichier[nomFichier.length - 1] ; // nomFichier est à nouveau une chaîne de caractères 

Il y a dans cette chaîne un point pour séparer le filename du fileext. Encore un coup de tronçonneuse split sur le point (seul le premier tronçon nous intéressera : le filename) :

 nomFichier = nomFichier.split('.'); // résultat : un tableau nomFichier = nomFichier[0]; // résultat : une chaîne 

En condensant quelques instructions le début du programme devient ceci :

 function reclasseBody() { var nomFichier = window.location.pathname.split('\/') ,isPage=(nomFichier.length >=2) && (nomFichier[nomFichier.length - 2]=='pages') ; nomFichier = nomFichier[nomFichier.length - 1] ; nomFichier = nomFichier.split('.')[0]; } 

On peut cocher la 3e ligne de la liste.

Découper le filename

Première chose : il peut ne pas y avoir de filename du tout si l'on est arrivé sur la page d'accueil du blog. On sait que dans ce cas-là le filename est en réalité index (et l'extension .html). Hé bien on le dit :

 if (!nomFichier) nomFichier='index'; 

Ensuite, que le nom de fichier soit déduit de l'URL ou reconstitué comme on vient de le faire, il faut tout découper à hauteur des tirets :

 nomFichier = nomFichier.split('-'); 

Cochons la 4e ligne de la liste, et nous voilà devant un tableau de mots, à interpréter aussi intelligemment que possible.

Traitement des OB-pages

Ce cas est facile : pas de numéro d'ordre, le nom de fichier se compose d'un numéro d'identité suivi d'un texte descriptif qu'on récupère aussi, à tout hasard :

 var pgType, pgNumero, pgIdent, pgSubIdent ; if (isPage) { pgType='page'; pgNumero=0; pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } else {/* traitement d'une page normale */ } 

Vous voyez apparaître quatre variables où nous rangeons les informations tirées de nomFichier :

  • pgType nature de la page,
  • pgNumero numéro d'ordre,
  • pgIdent identifiant unique,
  • et pgSubIdent complément d'information.

Tout le jeu ultérieur sera de calculer correctement ces quatre variables dans les divers cas de figure. Vous remarquez aussi deux nouvelles méthodes :

  • shift retourne le premier élément du tableau et le retire de ce tableau. Le tableau ne contient donc plus que les tronçons formant le titre de la page ;
  • join produit l'effet inverse de split : tous les éléments du tableau sont raboutés, en les séparant par des tirets (argument '-' de la méthode join).

La section suivante est consacrée au traitement d'une page normale, autrement dit elle va remplir le else. Pour alléger un peu l'exposé je n'y répéterai pas ce else et ses accolades, nous les retrouverons au moment d'exploiter les informations collectées.

Analyser le filename

Si nous revenons à notre liste de principes, nous trouvons une règle universelle : si le 1er mot est un nombre c'est le numéro d'ordre de la page – et, implicitement, il ne change rien à l'interprétation du reste de l'URL. Bon, allons-y :

 var mot; mot = nomFichier[0]; 

Comment savoir que mot, qui est une chaîne de caractères, peut se comprendre comme un nombre ou pas ? La technique la plus rapide, exploitant la grande tolérance de JavaScript en matière de types de données, est de le multiplier par 1 et de tester le résultat.

Gné ? Voui : 02 (ou 2 ou 0002) est un nombre mais '02' (qui n'est ni '2' ni '0002') est une chaîne de caractères. Si vous écrivez '02'+'02', vous obtiendrez '0202' et pas 4, tout comme 'deux'+'deux' fait 'deuxdeux' et pas 'quatre'. Mais si vous multipliez une chaîne par le nombre 1, JavaScript va faire de son mieux pour que l'expression ait un sens :
1* 'brendufat' c'est vraiment pas possible mais
1* '02', ça fait 2 (le nombre, pas la chaîne '2').

Deuxième exploitation du bon caractère de JavaScript, le test. Là aussi, faisant de son mieux pour comprendre ce qu'on lui demande, il considère que 2 et '2' sont égaux (alors que 2 et 'deux', voire 2 et '1+1' ne le sont pas, faut pas pousser). Au bout du compte, pour savoir si mot est un nombre, nous écrirons

 if ( mot== 1*mot) {/* mot est un nombre */} else {/* mot n'est pas un nombre */}; 

Que mettre entre les accolades ? Si mot est un nombre : 1) c'est le numéro d'ordre et 2) nous l'éjectons du tableau. Si mot n'est pas un nombre 1) le numéro d'ordre est zéro et 2) nous ne touchons pas au tableau. Ça s'écrit brièvement :

 if ( mot== 1*mot) nomFichier.shift(); else mot=0; pgNumero=mot; 
À nouveau la méthode shift mais cette fois on laisse perdre le résultat.

Au suivant ! Dans le tableau ainsi débarrassé de l'éventuel numéro d'ordre, on sait que le premier mot indique la nature de la page. Donc on le récupère et le supprime du tableau, comme ceci :

 pgType=nomFichier.shift(); 

La suite des opérations dépend de ce qu'on vient de trouver :

  • article : il y a peut-être, à la fin, un "6" ou un "comments" signalant une page de commentaires. Il peut aussi y avoir le titre de l'article.
  • archive : les deux ou trois éléments du tableau forment la date de l'archive, c'est elle qui nous servira de "numéro d'identité".
  • tous les autres cas : le premier élément du tableau nous sert de "numéro d'identité", l'éventuel reliquat sera conservé comme complément d'information.

Ce genre d'aiguillage selon la valeur de quelque chose (ici pgType) se traite par une instruction switch :

 switch (pgType) { case 'article' : /* traitement du cas où pgType == 'article' */ case 'archive' : /* traitement du cas où pgType == 'archive' */ default : /* tous les autres cas*/ } 

Le cas article est le plus complexe :

 case 'article' : mot= nomFichier[nomFichier.length-1]; if (mot=='6'|| mot=='comments') { pgType='comments'; nomFichier.pop(); } pgIdent= nomFichier.pop(); pgSubIdent = nomFichier.join('-'); break; 

En paraphrasant :

  • on regarde le dernier mot du tableau. Si c'est un signal de commentaires ('6' ou 'comments') on redéfinit pgType puis on élimine cette fin de tableau : la méthode pop (joli nom !) renvoie le dernier élément du tableau et le retire du tableau.
  • l'éventuel signal de comms ayant disparu, la fin du tableau est maintenant le numéro de l'article. On le récupère et l'élimine par un autre appel à pop.
  • ne restent plus que les morceaux du titre, s'il y en a, qu'on recolle par un appel à join.
  • le break est là pour nous faire sortir du switch, sans quoi on passerait aux instructions suivantes.

Le cas archive est bien plus simple :

 case 'archive' : pgIdent=nomFichier.join('-'); break; 

Paraphrase : on recolle les morceaux de la date et roulez jeunesse…

Et maintenant, pour tous les autres cas, la voiture-balai :

 default : pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); 

Paraphrase : on récupère/élimine le premier mot en guise de numéro d'identité. Le reste éventuel, après recollage, est un complément d'information.

Notez aussi qu'il n'y a pas, ici, d'instruction break : ce cas est le dernier du switch, après l'avoir exécuté on sort de toute manière du switch.


Voilà, nous avons fini d'être intelligents, nous avons garni les accolades du else :

 else { mot = nomFichier[0]; if ( mot== 1*mot) nomFichier.shift(); else mot=0; pgNumero=mot; pgType=nomFichier.shift(); switch (pgType) { case 'article' : mot= nomFichier[nomFichier.length-1]; if (mot=='6'|| mot=='comments') {pgType='comments';nomFichier.pop()} pgIdent= nomFichier.pop(); pgSubIdent = nomFichier.join('-'); break; case 'archive' : pgIdent=nomFichier.join('-'); break; default : pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } }; 

Bilan : le cas OB-page et le cas usuel sont couverts. Cochons la 4e ligne de la liste et redevenons bourrins pour former le nom des classes que nous allons donner au body.

Repérer l'élément body

Procédé bestial : repérer tous les éléments body du document (tous… façon de parler) et garder le premier :

 var refBody=document.getElementsByTagName('body')[0]; 

Donner une classe au body

Autrement dit, construire une jolie chaîne de caractères avec tous les éléments calculés précédemment, et la flanquer dans le className du body :

 refBody.className = 'une_très_jolie_chaîne' ; 

Euh… sait-on jamais, un autre script astucieux a peut-être déjà donné une classe au body, il serait fâcheux de perdre cette information. Donc, plutôt que de mettre la jolie chaîne dans className, on va la rajouter à la fin :

 refBody.className += ' une_très_jolie_chaîne' ; // avé un espace de séparation au début de la chaîne 

Si jamais vous installez après mon gadget un autre script génial qui redéfinit la classe du body sans prendre la même précaution, vous pourrez engueuler son auteur de ma part (et corriger son script au passage).

Et l'espace au début de la chaîne ? Il est là pour séparer les nouvelles classes de l'existant éventuel. Bien. Si le body n'a pas déjà de classe(s), la liste de classes après ajout commencera donc par un espace. Ce n'est pas défendu mais certains navigateurs ne le digèrent pas. Donc selon donc que refBody.className est déjà défini ou pas, on ajoutera ou pas cet espace initial :

 refBody.className+=(refBody.className ? ' ' : '') /*+ les nouvelles classes */; 

Ah, encore une construction exotique plutôt que des if ! Parlons du test rapide ? : si la condition est vraie (= il y a déjà une classe) le résultat suit immédiatement (= un espace), si elle est fausse(= il n'y a pas de classe) le résultat vient après le : (= chaîne vide : deux apostrophes consécutives, pas un guillemet !)

Bon, revenons à la jolie chaîne… L'intention est de donner au body des noms de classe qui contiennent l'information précédente. Notre "page 33 de la catégorie 84203", par exemple, recevra les classes pg_categorie (= c'est une page de catégorie), pgid_84203 (= de la catégorie 84203) et pgnum_33 (= la page numéro 33 de cette catégorie). Simple. Si une information manque (pas le numéro d'ordre, toujours défini quitte à valoir zéro) elle ne produira pas de classe.

En une seule instruction :

 refBody.className+=(refBody.className ? ' ' : '') + 'pg_'+pgType+' pgnum_'+pgNumero +(pgIdent ? ' pgid_'+pgIdent :'') +(pgSubIdent ? ' pgsubid_'+pgSubIdent :''); 

Le test rapide est bien commode…

Synthèse

En assemblant tous les fragments qui précèdent, on arrive à ça :

 function reclasseBody() { var refBody=document.getElementsByTagName('body')[0] ,pgNumero,pgType,pgIdent,pgSubIdent ,mot,nomFichier = window.location.pathname.split('\/') ,isPage=(nomFichier.length >=2) && (nomFichier[nomFichier.length - 2]=='pages') ; nomFichier = nomFichier[nomFichier.length - 1] ; nomFichier = nomFichier.split('.')[0]; if (!nomFichier) nomFichier='index'; nomFichier = nomFichier.split('-'); if (isPage) {pgType='page'; pgNumero=0; pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } else { mot = nomFichier[0]; if ( mot== 1*mot) nomFichier.shift(); else mot=0; pgNumero=mot; pgType=nomFichier.shift(); switch (pgType) { case 'article' : mot= nomFichier[nomFichier.length-1]; if (mot=='6'|| mot=='comments') {pgType='comments';nomFichier.pop()} pgIdent= nomFichier.pop(); pgSubIdent = nomFichier.join('-'); break; case 'archive' : pgIdent=nomFichier.join('-'); break; default : pgIdent=nomFichier.shift(); pgSubIdent=nomFichier.join('-'); } }; refBody.className+=(refBody.className ? ' ' : '') +'pg_'+pgType+' pgnum_'+pgNumero +(pgIdent ? ' pgid_'+pgIdent :'') +(pgSubIdent ? ' pgsubid_'+pgSubIdent :''); } reclasseBody(); 

Voilà, nous avons reconstruit le programme. Avec cette petite vingtaine de lignes de JavaScript, c'est comme si vous aviez un onglet de CSS pour chaque type de page de blog et même pour chaque page de blog, y compris la liste des articles et les CGU. Mignon, non ? Si vous combinez ça avec les possibilités qu'offrent, justement, les onglets, et celles que vous donne cet autre gadget analogue, mais pour les articles, il y a de quoi s'occuper.


À toutes fins utiles : quelques bonnes adresses pour en savoir plus sur JavaScript.

commentaires

Archives