Nota bene: l'articolo proposto in questa pagina è proprietà dell'autore e non può essere distribuito o riutilizzato in alcun modo o forma senza il suo consenso scritto.
Le informazioni qui riportate risalgono alla data di pubblicazione dell'articolo e non è detto che siano ancora valide o che rispecchino lo stato attuale dei programmi, le posizioni delle persone interpellate o quelle degli autori stessi.

Avviso

Questo corso descrive JavaScript 1.1. Sebbene le informazioni proposte siano valide come punto di partenza per la conoscenza del linguaggio, suggerisco agli interessati di seguire guide aggiornate alla versione più recente di JavaScript che offre funzionalità avanzate come la gestione di dati XML esterni e la completa manipolazione dell'aspetto del documento.

Introduzione

Nel corso delle puntate precedenti abbiamo descritto gli elementi di base del linguaggio: principali comandi, funzioni, oggetti ed eventi; offrendo inoltre una serie di esempi mirata a guidare la realizzazione di script, dai più comuni, di puro intrattenimento, a progetti avanzati.
Ci siamo volutamente concentrati su JavaScript 1.1 in quanto ben rappresentante delle summenzionate basi del linguaggio. La scelta inoltre non è limitante come può apparire: JavaScript 1.1 è stato utilizzato per la definizione dello standard di script (ECMA-262, di cui parliamo in seguito), pertanto conoscerne il funzionamento consente di lavorare anche su implementazioni differenti, come le nuove versioni di JavaScript stesso o JScript.
JavaScript infatti si evolve e moltiplica. E le nuove versioni offrono una serie davvero interessante di funzionalità, presentate con sigle e termini differenti ma solo apparentemente complessi.
In questa quarta e ultima puntata parleremo, appunto, di evoluzione. Cosa è stato aggiunto a JavaScript? Quali nuove funzionalità ci mette a disposizione un linguaggio le cui possibilità, speriamo sia ormai chiaro, sono solo apparentemente limitate.
Va comunque detto che gli argomenti di cui ci occuperemo non sono, almeno per ora, alla portata dei browser Amiga. Mettendo da parte AWeb, tuttora limitato alla versione 1.1 del linguaggio, le implementazioni JavaScript di IBrowse 2.2 e Voyager 3, pur se dichiarate compatibili con JavaScript 1.3, ne supportano solo un sottoinsieme di funzionalità e gli aggiornamenti rilasciati in questi mesi non migliorano di molto la situazione. Vi invitiamo comunque a proseguire la lettura, in quanto riteniamo sia importante aggiornarsi sulle novità relative al linguaggio e che comunque, prima o poi, saranno fruibili anche sul nostro sistema. Infine, se è vero che Netscape e Internet Explorer non esistono in forma nativa su Amiga, è altrettanto vero che fra Linux e gli emulatori Macintosh le possibilità per sperimentare non mancano.
Ci piace considerare questo capitolo come un ultimo giorno di scuola nel quale, invece di dover sostenere un esame, si discute rilassatamente di cosa riserva il futuro (prossimo o remoto dipende da noi utenti, non solo dagli autori di browser).

Evoluzioni di JavaScript

