Archivio

Posts Tagged ‘web’

Flash e Lightbox, galleria completamente funzionante

14 Gennaio 2009 2 commenti

Nel post precedente avevo mostrato come integrare in modo semplice e veloce una galleria di immagini fatta in Flash con Lightbox. Rimaneva, però, un inconveniente: dopo aver aperto un’immagine con Lightbox non era possibile passare direttamente alla successiva, ma era necessario chiuderla e poi cliccare su un’altra immagine della galleria. Ho usato l’idea della soluzione precedente per risolvere anche questo problema.

Per vedere un esempio del risultato potete cliccare sull’immagine qui sotto:

Demo integrazione flash-lightbox

Demo integrazione flash-lightbox

Come utilizzare questa soluzione nelle tue pagine

Prima di tutto, per usare questa soluzione, occorre che la galleria Flash carichi i dati delle immagini da un file in formato xml, chiamato data.xml, con la seguente struttura:

<?xml version="1.0" encoding="utf-8"?>
<thumbs thumbsToScale="5" showReflection="yes" showTitle="no">
  <thumbItem title="Pinguino 1" thumb="foto/01.jpg"
     link="javascript:flashLightbox('foto/01.jpg');"  targetWindow="" />
  <thumbItem title="Pinguino 2" thumb="foto/02.jpg"
     link="javascript:flashLightbox('foto/02.jpg');"  targetWindow="" />
  <thumbItem title="Pinguino 3" thumb="foto/03.jpg"
     link="javascript:flashLightbox('foto/03.jpg');"  targetWindow="" />
  <thumbItem title="" thumb="foto/04.jpg"
     link="javascript:flashLightbox('foto/04.jpg');"  targetWindow="" />
</thumbs>

Gli unici attributi dei thumbItem che vengono utilizzati da Lightbox sono link, che deve contenere l’indirizzo dell’immagine, e title, che può contenere una descrizione dell’immagine.

E’ necessario un modo per leggere questo file e fornire il suo contenuto al codice javascript. Di questo si occupa data_photo_parser.php, che deve essere messo nella stessa posizione di data.xml. Se cambiate il nome (o la posizione) di data.xml, allora dovete modificare di conseguenza la variabile $imgDataFile in data_photo_parser.php.

Una volta installato data_photo_parser.php nella posizione corretta, bisogna includere nella pagina con la galleria Flash il foglio di stile e le librerie javascript esattamente come si farebbe per la versione standard di Lightbox:

<link rel="stylesheet" href="css/lightbox.css" type="text/css" media="screen"/>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/scriptaculous.js?load=effects,builder"></script>
<script type="text/javascript" src="js/lightbox_x_flash.js"></script>

Queste inclusioni, però, non sono sufficienti ad inizilizare la galleria per Lightbox: occorre richiamare esplicitamente la funzione initImageListFlashLightbox() prima della chiusura del </body>, passandogli come parametro l’indirizzo a cui trovare la pagina data_photo_parser.php:

<script type="text/javascript">
initImageListFlashLightbox('http://www.ottaviodinale.it/demo/flash_lightbox_gallery/data_photo_parser.php');
</script>
</body>

A questo punto tutto dovrebbe essere pronto all’uso.

ATTENZIONE: una cosa che potrebbe non essere evidente da quanto detto è che, per funzionare correttamente, la pagina che contiene la galleria d’immagini deve essere ospitata sullo stesso server della pagina  pagina data_photo_parser.php, altrimenti il javascript non potrà recuperare le informazioni in data.xml.

Qui si trovano tutti i file necessari: flash_lightbox_gallery.zip.

Qui la versione dello script modificate e il file php: lightbox_x_flash.js e data_photo_parser.zip.

Lo script modificato può ancora essere utilizzato in modo identico a quanto scritto nel post precedente, usando la nuova funzione flashLightboxSingle(img) al post di flashLightbox(img), nei link della galleria Flash.

Come funziona

