samedi 18 octobre 2014 (), par
Ce script est le même pour le diaporama de la page de garde et pour le diaporama de la galerie.
Il lit des paramètres dans la page (numéro de rubrique à parcourir, nombre de niveaux de sous-rubriques à chercher, langue) et envoie la requête au serveur, puis insère dans la page l’image, le titre et le texte de chaque élément du diaporama en l’adaptant à la taille de la fenêtre, avant de demander l’élément suivant.
En effet, les éléments de ce diaporama sont des articles :
Comme on peut le voir dans l’aperçu Firebug de la structure de la page de garde, il y a dans chaque page, pour l’évènement onLoad
(= "au chargement") de l’élément HTML body
, appel à une fonction unique et générique "AuChargement".
function AuChargement(){
pisteIE();
cachejs();
setTimeout("charger_diapo()",1000); // 1 seconde
traitercadres();
traiterimages();
}
Cette fonction unique en invoque d’autres, qui, selon les éléments présents ou non dans la page, feront quelque chose :
pisteIE
va, comme son nom l’indique, rediriger les utilisateurs du navigateur Internet Explorer vers une page d’avertissement, indiquant les déficiences de Microsoft Internet Explorer quand aux éléments pourtant normalisés W3C de la page.
cachejs
sert, comme son nom l’indique, à cacher le message selon lequel JavaScript est désactivé sur le navigateur.setTimeout("charger_diapo()",1000);
lance la fonction charger_diapo()
au bout d’une seconde.
traitercadres
et traiterimages
vont redimensionner les cadres et images qui en ont besoin.function charger_diapo(){
if (this.document.getElementById('diapo')!=null){
Cette fonction ne s’exécutera que si un élément "diapo" est présent dans la page
if (this.document.getElementById('nrubrique')!=null){
nrubrique=this.document.getElementById('nrubrique').value;
}else{
nrubrique=0;
}
Pour chaque paramètre du diaporama, on vérifie sa présence, et en cas d’absence on utilise une valeur par défaut
Puis on initialise quelques valeurs avant d’appeler la fonction qui créera et enverra la requête.
var url_image=null;
var texte_diapo=null;
var titre_diapo=null;
requete_requete_image_diapo('charger_diapo.php?id=0&rub='+nrubrique+'&min='+mindepth+'&max='+maxdepth+'&lang='+lang);
}
}
Le script javaScript défini quelque variables globales, que plusieurs fonctions utiliseront.
var diapo = 0;
var prochain=0;
var nrubrique=0;
var mindepth=0;
var maxdepth=1;
var delai=5000;
Avant de pouvoir créer la requête XMLHTTP, il faut un objet de type xmlhttprequest pour y mettre les paramètres, l’envoyer au serveur et recevoir sa réponse.
En fonction du navigateur, on utilisera plusieurs méthodes.
La fonction précédente, qui créé l’objet xmlhttprequest, est appelée par la fonction de création et d’envoi de la requête que voici :
function requete_requete_image_diapo(url){
requete_image_diapo=get_requete_image_diapo();
if (!requete_image_diapo){
alert("Votre navigateur ne peut créer de requête AJAX.");
/* + redirectiuon vers page navigs.html */
return;
}
On récupère l’objet xmlhttprequest, puis, si celui-ci n’a pu être créé et que par conséquent la variable requete_image_diapo
n’existe pas - le !
signifiant la négation - on affiche un message et l’on quitte la fonction.
Sinon, on poursuit
//alert("envoi de la requete");
requete_image_diapo.onreadystatechange=function(){traitementReponse_requete_image_diapo(requete_image_diapo)}
requete_image_diapo.open("GET",url,true);
requete_image_diapo.send(null);
}
requete_image_diapo.onreadystatechange=<code> définit la fonction <code>function(){ ... }
de traitement de la réponse, quand la requête aura "changé d’état".
Puis on ouvre l’url du script du serveur auquel il faut envoyer la requête, et qui la traitera pour envoyer la réponse selon les paramètres indiqués, et on y envoie la requête.
function traitementReponse_requete_image_diapo(requete_image_diapo){
var contenu;
var erreur=0;
if (requete_image_diapo.readyState==4){
Si la requête est dans l’état "4", c’est à dire que l’on a reçu la réponse du serveur,
//alert("reception de la reponse : traitement");
contenu=requete_image_diapo.responseXML;
On récupère le document XML constituant la réponse du serveur.
if (contenu.getElementsByTagName("image")[0].firstChild.nodeValue!=null){
url_image=contenu.getElementsByTagName("image")[0].firstChild.nodeValue;
}else{
erreur=1;
}
On vérifie la présence de différents éléments de réponse, en préparant des valeurs par défaut si besoin.
contenu
est le document XML de réponse. Celui-ci est organisé selon une arborescence de balises pour les divers éléments et leurs valeurs.getElementsByTagName
pour adresser ces balises par leur nom
nodeValue
).De même, on récupère plusieurs paramètres toujours présents, dont la prochaine image preload
que l’on va commencer à charger comme arrière plan à taille nulle pour accélérer son chargement lorsque ce sera son tour.
prochain=contenu.getElementsByTagName("prochain")[0].firstChild.nodeValue;
// delai=contenu.getElementsByTagName("delai")[0].firstChild.nodeValue;
preload=contenu.getElementsByTagName("preload")[0].firstChild.nodeValue;
//alert("texte="+contenu.getElementsByTagName("texte_diapo")[0].firstChild.nodeValue);
Pour comprendre à quoi correspondent ces éléments, voir l’onglet détaillant le script PHP.
Et si tout va bien (variable erreur
toujours à zéro)
if (erreur==0){
On essaie de récupérer le bloc "image" du diaporama afin de pouvoir y intégrer l’image indiquée dans le document XML.
Dans la même structure conditionnelle, l’on s’assure qu’il y a bien une image à y intégrer (l’url de l’image fournie dans la réponse XML est non nulle).
if ((this.document.getElementById('diapo_image')!=null)&&(url_image!="")){
//alert ("on a le div image");
Si le bloc image existe, on le référence afin de pouvoir le modifier, en utilisant la fonction getElementById
qui renvoie un élément unique, puis on y intègre un lien vers l’article sous la forme d’une image qui est son logo avec la valeur innerHTML
de l’élément, qui correspond au code HTML que l’élément contient.
var div_image=this.document.getElementById('diapo_image'); // le div
div_image.innerHTML="<a href=\""+url_article+"\"><img src=\""+url_image+"\" style=\"margin-left:0px;margin-top:0px;\"></a>";
L’image du diaporama étant dans son bloc, il est peut être utile à présent de la redimensionner afin de l’adapter aux dimensions dudit bloc.
On récupère donc les dimensions du bloc, par offsetHeight
et offsetWidth
, puis les dimensions réelles de l’image, par les valeurs "hauteur" et "largeur" fournies dans la réponse (voir script PHP).
var h_div=this.document.getElementById('diapo_image').offsetHeight;
var l_div=this.document.getElementById('diapo_image').offsetWidth;
var h_img=parseFloat(contenu.getElementsByTagName("hauteur")[0].firstChild.nodeValue);
var l_img=parseFloat(contenu.getElementsByTagName("largeur")[0].firstChild.nodeValue);
//alert("traitement d'une image de "+h_img+" par "+l_img);
Selon la différence de dimension entre le bloc et l’image, il faudra la réduire prioritairement en largeur, en hauteur, ou les deux, et si l’image n’est pas trop grande, on cherchera à la centrer.
Le reste du script de traitement devrait être aisément compréhensible, d’autant que les commentaires permettent de suivre les conditions vérifiées et le traitement effectué.
Le principe reste toujours le même :
if (l_img>l_div){
var reduction=l_img/l_div; // la proportion de réduction, qui sera la meme en hauteur et largeur
// si h_img/reduction>h_div alors
if ((h_img/reduction)>h_div){
// image plus large et en réduite, plus haute que le div
// image plus HAUTE que large
// réduction_h = h_img/h_div
var reduction_h=h_img/h_div;
// appliquer reduction_h en hauteur et largeur
var nh=h_img/reduction_h;
var nl=l_img/reduction_h;
// pour être au milieu
Une fois l’image réduite, on calcule la marge, puis on applique réduction et marge de centrage.
De même que pour le bloc diapo, on utilise la fonction getElementById
pour référencer le bloc, puis, comme pour la réponse XML, getElementsByTagName
pour récupérer le tableau des images (balise <img ...>
contenues, la première (et seule) étant à la case 0.
Puis l’on définit les attributs HTML de cette balise <img ...>
pour retailler l’image, et lui ajouter un style personnalisé contenant la marge de centrage, ainsi que la prochaine image preload
comme arrière-plan de taille nulle pour accélérer son chargement lorsque ce sera son tour.
var margel=(l_div-nl)/2;
// si la marge est > à x px, calculer la différence et réduire div image pour agrandir div texte
image=this.document.getElementById('diapo_image').getElementsByTagName('img')[0];
image.setAttribute("height",nh);
image.setAttribute("width",nl);
image.setAttribute("style","margin-left:"+margel+"px; background-image:url(\""+preload+"\"); background-size: 0px 0px;");
Puis on fait la même chose pour le texte et le titre
if (this.document.getElementById('diapo_texte')!=null){
var div_texte=this.document.getElementById('diapo_texte'); // le div
//alert ("on a le div texte");
div_texte.innerHTML="<span class=\"titre_diapo\"><a href=\""+url_article+"\">"+titre_diapo+"</a></span><p><span class=\"texte_diapo\">"+texte_diapo+"</span></p>";
}else{
//alert("div texte non trouvé");
}
}
...et on demande le prochain élément, si bien sûr cela a du sens.
Si le diaporama n’a aucun ou qu’un seul élément, il n’est pas utile d’envoyer d’autre requête, et le script du serveur indiquera que prochain vaut "-1", soit une valeur non valide.
la commande setTimeout(
permet de définir un délai avant de demander le prochain élément.
diapo=prochain;
if (prochain!=-1){
// requete_requete_image_diapo("charger_diapo.php?id="+diapo+"&delai="+delai);
setTimeout("requete_requete_image_diapo('charger_diapo.php?id='+diapo+'&rub='+nrubrique+'&min='+mindepth+'&max='+maxdepth+'&lang='+lang)",delai);
}
}
}
Le script PHP charger_diapo.php
, situé à la racine du serveur web, et appelé comme URL par la fonction charger_diapo
et après chaque traitement de réponse, va traiter la requête xmlhttp et envoyer une réponse au format XML.
<?php
session_start();
Dans la mesure où les scripts PHP de SPIP peuvent utiliser des variables de session pour stocker des paramètres, on se connecte à cette session afin de pouvoir récupérer la langue choisie.
On créé l’entête de la réponse avant qu’un autre script, de traitement des accents, ne s’en charge.
le script accents.php
contient plusieurs fonctions, dont une utilisée pour transformer les caractères accentués contenus dans la base en leur équivalent en code HTML, permettant un affichage normal quel que soient les encodages de caractère du serveur et du navigateur.
header('content-Type:text/xml,charset=UTF-8');
require "accents.php";
On se connecte à la base de données.
mysql_connect('serveur','utilisateur','mot de passe','MYSQL');
mysql_select_db("nom de la base");
On travaillera sur les tables des rubriques et des articles de SPIP, qui ont généralement un nom commençant par "spip_".
$tablerubriques="spip_rubriques";
$tablearticles="spip_articles";
La requête xmlhttp envoyant ses paramètres dans l’url, par la méthode GET (voir article sur PHP), on récupère les paramètres par la variable globale $_GET
, qui est un tableau associatif (les colonnes sont identifiées par nom et non par numéro).
if (isset($_GET["id"])){
$idd=$_GET["id"];
}else{
$idd=0; /* id du diapo en cours, à diviser par total de diapo et prendre le reste , ex si idd = 5 pour 3 photos existantes, alors charger photo 2 */
}
Une fonction de parcourt de rubrique et sous-rubrique est définie, ce qui permet de faire du parcours récursif, lorsque la fonction s’appelle elle-même.
La fonction renverra tous les identifiants de sous-rubriques dans lesquelles on est susceptible de trouver des éléments de diaporama.
function parcourt_rubrik($cur_rubrik,$curdepth,$maxdepth,$mindepth) {
Ces deux paramètres ne pouvant être passés d’un niveau à l’autre de la boucle récursive sans rendre la fonction complexe, on les redéfinit localement à chaque niveau.
$tablerubriques="spip_rubriques";
$tablearticles="spip_articles";
$curdepth.=$curdepth+1;
$rubs=array(); // le tableau qu'on renverra, contenant dans la case 0 sa taille
$reksrub="select id_rubrique from ".$tablerubriques." where id_parent=$cur_rubrik";
$resrub=mysql_query($reksrub);
while ($rowrub=mysql_fetch_array($resrub,MYSQL_ASSOC)){
$ssrub=$rowrub["id_rubrique"];
if ($curdepth>=$mindepth){
$rubs[]=$ssrub;
}
if ($curdepth<$maxdepth){
$rubs=array_merge($rubs,parcourt_rubrik($ssrub,$curdepth,$maxdepth,$mindepth));
}
}
return $rubs;
}
On créé donc la liste des rubriques en appelant la fonction récursive, en partant de la rubrique fournie en paramètre par la requête xmlhttp.
$liste_rubriques=parcourt_rubrik($id_rubrique_diapo,$min_depth,$max_depth,$min_depth);
Ici, on construit un morceau de la requête SQL, à savoir une partie de la clause conditionnelle WHERE, en ajoutant à un code de base des morceaux de code SQL [1]
On utilise une boucle while (tant que) pour parcourir un tableau dont la taille est variable, en s’arrêtant lorsque l’index atteint le nombre de case contenues dans le tableau, et en incrémentant l’index au fur et à mesure.
$liste_rubrik="(id_rubrique=$id_rubrique_diapo";
$irub=0;
while ($irub<count($liste_rubriques)){
$liste_rubrik.=" or id_rubrique=$liste_rubriques[$irub]";
$irub++;
}
$liste_rubrik.=")";
Une fois que la partie "liste des rubriques parentes" de la requête des articles que l’on va sélectionner est créée, on l’utilise d’abord dans une requête pour compter les éléments, puis ensuite dans la requête de récupération.
/* requete pour connaître le nombre total de photos pouvant être affichées */
$rek_compt="select count(*) as total from ".$tablearticles." where $liste_rubrik and statut='publie' and lang='".$_SESSION["lang"]."'"; // pas de order by pour compter
$res_compt=mysql_query($rek_compt);
$row_total=mysql_fetch_array($res_compt,MYSQL_ASSOC);
$total=$row_total["total"];
count
count(*)
as total
, plus simple pour la référencer,tablearticles
if ($total>0){
Si il y a effectivement plusieurs articles publiés dans les rubriques ciblées et ce dans la langue choisie, alors on va récupérer les deux premiers articles selon un tri définit plus bas.
Le script JavaScript, fournissant toujours un paramètre de numéro de diapo à charger dans la requête xmlhttp, on se base sur ce paramètre pour démarrer la sélection dans la liste des articles disponibles.
Quand au prochain élément de diaporama à charger, c’est soit l’article suivant dans la liste - si l’article en cours n’est pas le dernier de la liste - soit le premier, à savoir le numéro 0 de la liste (tableau des réponses).
$debut=$idd; // 0 par defaut
$par_page=1;
if (($idd+1)<$total){
$prochain=$idd+1;
}else{
$prochain=0;
}
La requête de sélection est la même que celle de comptage, logique puisque l’on travaille sur les mêmes éléments.
Seules les clauses SELECT - car on ne compte pas cette fois - et la clause de tri ORDER - qui était inutile dans une requête de comptage - vont changer
On ajoute aussi une clause LIMIT qui restreint le nombre de lignes de résultat que l’on va finalement traiter.
*
DESC
, et donc par article le plus récent. $par_page=1;
, et on démarre à la ligne numéro $debut
, à savoir $idd
, le paramètre de numéro de diapo fourni à l’appel du script par la requête xmlhttp. $rek_sel="select * from ".$tablearticles." where $liste_rubrik and statut='publie' and lang='".$_SESSION["lang"]."' order by 'date' DESC limit $debut,$par_page "; // order by date DESC
$res_sel=mysql_query($rek_sel);
Étant donné que l’on traite des articles, et que l’image cherchée est un logo, il est bon de savoir que sous SPIP, les logos ont un nom particulier :
IMG
, peu importe leur extension (jpg, png, gif...) $imagebase="IMG/arton";
Pour chaque ligne de résultat renvoyée (en fait il n’y en a qu’une)
while ($row_sel=mysql_fetch_array($res_sel,MYSQL_ASSOC)){
On récupère la valeur de chaque champ dont on a besoin.
Les champs de type texte (titre et texte) passent par plusieurs filtres pour traiter des caractères spéciaux qui risqueraient de faire planter le document XML réponse.
$id=$row_sel["id_article"];
$titre=htmlentities($row_sel["titre"]);
$titre=remet_accents($titre);
$titre=str_replace("'","'",$titre);
$titre=str_replace('"','"',$titre);
$titre=str_replace("°","°",$titre);
$texte=htmlentities($row_sel["texte"]);
$texte=remet_accents($texte);
$texte=str_replace("'","'",$texte);
$texte=str_replace('"','"',$texte);
$texte=str_replace("°","°",$texte);
$texte=nl2br($texte);
$urlarticle="spip.php?article$id";
Pour l’image, on recherche si le fichier artonXX existe pour plusieurs extensions possibles.
Si un fichier portant ce nom existe, alors on a trouvé.
// image = IMG/arton[+id article].jpg ou autre
// =========== existence du fichier jpg, png ou gif ==============
$extensions=array("jpg","jpeg","JPG","png","PNG","gif","GIF");
$i=0;
$trouve=false;
$image="";
while (($i<sizeof($extensions))&&($trouve==false)){
$image_test=$imagebase.$id.".".$extensions[$i];
//echo "on teste $image_test<br>";
if (file_exists($image_test)){
//echo "$image_test existe<br>";
$image=$image_test;
$trouve=true;
}else{
//echo "$image_test n'existe pas<br>";
}
$i++;
}
// image par défaut
if (!$trouve){
$image="interrogation.png";
}
Si aucun fichier image de logo correspondant à l’article n’est trouvé, on prend une image par défaut.
Puis on récupère les dimensions de l’image contenue dans le fichier avec une fonction PHP.
$infos_image = @getImageSize($image); // info sur la dimension de l'image
// '@' est placé devant la fonction getImageSize() pour empecher l'affichage des erreurs si l'image est absente.
//dimension
$largeur = $infos_image[0]; // largeur de l'image
$hauteur = $infos_image[1]; // hauteur de l'image
Ces informations de taille seront envoyées dans la réponse et utilisées par le script pour redimensionner l’image si besoin est.
Et on oublie pas de fermer la boucle de traitement de la ligne de résultat des articles correspondants à ce que l’on cherche.
}
Puis on sélectionne l’image suivante afin de pouvoir commencer à la charger plus tôt, de sorte qu’au tour d’après, l’image de la diaporama suivante se chargera plus vite.
La requête est la même que la précédente, mais on ne prendra pas la même ligne de résultat.
Le traitement pour trouver le fichier image sera le même.
Si en revanche il n’y a aucun article correspondant à ce que l’on cherche
}else{
$image="interrogation.png";
$texte="\"\"";
$largeur=0;
$hauteur=0;
$delai=0;
$pre_image="\"\"";
$urlarticle="#"; // page actuelle
$prochain=-1;
$titre="Aucune image disponible."; // changer le texte selon la langue
if ($_SESSION["lang"]=="fr"){
$titre="Aucune image disponible."; // changer le texte selon la langue
}
if ($_SESSION["lang"]=="en"){
$titre="No article available for this language : english."; // changer le texte selon la langue
}
if ($_SESSION["lang"]=="de"){
}
$texte=$titre;
// $texte=htmlentities($texte);
// $texte=remet_accents($texte);
}
Puis il est temps de créer le document XML qui servira de réponse.
Dans Firebug, il est possible de voir, dans l’onglet réseau, les requêtes avec leurs paramètres, et les réponses associées en XML.
On crée un objet de type DOMDocument, puis on y ajoute les éléments et sous-éléments, en commençant par la racine du document.
// réponse du serveur = contenu de l'XmlHttpResponse
$doc=new DOMDocument("1.0","utf-8");
$racine=$doc->createElement('root');
$doc->appendChild($racine);
$url_image=$doc->createElement('image',$image);
$racine->appendChild($url_image);
$lien=$doc->createElement('url_article',$urlarticle);
$racine->appendChild($lien);
$titre_diapo=$doc->createElement('titre',$titre);
$racine->appendChild($titre_diapo);
$texte_diapo=$doc->createElement('texte',$texte);
$racine->appendChild($texte_diapo);
$info=$doc->createElement('largeur',$largeur);
$racine->appendChild($info);
$info2=$doc->createElement('hauteur',$hauteur);
$racine->appendChild($info2);
$tempo=$doc->createElement('delai',$delai);
$racine->appendChild($tempo);
$next=$doc->createElement('prochain',$prochain);
$racine->appendChild($next);
$pre=$doc->createElement('preload',$pre_image);
$racine->appendChild($pre);
$debug=$doc->createElement('debug',"$debugmsg");
$racine->appendChild($debug);
Et pour finir, on envoie le document par un simple echo
du texte XML renvoyé par la fonction saveXML
sur l’objet.
echo $doc->saveXML();
?>
[1] ça tombe bien, j’avais justement besoin d’exemple de ce genre de méthode pour un autre article