Pochi linguaggi possono vantare un'evoluzione simile a quella di JavaScript. Netscape, l'azienda che lo ha ideato, ha incrementato le funzionalità di base (comandi, oggetti, ecc.) in ogni nuova versione, a volte anche stravolgendo il funzionamento di cose implementate in precedenza (da qui l'importanza di specificare a quale versione di JavaScript si riferisce uno script). Poichè le specifiche di JavaScript sono pubbliche, non è passato molto tempo perché altre aziende realizzassero proprie versioni dello stesso. Fra tutte spicca JScript della "solita" Microsoft.
Per evitare il diffondersi di linguaggi di script incompatibili fra loro, come stava avvenendo, l'ECMA (European Computer Manufacturers Association) ha realizzato le specifiche di quello che oggi è noto con il nome di ECMAScript o ECMA-262. Si tratta del cuore di un linguaggio di script a oggetti che, come già detto, si basa sulla versione 1.1 di JavaScript. Chiunque intenda implementare JavaScript in un proprio prodotto può basarsi sulla direttive ECMA per realizzare un linguaggio standard che funzioni analogamente su qualsiasi piattaforma. Almeno così dovrebbe essere, visto che JScript di Microsoft, pur facendo sfoggio della compatibilità ECMA-262, continua a gestire alcune funzioni a modo proprio, generando piccole ma insidiose incompatibilità (una su tutte riguarda le date).
La disponibilità di ECMAScript ha comunque frenato le smanie di Netscape che, dopo aver alterato il comportamento di diversi elementi di JavaScript 1.2 (realizzato prima che le specifiche ECMA venissero rilasciate), è tornata sui propri passi, riallineando JavaScript 1.3 a quanto prescritto dagli organismi internazionali.
E' bene precisare che ECMAScript non è un linguaggio utilizzabile in sostituzione a JavaScript o JScript: si tratta piuttosto di una raccolta di specifiche e indicazioni inerenti le sole funzioni base del linguaggio. Tali specifiche, naturalmente, non limitano la possibilità, da parte degli sviluppatori, di introdurre funzionalità peculiari nelle proprie implementazioni del linguaggio. Le direttive ECMA, inoltre, tralasciano completamente il modello a oggetti che rappresenta il documento HTML (detto "DOM", ossia "document object model"), vitale per l'utilizzo di JavaScript, che è stato invece standardizzato dal W3c, lo stesso organismo che si occupa di definire l'HTML. Insomma, da semplice trovata degli autori di un browser, JavaScript ormai è diventato una cosa seria e codificata a livello internazionale!
ECMA ha presentato anche diverse direttive circa l'espansione delle funzionalità del linguaggio tramite librerie. Una di queste librerie, che farà inorridire i più (anche perchè subito adottata da Microsoft per JScript), mette a disposizione un oggetto il cui nome non lascia spazio a dubbi: "File System Object". Tale innovazione, che a un primo sguardo sembrerebbe snaturare JavaScript, al contrario amplia notevolmente le potenzialità del linguaggio, avvicinandolo ad altri ben più blasonati, quali ad esempio PERL o ASP. Peccato che, oltre alla presenza, nel mondo, di tanti malintenzionati, questo nuovo oggetto sia, per ora, peculiarità del solo browser di casa Gates.
Altra caratteristica di recente introduzione, questa volta da parte di Netscape (e disponibile soltanto sul loro browser), sono i JavaScript Beams. Frutto della crescente integrazione con Java (ora JavaScript ha accesso ad una API Java che consente a script ed applet lo scambio di informazioni e funzioni), i JavaScript Beams, assimilabili ai Java Beams, sono delle raccolte di funzioni esterne poste all'interno di file testuali con estensione ".jsb". Ogni file contiene le varie funzioni, scritte in puro JavaScript ed incapsulate all'interno di un linguaggio di tipo SGML, quindi con una sintassi simile all'HTML. La struttura e le regole di implementazione dei JSB avvicinano JavaScript a Java, offrendo agli sviluppatori funzioni simili e una sorta di gestione dei tipi di dati (anche se JavaScript resta un linguaggio fondamentalmente libero dai vincoli di questo genere).

Sicurezza

Con JavaScript che procede a grandi passi verso una forte integrazione con il mondo esterno al browser, la sicurezza acquista sempre maggiore importanza. Vediamo dunque cosa si è fatto nelle varie versioni del linguaggio per prevenire gli abusi.
Sicurezza non significa soltanto evitare che uno script danneggi il sistema operativo o cancelli l'hard disk. Significa anche prevenire il furto di informazioni. Senza andare troppo lontano, non sarebbe difficile nascondere in una pagina uno script che raccolga informazioni sulla lista dei siti visitati dall'utente in precedenza (oggetto "history") o su altre finestre e relativi documenti (oggetti "window" e "document", con tutte le proprietà del caso, compresi gli eventuali moduli) e le invii, in e-mail o trasmettendo dati ad un apposito script CGI, allo pseudo-hacker di turno.
Per evitare questa possibilità è stato introdotto, sin da JavaScript 1.0, un controllo sulla provenienza dei documenti. Ne abbiamo già parlato: uno script può accedere soltanto a finestre e documenti provenienti dallo stesso server. Il limite si estende a protocollo (differenziando "http://", "https://" e "file://") e porta.
Uno script posto, ad esempio, su "http://www.amigalife.it/index.html" potrà accedere, oltre che a finestre da esso generate, solo a informazioni (a video) provenienti da "http://www.amigalife.it/". Non potreebbe invece leggere un documento prelevato dall'ipotetico indirizzo "http://amigalife.it/", nè da "http://www.amigalife.it:81/".
Questo modello di sicurezza, pur se funzionale, presentava alcune limitazioni allo sviluppo di script leciti. Per superarle è stato introdotto, con JavaScript1.1, il concetto del "data tainting". Abilitando la relativa opzione (che, per inciso, non è supportata da nessun browser Amiga), l'accesso ai dati di altri documenti, anche provenienti da server diversi, può essere impostato selettivamente all'interno dello script. L'autore di uno script può insomma consentire l'accesso all'intero documento o a una parte delle informazioni ad esso inerenti (compreso quindi il proprio script e le sue funzioni e variabili), servendosi delle funzioni "taint()" ed "untain()". Chiariamo le cose con un esempio.
Innanzitutto è necessario controllare se il taint è abilitato. Per farlo si verifica il risultato del metodo "navigator.taintEnabled()". Se è "true" si può procedere. Per evitare incompatibilità sarebbe anche opportuno verificare se il metodo esiste con il classico: "if (navigator.taintEnabled"). Su AWeb esiste e restituisce sempre false, su Voyager 3 ed IBrowse 2.2 non esiste proprio.
Anche se il data tainting è abilitato, molti oggetti critici ("document", "window", "location" e tutti gli oggetti relativi ai moduli) sono protetti dall'accesso indiscriminato. Possiamo abilitarne l'interscambio utilizzando ad esempio "status=untaint(document.title)". In questo modo il titolo del documento nel quale si trova lo script sarà disponibile ad altri script. Lo stesso si può fare con variabili e oggetti. Omettendo l'argomento si rende accessibile l'intero documento. "taint()" svolge invece la funzione opposta, impedendo l'accesso a informazioni precedentemente rese disponibili. Per maggiori informazioni rimandiamo alla documentazione ufficiale JavaScript presente sul CD.
Il data tainting è destinato ai programmatori e solo un suo utilizzo accorto può prevenire problemi. Come assicurazione c'è da dire che i dati ottenuti in questo modo non possono essere inviati ad altri server. Provando a farlo, infatti, l'utente verrà avvertito da un requester che chiederà la conferma dell'azione.
Nelle versioni più recenti di JavaScript è stato introdotto un altro elemento di sicurezza, basato più sulla "fiducia" che un nome porta con sé che su accorgimenti tecnici. Analogamente a quanto già accade con gli applet Java, anche gli script possono essere "firmati". Il meccanismo è semplice: se navigando su una pagina si incontra uno script, il browser ne verifica la provenienza e propone questa informazione all'utente che è libero di decidere se accettarne l'esecuzione. Al programmatore è sufficiente, dopo aver realizzato uno script, utilizzare gli appositi tool Netscape per ottenerne la certificazione. Chi naviga abitualmente su altre piattaforme sa di cosa parliamo e conosce l'effetto rassicurante che questo meccanismo porta nell'utente, almeno finché questi non si trova davanti a un applet firmato e apparentemente sicuro che però gli azzera l'hard disk...