Lo scopo dello script è integrare Flash e Lightbox nel modo più semplice possibile. Tutte le informazioni relative alle immagini sono già state specificate nel file data.xml, quindi volevo evitare di doverle ripetere all’interno della pagina con la galleria. D’altra parte, queste informazioni sono necessarie per poter utilizzare Lightbox ed in qualche modo devono essere elaborate tramite javascript.

Una soluzione poteva essere quella di far chiamare direttamente dal codice Flash un funzione javascript di inizializzazione, passando come parametro tutte le info necessarie. Così, però, si è costretti a ripetere le informazioni contenute nel file data.xml, cioé proprio quello che volevo evitare. Inoltre, sarebbe necessario moficare anche il codice sorgente Flash per fare in modo che chiami questa nuova funzione.

Per questi motivi ho preferito adottare un’altra strategia: far parsare il file data.xml da una pagina php che ne renda  disponibile il contenuto in modo “comprensibile” a javascript. In questo modo la funzione di inizializzazione può essere chiamata direttamente dalla pagina html con la galleria, passando come parametro l’indirizzo di questa pagina php. Un ulteriore vantaggio è che anche modificando il contenuto del file data.xml, le differenze si propagano automaticamente tramite la pagina php senza necessità di cambiare nulla manualmente.

data_photo_parser.php

Il funzionamento è molto semplice:

  1. apre il file data.xml e lo legge una riga alla volta;
  2. per ogni riga, controlla se contiene informazioni relative ad un’immagine;
  3. se il controllo è positivo, allora estrae link e titolo e li salva in un array;
  4. per finire usa l’array per stampare la lista delle immagini in formato json.

Il nome del file e la posizione sono specifati nella variabile $imgDataFile:

$imgDataFile = "./data.xml";

Anche se questo file è in formato xml, non uso parser ma preferisco scansionarlo una riga alla volta ed estrarre le informazioni che mi servono tramite espressioni regolari. Questa è probabilmente la soluzione più semplice e veloce per un file xml con una struttura molto semplice, sempre che non si abbia la necessità di validarlo.

//Leggo il file che contiene le informazioni sulle immagini una riga alla volta
while (!feof($fh)) {
  $data = fgets($fh);

  //Controllo se questa riga contiene info su un'immagine
  if (preg_match("/^\s*<thumbItem/",$data)) {
     $img = Array();
     $img['link'] = '';
     $img['title'] = '';

     //Estraggo l'informazione sul link
     if (preg_match('/link="(.*\?)"/',$data,$matches)) {
       $img['link'] = $matches[1];

       //Il link potrebbe contenere una chiamata ad una funzione,
       // nel qual caso elimino la funzione
       if (preg_match('/^\s*javascript:.+?("(.+)")/',$img['link'],$matches)
        || preg_match("/^\s*javascript:.+?('(.+)')/",$img['link'],$matches)) {
          $img['link'] = $matches[2];
       }
     }

     //Se il link e' vuoto proseguo nella scansione del file senza fare niente
     if (preg_match("/^\s*$/",$img['link'])) {
       continue;
     }

     //Titolo
     if (preg_match('/title="(.*?)"/',$data,$matches)) {
       $img['title'] = $matches[1];
     }

     $images[$i++] = $img;
  }
}
fclose($fh);

Ora si pone il problema di come rendere disponibili  le informazioni estratte. In questo caso ho scelto di usare il formato json per due motivi:

  • è più semplice da usare del formato xml quando si vogliono rappresentare strutture dati semplici, come la nostra lista con i dati delle immagini;
  • una stringa in formato json è codice javascript corretto, quindi può essere  interpretata e tradotta direttamente in un array usando la funzione eval().

ATTENZIONE: interpretare direttamente una stringa json con la funzione eval() è molto comodo ma può essere anche MOLTO pericoloso, perché espone ad attacchi di tipo javascript injection. Mostro come mitigare questo pericolo nella funzione javascript che interpreta la stringa json.