Le nuove funzionalità di JavaScript

Dopo questa panoramica sulle molteplici evoluzioni di JavaScript, torniamo a parlare di programmazione e analizziamo alcuni degli elementi introdotti più recentemente nel linguaggio. Abbiamo scelto di occuparci di eventi ed espressioni regolari poiché rappresentano, a nostro giudizio, ottimi esempi di nuove o migliorate funzionalità.

Le espressioni regolari

Le espressioni regolari, già note agli esperti di script AmigaDOS come "pattern" o "wild card", sono particolari sequenze di caratteri che permettono di rappresentare una stringa tramite dei simboli. Questa naturalmente è una forte semplificazione del concetto dettata da ragioni di spazio. Per approfondire l'argomento consigliamo la lettura dell'articolo "Le Espressioni Regolari" del collega Fabio Rotondo, presente in questo stesso numero di Amiga Life.
Un esempio classico di espressioni regolari è offerto da AmigaDOS: per visualizzare tutti i file presenti nella directory C: che iniziano con "re" possiamo servirci di "List C:re#?". La sequenza "#?", per AmigaDOS, equivale a "qualunque carattere o sequenza di caratteri". Quindi, "re#?" equivale a tutte le stringhe in cui, dopo le lettere "r" ed "e", seguono zero o più lettere (o numeri) di altro tipo. A questa espressione corrispondono, fra gli altri, i file "Rename", "Relabel" e "Remrad".
Anche JavaScript, a partire dalla versione 1.2, ha introdotto il supporto per le espressioni regolari. Nello specifico contesto risultano utili per verificare se una stringa immessa dall'utente risponde alle nostre richieste.  Con il semplice utilizzo del metodo ".test()" dell'espressione regolare (si tratta di un oggetto) è possibile accertare, ad esempio, se l'utente ha indicato la chiocciolina nel suo indirizzo E-Mail ("esp.test(stringa)", con espressione "/*@*/").
Le potenzialità delle espressioni regolari non si limitano al controllo della presenza di uno o più caratteri, cosa che si potrebbe ottenere più facilmente con il metodo ".indexOf()", presente sin dalla versione 1.1 di JavaScript. Vediamo perché con un esempio.
Prima di proseguire è doveroso sottolineare che in questo contesto il termine "espressione" è da considerarsi riferito alle espressioni regolari e non alle espressioni JavaScript (es.: "if (ciao() == true)").
Per servirsi delle espressioni regolari si fa riferimento all'oggetto "RegExp". A differenza di altri oggetti, anche creandone nuove istanze, ne permane uno principale per ognuno degli script in esecuzione. La ragione di questa anomalia è data dal fatto che le operazioni relative alle espressioni regolari causano modifiche alle proprietà del suddetto oggetto primario.
Il sistema più semplice per creare un'espressione regolare, qualora non sia necessario utilizzarne differenti all'interno dello script (in tal caso è necessario seguire un diverso approccio, descritto nella documentazione del linguaggio) è il seguente:

var esp=/espressione_regolare/
(notare l'uso di "/" che differenzia l'espressione da una normale stringa).

Una volta definita l'espressione regolare, sia lo stesso oggetto "RegExp", sia l'oggetto "string", ci consentono di effettuare le varie operazioni. Vediamo ora in dettaglio alcuni dei numerosi caratteri utilizzabili per costruire l'espressione:

* Equivale a zero o più occorrenze della lettera che lo precede. Esempio: /car*o/ equivale sia a "caro", sia a "carro".
+ Equivale a una o più occorrenze della lettera che lo precede. Esempio: /ami+ga/ equivale sia ad "amiga", sia ad "amiiiga", ma non ad "amga".
\s Equivale ad un singolo spazio. Esempio: /Amiga\s\Life/ equivale ad "Amiga Life".
\D Equivale ad un numero. Esempio: /Amiga\s\D/ equivale ad "Amiga 500" o "Amiga 3000", ma non ad "Amiga CDTV".
\w Equivale a un qualsiasi carattere alfanumerico. Esempio: /ore\s\w/ equivale ad "ore tre" ma non ad "ore 3".

Questi sono solo alcuni dei caratteri utilizzabili. Non li riportiamo tutti per ragioni di spazio.
La cosa interessante è che i vari caratteri sono combinabili fra loro. Ad esempio "/\w+\s\w+/" equivale ad una stringa composta necessariamente da due gruppi di caratteri inframezzati da uno spazio. Verificare la correttezza di una simile sequenza (ad esempio un nome e cognome) è infinitamente più veloce così rispetto ai metodi dell'oggetto "string".
Utilizzando le parentesi, poi, possiamo indicare a JavaScript che, al momento del controllo dell'espressione, alcune parti della stessa devono essere memorizzate all'interno delle proprietà dell'oggetto "RegExp".
Se quindi l'espressione proposta diventasse "/(\w+)\s(\w+)/", otterremmo, con una singola istruzione (oltretutto velocizzata dal fatto che l'espressione è stata già interpretata in fase di definizione), sia la verifica della correttezza della stringa stessa, sia le sue due componenti (nome e cognome) già isolate e disponibili come elementi di un array. E il tutto, eventualmente, con o senza differenziazione fra maiuscole e minuscole.
Un esempio concreto aiuterà a comprendere le potenzialità di quanto esposto:

  <html>
   <head>
    <script language="JavaScript1.2">
    <!--
     pattern=/(\w+)\s(\w+)/
     function controlla()
     {
      var temp=document.anagrafici.info.value;
      if (pattern.test(temp))
       alert("Nome:"+RegExp.$1+"\nCognome: "+RegExp.$2);
      else
       alert("Errore: inserire nome e cognome separati da uno spazio");
      return true;
     }
    // -->
    </script>
   </head>
   <body>
    <form name="anagrafici" onSubmit="return false">
     Inserire nome e cognome:<br>
     <input type="text" name="info" value="" onChange="controlla()">
    </form>
   </body>
  </html>

Come si può notare, a seguito di un'operazione, l'oggetto "RegExp" può essere interrogato e fornire informazioni sulla stringa analizzata. In questo caso si è fatto riferimento ai soli due elementi inseriti fra parentesi, ma sono molte le informazioni disponibili, variabili a seconda delle operazioni effettuate.
Insomma, se usate con cognizione di causa, le espressioni regolari di JavaScript semplificano notevolmente le operazioni di verifica e controllo dei dati, pur se al prezzo della compatibilità all'indietro.

Gli eventi