In json un oggetto è racchiuso tra parentesi graffe ed è rappresentato da una serie di coppie chiave – valore separate da virgole. In questo coso voglio creare un array di nome lista_immagini (le parentesi quadre servono ad indicare che un oggetto è un array):

{"lista_immagini":[]}

Supponendo che il file data.xml contenga informazioni riguardanti due immagini, con link rispettivamente ‘foto/01.jpg’ e ‘foto/02.jpg’ e titoli ‘Foto 1’ e ‘Foto 2’, la stringa json risultante sarebbe:

{"lista_immagini":[{"link":"foto/01.jog","title":"Foto 1"}
                  ,{"link":"foto/02.jog","title":"Foto 2"}]
}

Ecco finalmente il codice php che crea la stringa json e la stampa:

$sep = false;
foreach ($images as $img) {

  if ($sep) { $json .= ','; }
  else       { $sep = true; }

  $json .= '{';
  $json .= '"link":"'.escapeJSON($img['link']).'"';
  $json .= ',"title":"'.escapeJSON($img['title']).'"';
  $json .= '}';
}

$json .= ']';
$json .= '}';

echo $json;

flash_x_lightbox.js

Come facciamo a far arrivarela stringa json con i dati delle immagini allo script javascript che la deve interpretare?

La soluzione è usare Ajax per connettersi a data_photo_parser.php. Per fortuna Lightbox si appoggia sul framework Prototype che permette di gestire Ajax in modo molto semplice. Di seguito è presentata la funzione initImageListFlashLightbox(url) che non fa altro che collegarsi all’url indicata (dove si trova data_photo_parser.php) e specificare quale funzione tratterà il risultato:

  var myAjax = new Ajax.Request(
      url,
	  {
       method: 'post',
       onComplete: initFlashLightBox
    });

La funzione initFlashLightBox(originalRequest) si occupa di interpretare la stringa json ed inizializzare la galleria per Lightbox:

  1. controlla che la stringa abbia il formato json indicato sopra;
  2. se si, allora traduce la stringa in un oggetto javascript, usando un metodo di Prototype che in qualche modo cerca di prevenire usi “malevoli” (http://www.prototypejs.org/learn/json);
  3. crea un div con display:none e vi pone all’interno un link per ogni immagine, formattato come richiesto da Lightbox.

Ognuno di questi link deve avere un’id unico, che viene ottenuto aggiungendo l’indice della posizione nell’array al prefisso ‘light_link’. Diventa però necessario memorizzare l’indice di ciascuna immagine, in modo che quando si clicca su una foto nella gallery sia poi possibile passare come attributo a slight.tart() il link corretto. Per questo si usa l’oggetto var img2index = new Object();

function initFlashLightBox(originalRequest) {

  if (/^\{"lista_immagini":\[\{"link":".+?","title":".+?"\}(,\{"link":".+?","title":".+?"\})*\]\}/.test(originalRequest.responseText)) {
    var result = originalRequest.responseText.evalJSON(true);
    var lista_immagini = result.lista_immagini;

    if (lista_immagini.length>0) {
      var objBody = $$('body')[0];
      var div = Builder.node('div',{style:'display:none;'});
      objBody.appendChild(div);
      for (var i=0; i<lista_immagini.length; i++) {
        var img = lista_immagini[i];
        img2index[img['link']] = i;
        div.appendChild(
          Builder.node('a',{id:'light_link'+i, href: img['link'], 
                title:img['title'], rel: 'lightbox[flash_gallery]'})
        );
      }
      
    }    
  } else {
    alert('Errore nel reperire la lista delle immagini!');
  }
}

Infine, bisogna definire la funzione da usare per ogni immagine della galleria Flash, che si occupa semplicemente di chiamare il metodo slight.tart() passando il link corretto:

function flashLightbox(img) {
 var id = 'light_link'+img2index[img];
 light.start($(id));
}