Di eventi si è parlato nella seconda parte del corso. Sostanzialmente, molti elementi HTML possono invocare una funzione quando si verifica un evento che li riguarda, ad esempio il passaggio del mouse sull'elemento stesso.
Le nuove versioni di JavaScript hanno espanso il sistema degli eventi in tre modi. Innanzitutto creando nuovi gestori di evento, quali, ad esempio, "OnMouseUp" e "OnMouseDown" che consentono di rilevare non solo il singolo clic, quanto le fasi dello stesso, permettendo la creazione di interfacce grafiche più realistiche, ove le icone (immagini) cambiano forma o colore fintanto che l'utente tiene premuto il bottone del mouse sopra di esse.
E' stato poi introdotto l'oggetto "Event" contenente informazioni sul momento in cui è occorso l'evento: eventuali tasti o bottoni del mouse premuti, destinatario dell'evento, ecc.
Già con questo oggetto le potenzialità degli eventi vengono ampliate: potremmo, ad esempio, fornire le nostre pagine di una funzione automatica che apre i link su una nuova finestra qualora l'utente, nel cliccarvi, tenga contemporaneanente prenuto il tasto ALT.
I lettori più attenti avranno già storto il naso di fronte a tanto entusiasmo, ricordando che per ottenere l'effetto proposto si dovrebbe assegnare un gestore di evento ad ognuno dei link presenti sulla pagina. In realtà ora non è più così. L'ultima delle novità relative agli eventi è forse anche la più interessante. Sono ora disponibili, associati agli oggetti "window" e "document", i metodi "captureEvents()", "releaseEvents()", "routeEvent()" e "handleEvent()".
In pratica è possibile intercettare tutti gli eventi di un certo tipo, ad esempio i clic del mouse, a livello di finestra o documento, gestirli direttamente, trasmetterli al livello inferiore (la gerarchia è: finestra, documento, suoi elementi) o ad uno specifico elemento del documento, qualora questo preveda un gestore di evento.
Ecco quindi come si presenterebbe uno script per realizzare quanto descritto:

  <html>
   <head>
    <script language="JavaScript1.2">
    <!---
     function gestioneEvento(e)
     {
      if (e.modifiers == 2)
       window.open(e.target);
      else
       document.location=e.target;
      return false;
     }
    //-->
    </script>
   </head>
   <body>
    <a href="http://www.cnn.com">cnn</a><br>
   </body>
  </html>

Analizziamo le parti di codice JavaScript 1.2/1.3 dello script:
"e" è il nome che abbiamo assegnato all'oggetto "Event" generato in occasione dell'evento (prestare attenzione al fatto che viene creato solo se l'evento è intercettato da un gestore globale); "target" e "modifiers" sono due sue proprietà. La prima è composta da una stringa che rappresenta l'elemento sul quale è occorso l'evento (nel caso dei link è l'URL) mentre la seconda indica i tasti premuti in quel momento. Seguono, dopo la funzione, i comandi:

  document.captureEvents(Event.MOUSEDOWN);
  document.onmousedown=gestioneEvento;

Il primo istruisce il documento per catturare l'evento "OnMouseDown" (questo poiché all'evento "OnClick" spesso i browser associano azioni differenti a seconda dei tasti premuti) mentre il secondo imposta il gestore di evento "OnMouseDown" del documento affinché esegua la nostra funzione.
E' importante notare come nella seconda linea non vadano specificate le parentesi (altrimenti a "document.onmousedown" verrebbe assegnato il risultato della funzione e non la funzione stessa) e l'uso delle maiuscole e minuscole, non casuale. Attenzione anche al fatto che la funzione verrà invocata in seguito a clic su qualunque elemento HTML che preveda l'evento "OnClick", non soltanto sui link. In uno script reale sarà quindi necessario verificare il contenuto di "e.target" prima di procedere all'apertura del documento.
Esempi relativi sia all'utilizzo delle espressioni regolari, sia alle nuove funzionalità degli eventi sono disponibili sul CD.

Conclusioni

Per ora è tutto. Ci auguriamo che questo corso sia risultato utile e vi rimandiamo a futuri eventuali approfondimenti sul tema, magari in occasione dei progressi dei browser Amiga in questo campo.

Ringraziamenti e note

Si ringraziano AmiTrix development per AWeb e Vapor (nella persona di Luca Danelon) per Voyager3. Grazie ad Angelo Verdone e Daniele Franza per la "prova su strada" del corso.

Ricordiamo che, per ragioni di spazio, gli esempi proposti non contengono tutti gli elementi HTML il cui uso è normalmente obbligatorio all'interno di un documento.